Conversation
This does not yet use IOptions to configure the storage providers' specific info, but that's coming later
|
Thanks for tinkering with me! |
|
|
||
| namespace Microsoft.Orleans.Host.Abstractions | ||
| { | ||
| public interface ISiloBuilder |
There was a problem hiding this comment.
Was planning to put silo builder in Microsoft.Orleans.Silo.Abstractions.
A goal of this design is logical separation of actor model, i.e silo and service framework, i.e host. Silo should be able to be run in any service framework. Host is our framework as the silo is not much value without a framework to run it in.
| public interface IStartup | ||
| { | ||
| IEnumerable<Type> Members { get; } | ||
| IEnumerable<Type> Members { get; } // not sure I understand why this? |
There was a problem hiding this comment.
This is for the purpose of constructing any systems the host contains. Like a silo, in this way, a silo is just another pluggable component in the service framework. Provider are too. IMO, this silo should not concern itself with providers, they are a framework detail.
| { | ||
| logger = loggerFactory.CreateLogger<Thingy>(); | ||
| lifecycle.Subscribe(this); | ||
| lifecycle.Subscribe(this); //<-- passing this before finishing constructing is dangerous (although practical) |
There was a problem hiding this comment.
yeah, I was being lazy. There are extension functions to connect callbacks. I'll put out a PR to illustrate.
| namespace Microsoft.Orleans.Host.Abstractions | ||
| { | ||
| // this would go into the azure storage providers DLL | ||
| public static class AzureStorageProviderExtensions |
There was a problem hiding this comment.
extensions should go in an {namespace}.Extensions namespace according to framework standards. Users should have the option to opt in.
There was a problem hiding this comment.
Yup, as I mentioned in the description, I just put everything in the same project for now and didn't even consider the namespace, just showing what the programing Api would look like (mainly startup class) . Also, some of these new files mention that they should go in different Dlls that you only pull in if you use them
| // this would go into the (optional) storage providers DLL. | ||
| public interface IStorageProvidersFactory | ||
| { | ||
| ISiloBuilder Silo { get; } |
There was a problem hiding this comment.
Why should as silo builder be part of this interface..?
There was a problem hiding this comment.
Because it allows you to access runtime service that can be used by it or passed along for concrete provider factories to use, as in this azure storage example
| namespace Microsoft.Orleans.Host.Abstractions | ||
| { | ||
| // this would go into the (optional) storage providers DLL. | ||
| public interface IStorageProvidersFactory |
There was a problem hiding this comment.
This interface doesn't have any characteristics of a factory..
There was a problem hiding this comment.
Yup, I agree, remnants of previous prototyping. This is more an IStorageProviderManager
There was a problem hiding this comment.
or IStorageProvidersBuilder actually, and then it would be more obvious why it has an ISiloBuilder too.
| namespace Microsoft.Orleans.Host.Abstractions | ||
| { | ||
| // this would go into the azure storage providers DLL | ||
| public static class AzureStorageProviderExtensions |
There was a problem hiding this comment.
Yup, as I mentioned in the description, I just put everything in the same project for now and didn't even consider the namespace, just showing what the programing Api would look like (mainly startup class) . Also, some of these new files mention that they should go in different Dlls that you only pull in if you use them
| namespace Microsoft.Orleans.Host.Abstractions | ||
| { | ||
| // this would go into the (optional) storage providers DLL. | ||
| public interface IStorageProvidersFactory |
There was a problem hiding this comment.
Yup, I agree, remnants of previous prototyping. This is more an IStorageProviderManager
| // this would go into the (optional) storage providers DLL. | ||
| public interface IStorageProvidersFactory | ||
| { | ||
| ISiloBuilder Silo { get; } |
There was a problem hiding this comment.
Because it allows you to access runtime service that can be used by it or passed along for concrete provider factories to use, as in this azure storage example
| { | ||
| public static void AddAzureStorage(this IStorageProvidersFactory factory, string providerName, string connectionString) | ||
| { | ||
| var concreteFactory = ActivatorUtilities.CreateInstance<AzureStorageProviderFactory>(factory.Silo.ApplicationServices); |
There was a problem hiding this comment.
I believe that the AzureStorageProviderFactory might not be required if I use the right overload for ActivatorUtilities.CreateInstance, since the factory is there mainly to forward injected service dependencies and separate them from instance specific data/config
|
So I updated tree with a second prototype pass on the provider model. Separation of framework from Virtual Actor container (Silo) Providers are not Services Current Prototype In this pattern, the component that knows most about the provider is the provider itself and its configuration step. Everything else in the system knows only what it needs. Provider group just knows that it exists and the caller just knows the client behavior. While this design achieves most of my goals, there is a problem with configuration, because the behavior is supported by a collection of classes that all need to setup. This degree of complexity can't easily be hidden by a per provider extension function like AddStorageProvider, so simplifying the configuration/setup is something that will need more thought. |
|
Took another pass on configuration. Much nicer now. |
|
Tinkered with silo and multi-silo hosts using the provider model. Since service provider supports singletons and provider model (probably need to rename it) supports unique instances, we can create, configure, initialize, run, and stop one or more silos in the host, by simply changing the startup class. Members management needs to change though, I think. Members, like services, need to be modifiable in a better way. Have an idea how to do this, but need to think on it more. |
xiazen
left a comment
There was a problem hiding this comment.
I probably should just use GitHub comment , instead of starting a review, since this is a prototype PR.
But hey I'm playing with the new GitHub feature!
| { | ||
| public static void AddAzureStorage(this IStorageProvidersBuilder factory, string providerName, string connectionString) | ||
| { | ||
| var concreteFactory = ActivatorUtilities.CreateInstance<AzureStorageProviderFactory>(factory.Silo.ApplicationServices); |
There was a problem hiding this comment.
feels like concreteFactory can be reused for creating other AzureStorageProvider, which should be a singleton. maybe the method signature should be
public static void AddAzureStorage(this IStorageProvidersBuilder factoryBuilder, AzureStorageProviderFactory factory, string providerName, string connectionString)
| // this would go into the azure storage providers DLL | ||
| public static class AzureStorageProviderExtensions | ||
| { | ||
| public static void AddAzureStorage(this IStorageProvidersBuilder factory, string providerName, string connectionString) |
There was a problem hiding this comment.
Why isn't this method member of AzureStorageProviderFactory?
If you worry about AzureStorageProviderFactory'saccessability as an internal class, maybe we can use an interface as a medium to publicize AddAzureStorage method:
interface IAzureStorageProviderFactory: { public AddAzureStorage(...) }
There was a problem hiding this comment.
because this extendes the IStorageProvidersBuilder interface, not the concrete azure factory. In fact, as I mentioned in a comment, the factory itself might even go away, it's just an implementation detail.
| // this would go into the (optional) storage providers DLL. | ||
| public interface IStorageProvidersBuilder | ||
| { | ||
| ISiloBuilder Silo { get; } |
There was a problem hiding this comment.
not sure why IStorageProviderBuildershould have a member of ISiloBuilder. Feels to me IStorageProviderBuildershould be member of ISiloBuilder, so it will build StorageProviders based on silo's lifecycle event. Not the other way around.
There was a problem hiding this comment.
this way storage providers are an opt-in framework feature that do not require to be bundled in the virtual actors core. You decide whether you want to use storage providers explicitly, and since it's entirely opt-in, you might decide to use an alternative storage providers model entirely, because the framework would be flexible enough to support it.
| public static void ConfigureStorageProviders(this ISiloBuilder silo, Action<IStorageProvidersBuilder> configureMethod) | ||
| { | ||
| var factory = silo.ApplicationServices.GetRequiredService<IStorageProvidersBuilder>(); | ||
| configureMethod(factory); |
There was a problem hiding this comment.
Ah I see why you give IStorageProviderBuilder a SiloBuilder memeber. You want to get the registered IStorageProvidersBuilderfrom it. But this is the sevice locator pattern we want to avoid. Well I think you can do ConfigureStorageProviders(IStorageProviderBuilder builder, Action<IStorageProvidersBuilder> configureMethod) and get IStorageProvidersBuilder before calling this method
There was a problem hiding this comment.
not sure I follow. This is not client code. The programming API for the user does not use the service locator pattern, it's all abstracted away by these configuration methods, in the same way as Asp.NET hides these too. If we change the signature to what you suggested, it means the user will be in charge of resolving the IStorageProvidersBuilder himself and then pass it in (probably by putting the burden on him to use the service locator pattern
| } | ||
|
|
||
| public interface IStorageProvider | ||
| { |
There was a problem hiding this comment.
I think this can inherite from ILifecycleObserver . So that all IStorageProvider which share similar life cycle style/pattern can be managed in the same LifeCycle class.
|
Per @jason-bragg comment. Not sure why I cannot just replied to @jason-bragg comment Minor:
If Grain takes provider group as a param in contruction time, this is service locator pattern again, which I think we should really avoid. Maybe we can have a Since ProviderGroup is really just a dependency of GrainFactory, not every single Grain. Confusion:
Why isn't Providers managed by LifeCycle class as ILifecycleObserver? So each type of Provider can have its own OnStartUp method or OnConfigure method which makes the lifecycle management much more flexible. configure providers : This strike to me that |
| { | ||
| Silo = silo; | ||
| } | ||
| public ISiloBuilder Silo { get; } |
There was a problem hiding this comment.
It something is a builder, don't miss the Builder from the name, it can be confusing.
There was a problem hiding this comment.
OK. Yeah, I don't understand why in ASP.NET they just call their IApplicationBuilder "app"... Or maybe it was just in param names, but not necessarily properties
This PR is just work in progress before I go to sleep, but it is basically how I envision the providers instantiation (as we know they are not singleton services).
This is modeled from how AspNet allows optional features.
The important thing is how the Startup class looks like, and how the configuration is done via extension methods on
IServiceCollectionandISiloBuilder(the latter I added emulating the IApplicationBuilder from Asp.NET)This does not yet use
IOptions<T>to configure the storage providers' specific info, but that's coming later if you feel the approach is good.For now I put everything in the abstractions project, but in theory they should be moved to different pieces (I added code comments specifying where they should live).