Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set up options and track changes in management SDK. #1081

Merged
merged 31 commits into from
Nov 9, 2020

Conversation

Y-Sindo
Copy link
Member

@Y-Sindo Y-Sindo commented Oct 27, 2020

Summary of the changes

  • Move options-related class to directory "Configuration"
  • set up ServiceManagerContext to track ServiceManagerOptions, set up ServiceOptions to track ServiceManagerContext
  • e2e tests passed.

Configuration Methods

DI style

Method1: use configuration instance such as JSON file and so on. It's OK that no configuration file is found.
      services.AddSignalRServiceManager();
{
    "Azure": {
        "SignalR": {
            "ApplicationName": null,
            "ConnectionCount": 1,
            "ConnectionString": "",
            "Proxy": null,
            "ServiceEndpoint": null,
            "ServiceTransportType": 0
        }
    }
}
Method2: use delegate and configuration file.
    services.AddSignalRServiceManager(o=>{
         o.ConnectionString="";
    });

ServiceManagerBuilder

    var manager = new ServiceManagerBuilder()
        .WithOptions(o=>o.ServiceTransportType=ServiceTransportType.Persistent)
        .WithConfiguration(new ConfigurationBuilder().AddJsonFile("config.json", reloadOnChange: true).Build())
        .Build();

@Y-Sindo Y-Sindo requested review from wanlwanl and JialinXin October 27, 2020 06:30
_options.ValidateOptions();

_services.AddSignalRServiceManagerCore();
_serviceProvider = _services.BuildServiceProvider();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if Build is called twice? the first _serviceProvider never dispose?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check added, throw InvalidOperationException() if called twice.

@@ -9,6 +9,7 @@ internal static class Constants
{
public static class Keys
{
public const string ServiceManagerOptionsSectionKey = "Azure:SignalR";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a more common name if it is in Common project, or you can move the const to the Management project

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the key to ServiceManagerOptions as a const string named Section.

internal abstract class CascadeOptionsSetup<TargetOptions> : IConfigureOptions<TargetOptions>, IOptionsChangeTokenSource<TargetOptions>
where TargetOptions : class
{
private readonly ServiceManagerOptions _initialSource;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what needs to be hot-reloaded is the multiple endpoint configure? So is it that we only need to track ServiceManagerOptions => ServiceOptions change? Does it work if ServiceManagerContext inherits ServiceOptions ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, good idea.

/// <summary>
/// The section name used to get <see cref="ServiceManagerOptions"/> value from <see cref="IConfiguration.GetSection(string)"/>
/// </summary>
public const string Section = "Azure:SignalR";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intended to expose it to customer?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think customer can get the right position of configuration from Section.

_options.ValidateOptions();
if (ServiceProvider != null)
{
throw new InvalidOperationException($"Mulitple invocation of the method is not allowed.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we have such restriction is because we want UT to test ServiceProvider? otherwise ServiceProvider can be disposed inside Build()?

Should the following case be a valid one?

var manager1 = builder.WithOptions(o=>o.ConnectionString="one").Build();
var manager2 = builder.WithOptions(o=>o.ConnectionString="another").Build();

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If ServiceProvider is disposed, then all the services built by service provider are disposed. We should only dispose ServiceProvider only when we don't need ServiceManager anymore.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now ServiceProvider is disposed from ServiceManager

{
if (_configuration != null)
{
_configuration.GetSection(Constants.Keys.AzureSignalRSectionKey).Bind(options);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think adding services.Configure<ServiceManagerOptions>(Configuration.GetSection(Constants.Keys.AzureSignalRSectionKey)); is enough, we don't need this setup class as we don't have additional setups? Emulator uses such configure and hotreload works, see https://github.com/Azure/azure-signalr/blob/dev/src/Microsoft.Azure.SignalR.Emulator/Startup.cs#L45

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In here, https://github.com/Azure/azure-signalr/blob/dev/src/Microsoft.Azure.SignalR.Emulator/Startup.cs#L45, the Configuration variable is actually an IConfiguration type variable. However, in the extension methods of ServiceCollection, we don't have IConfiguration. Do you think we should add it as a parameter?


private static IServiceCollection TrySetProductInfo(this IServiceCollection services)
{
var assembly = Assembly.GetExecutingAssembly();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this anyway executes even if ProductInfo is already set?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but if ProductInfo is not null, then the value would not be overwritten.


internal ServiceManager(ServiceManagerContext context, RestClientFactory restClientFactory)
//in builder mode, serviceProvider is not null and should be disposed.
internal ServiceManager(ServiceManagerContext context, RestClientFactory restClientFactory, ServiceProvider serviceProvider = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when would ServiceProvider be null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, some mistakes here . Changed it to IServiceProvider from ServiceProvider and make it required .

where TargetOptions : class
{
private ConfigurationReloadToken _changeToken;
private IDisposable _registration;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readonly

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix


public string Name => Options.DefaultName;

public void Configure(TargetOptions target) => Convert(target, _sourceOptions);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is using sourceMonitor.CurrentValue better?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

@@ -141,6 +144,14 @@ public async Task<IServiceHubContext> CreateHubContextAsync(string hubName, ILog
}
}

public void Dispose()
{
if (_context.DisposeServiceProvider)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is passing some ownServiceProvider parameter in .ctor doable?

_services.AddSignalRServiceManager();
_services.Configure<ServiceManagerContext>(c => c.DisposeServiceProvider = true);
var serviceProvider = _services.BuildServiceProvider();
var context = serviceProvider.GetRequiredService<IOptions<ServiceManagerContext>>().Value;
var productInfo = ProductInfo.GetProductInfo(_assembly);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read from context?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@Y-Sindo Y-Sindo merged commit a602e5c into Azure:dev Nov 9, 2020
@Y-Sindo Y-Sindo deleted the chained-options branch November 9, 2020 06:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants