-
Notifications
You must be signed in to change notification settings - Fork 227
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
Each new NpgsqlDataSourceBuilder() call holds 1 more Database Connection forever and locks the DB eventually #3377
Comments
The report above is confusing:
So which is it, connections or NpgsqlDataSourceConfiguration?
When exactly something gets cleaned up by the GC depends on many factors, but objects do not get cleaned up right away; it's possible that a lot of time must pass, or that memory becomes low before the GC will kick in. Seeing 14 NpgsqlDataSourceConfiguration in the memory profiler is not, in itself, any indication of a memory leak or similar. |
Regarding GC: DotMemory calls GC before creating each Snapshot (screenshots above show snapshot data). More than that, I also called GC manually by pressing the corresponding DotMemory button. Neigher NpgsqlDataSourceConfiguration nor DB connections were released after that. There is no such problem when I rewrite the code and call |
So just to be clear: calling NpgsqlDataSourceBuilder.Build() builds an NpgsqlDataSource; this NpgsqlDataSource references a NpgsqlDataSourceConfiguration and stays "alive", and both will continue to be in memory until there are no references to the NpgsqlDataSource. Also, as NpgsqlDataSource is disposable, it should be disposed (via Dispose() or DispoesAsync()). In general, applications are expected to only need to build a single NpgsqlDataSource for the entirety of the application (or possibly one-per-tenant, if they're multitenant and need different connection pools for each tenant). You seem to want to have two data sources - one for read-only usage and one for read-write - and that's fine; but in that case you should only ever be building two NpgsqlDataSources in your application. So I'd concentrate on understanding exactly when you're building NpgsqlDataSources and why; looking at NpgsqlDataSourceConfiguration via DotMemory really is secondary and not the important thing here. |
It doesn't work, I guess. Each subsequent call
I need to change ConnectionString time to time during API lifetime. What is the best way to implement that without memory objects/connection stuck issues? |
No. As I wrote above, you should not be calling UseNpgsql() with a new, different dataSource every time - doing so will cause severe performance problems (each data source represents a connection pool, so this effectively disables connection pooling). What you need to do, is have one or maybe two global/singleton data source instances, period. You can register these data sources as singletons in your DI container (as keyed services), or use some other mechanism to manage them. Note that if you're using 9.0, it's recommended to not configure and instantiate your NpgsqlDataSources outside of EF, and then pass them to UseNpgsql(); instead, use the new ConfigureDataSource() API (see the release notes). |
@roji Is it possible to obtain the Thanks |
DbContext doesn't itself expose the NpgsqlDataSource it was configured with, no. But if you also have external, non-EF usage (e.g. Dapper), then I'd advise registering your NpgsqlDataSources in DI (or both of them - via keyed services), and then using that same data source in both EF (passing it to UseNpgsql) and Dapper. That's the simple, recommended route, rather than trying to have EF manage your NpgsqlDataSource and then extracting it out for Dapper. I'll go ahead and close this as everything seems to be working by design. But if you have further questions don't hesitate to post back here. |
Thanks @roji, I can do that easily, only passing the |
Sure. If you're only using EF, definitely use ConfigureDataSource(). But if you really do need to share a data source between EF and something else, it's perfectly fine to build your data source externally, and pass it to EF. You just have to make sure that your data source and EF configuration align (e.g. enums and plugins must be set up in the same way at both layers). |
We have two Postgres databases: RW and RO. Accordingly, our AspNet core project uses two Entity Framework's DbContext: RW and RO. Each has its own connection string to the corresponding DB. Connection strings should not be hardcoded, they should be fetched from some function with some logic inside. This function should be called on each DbContext creation.
DbRwConnectionOptionsReader.cs
DbRoConnectionOptionsReader.cs
There is a Registrator class contains all DI registrations, including DB registration
Registrator.cs
It is created in Program.cs
The problem is each call
creates +1 Database connection hanging forever.
Eventually a Postgres Connection Limit is reached and API stops to serve any further requests with error
too many connections for role "role_name"
Jetbrains DotMemory analysis shows there are only 2 NpgsqlDataSourceBinder instances after many requests, which is totally fine (first is for the RW and second is for RO, I guess)
But there are 14 NpgsqlDataSourceConfiguration, they are not cleaned up by GC, why?
More dotMemory screenshots
The text was updated successfully, but these errors were encountered: