-
-
Notifications
You must be signed in to change notification settings - Fork 837
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
Feature Idea: Pooled Instances #1162
Comments
Interesting idea. Somewhat similar to thread/connection pooling I guess. What would be the advantage over lets say a singleton, since they somehow would behave like a singleton, wouldn't they? Only thing I can think of now is the fact, that there would be less time spend resolving dependencies, since the second request would just hand back the current existing instance of the pool. I think it could potentially also be misused very easily. Especially for dependencies that have some sort of resources that should be cleaned up. Also, how would that integrate with let's say Microsoft DI? They only provide Singleton|Transient|Scoped which for us is Singleton|InstancePerDependency|InstancePerLifetimeScope. |
Some resources, like connections, lend themselves quite well to "I have capacity to handle this many concurrent operations, and it's expensive to 'new up' one each time". You could quite simply manually define an Clean-up is an interesting one, because you wouldn't have Dispose to do that, so another mechanism (Rent/Return methods) would have to fill in the gap. As for integration with MS DI, this would be one of those scenarios where you have to directly manipulate the container (like if you wanted to do matching lifetime scopes). When the user resolves using |
I dig it. I could see how database connections or whatever would be good here, where previously you may have had to write your own singleton factory to hand out connections. Sort of like what's going on in this document but not bothering the consumer with the actual I can see it could be a potential challenge for new folks to get right. We'd have to think about how it would behave with respect to...
I mean, that's just details ("just?" 😆 ) but the idea of handling object pooling as a new lifetime management construct is really interesting. I don't know of any other container that does it, so it'd be a cool differentiating feature. |
I think there are three separate sets of issues here. The first is about how the lifetime of the pool and the objects in the pool maps to existing Autofac notions and implicit relationships. I think the easiest way to define this lifetime behaviour is to define a few simple rules and let everything else flow from there.
Okay, so 6 rules. But I suspect we can answer a lot of your questions with those. The second issue, for Renting/Returning, I propose defining an We could define three built-in pool policies:
Lastly, pool capacity, is an interesting one. There's something to be said for just letting the pool grow as needed (if you request more entities, you must need a bigger pool). I think it would also be good to be able to dynamically shrink the pool...so perhaps defining a 'target max' rather than an absolute max might be better. |
I wonder if we could use the standard I think the rules defined there do answer most of the questions. I think different pool policies will have to handle pool capacity issues. Like, I might define a maximum pool size on some database contexts to ensure I can effectively limit a given application's connections to that database, like a throttling mechanism. Shrinking it, in combination with a potential hard maximum, would cover most things, I think. |
I did think about using the existing
|
Could pooling be written as an extension, so you could add a reference to a package that adds support for such a feature? I agree, possibly adding an extension package as a default dependency if you're not using it is less than ideal; at the same time, I find I don't want to own redundant code if there's something we can use. Perhaps such a thing is too hard for an extension; on the other hand, maybe that tests the extensibility mechanism(s) of v6, to see if it can be done? |
What kind of connections are we speaking of? Thinking about database-connections, which most likely are based on ADO.NET, there is already connection pooling. For instance calling |
Does it matter? Maybe it's some connection type you've created in your code; maybe it's a connection to a Vault instance for secrets; maybe it's not a connection to a database at all and, instead, it's a set of resources used to perform expensive calculations, like access to a quantum computing resource. I say "database connection" as a placeholder for "any sort of resource for which it's overly expensive to create that resource (or connection to that resource) and/or the resource is finite such that there is a maximum amount of that to spread around the app." I don't mean to try to "sell" the concept solely based on ADO.NET connections, which, as you say, already have some level of pooling built in. I think if you had a need for a pooling mechanism where it wasn't provided by the thing you want pooled - this could be an interesting idea. |
You do that on a regular basis? :)
Yes, true. Just thinking that there might be many resources that have a mechanism like this built-in and people might end up using that new scope not knowing it's being provided by the specific resource as well. |
This is one of the things I was thinking about when I was mentioning, "I can see it could be a potential challenge for new folks to get right." Knowing when to use it is going to be just as important as knowing how to use it. But we have a lot of features like that, where it's important to know both when and how to apply it. I don't know that should stop us, just that we'll have to be mindful about explaining it in the docs and examples. |
Like you say, this may be a good test of the new extensibility options. At the end of the day, what I'm proposing here is basically an |
I'm going to have a go at this locally, see what issues I run into. |
Ok, this looks like it will work fairly easily, I've got something working locally, but need more tests and some overloads. The thing that v6 gives us is Service Pipelines, because I can now store a different object in the Shared Instance lookup than I actually return to the consumer. Without that this would be extremely difficult. One key point on using the
So the concept of, I've got X connection slots available, and can never go over that, isn't really part of the Right now I've got this: builder.RegisterType<PooledComponent>().As<IPooledService>()
.PooledInstancePerLifetimeScope()
.OnActivated(args => activateCounter++)
// ...other stuff
using (var scope1 = container.BeginLifetimeScope())
{
scope1.Resolve<IPooledService>();
}
using (var scope2 = container.BeginLifetimeScope())
{
scope2.Resolve<IPooledService>();
}
// Only 1 instance should have been activated.
Assert.Equal(1, activateCounter); @tillig, did you want to create a new |
Ooooookay. Autofac.Pooling repo is live. There is a corresponding team for triage. I didn't set up anything on AppVeyor because... there's nothing to build yet. 😄 I didn't create a develop branch, set up issue templates, or anything else. But the repo exists and we can start jamming on it. |
I'm going to close this issue since the repo is up and running and there's code going in. We can iterate over ideas there as needed. |
An idea came to me about a potentially useful new feature we could add in to Autofac; pooled instances.
Basically, when you resolve a registration configured as pooled, you get an instance from a pool of objects, not a new instance.
In more detail:
PooledInstances()
as the lifetime option instead of SingleInstance() or InstancePerLifetimeScope(). You could probably set an optional max capacity or something.ServicePool
that contains pooled instances of the service.INotifyPoolEvents
, which has aRent
andReturn
method, called when we retrieve from the pool or put back, to do any necessary resetting. We could conceivably also store additional event handlers somehow to serve a similar purpose without modifying the type.Any thoughts? Could give people a nice easy route to use a set of pooled 'something' in their DI?
The text was updated successfully, but these errors were encountered: