What's the performance benefit of pooling DbContexts
See original GitHub issueSince EF Core 2.0 it supports pooling of DbContext
instances. I can find statements from @anpete such as:
The new method introduces a few limitations on what can be done in the OnConfiguring() method of the DbContext but it can be adopted by many ASP.NET Core applications to obtain a performance boost.
Implying that pooling give a performance boost, while @ajcvickers states:
The point of DbContext pooling is to allow reuse of DbContext instances from a pool, which for certain scenarios can result in a performance boost over creating a new instance each time. This is also the primary reason for connection pooling in ADO.NET, although the performance boost for connections will be more since connections are generally a more heavyweight resource.
Implying that DbContext pooling is not as significant as connection pooling.
I couldn’t find any analysis or benchmarks of the actual performance improvement this brings. To my knowledge, the creation of DbContext
instances is really light. A small local test showed that I could make over 600,000 instances a second on a single thread.
I found this question from @davidroth:
Out of curiosity: Can you share some benchmarks about this improvement?
Unfortunately, the question was ignored.
What does cause a performance hit when DbContext instances are not reused? Can we find a publically accessible benchmark that shows what the benefit of pooling is and especially an alalysis of what is causing the bottleneck without the reuse of DbContext instances?
Thanks in advance.
Issue Analytics
- State:
- Created 6 years ago
- Reactions:15
- Comments:17 (8 by maintainers)
@dotnetjunkie DbContext initialization is a two-part process. Creating the instance itself does minimal work–basically just initializing any DbSet properties. This means creating a DbContext that is not used does not add significant overhead, which is important for scenarios such as a controller that may need to access the database but often does not–just injecting/creating the instance should not be a perf issue.
The first time the context is used, for a query, to track something, etc., there is a second lazy initialization that happens. This involves loading some services from EF’s internal D.I. that are scoped to the same lifetime as the context. Not all services are like this–many are singletons that are shared between all context instances. Also, not all services are initialized at this time–services that may not be needed can be loaded lazily–however, we have seen that this is sometimes more expensive than loading immediately due to the way the D.I. system compiles constructor calls.
The second initialization step is also not really slow, but DbContext pooling means that instead of doing this step, an existing pooled instance is used instead, with resetting of the service internals. This is less flexible–each context instance in the pool must be configured the same way so that the same set of services with the same configuration is correct each time.
Whether or not this really shows a measurable difference in your app depends a lot on how much else the context is doing. Where it works well is for simple, no-tracking queries where very little other work is being done. As soon as the context starts to do more work, the relative amount of time saved by pooling is much less.
It’s also worth keeping in mind that there is additional initialization which is not bound to a particular context instance. So, for example, the first time a model is used by any context instance there will be a hit while the model is built and cached. Likewise for the first time a query is executed, and so on. Pooling will not have any impact on these things.
Hope that helps!
@gius - All services including ChangeTracker are reset when the DbContext is returned to the pool. So whenever you get a context from pool, it is same as what it would be if you re-initialize it (except for few limitations).