Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Rewrite NpgsqlDatabaseCreator.Exists() to do SELECT 1 - #3648

#3648
Merged
roji merged 1 commit into
npgsql:mainnpgsql/efcore.pg:mainfrom
roji:ExistsLeakroji/efcore.pg:ExistsLeakCopy head branch name to clipboard
Oct 30, 2025
Merged

Rewrite NpgsqlDatabaseCreator.Exists() to do SELECT 1#3648
roji merged 1 commit into
npgsql:mainnpgsql/efcore.pg:mainfrom
roji:ExistsLeakroji/efcore.pg:ExistsLeakCopy head branch name to clipboard

Conversation

@roji

@roji roji commented Oct 30, 2025

Copy link
Copy Markdown
Member

#3646 reported a memory leak: when NpgsqlDatabaseCreateor.Exists() is called (from DatabaseFacadeExtensions.CanConnect()), it was using NpgsqlConnection.CloneWith() to create a new, unpooled connection, which it then tried to connect to the database. The problem is, the cloned connection doesn't use an explicit data source, meaning that a data source is internally created inside Npgsql, referenced by the internal static dictionary. If the password continuously changes (rotating auth tokens), that led to increasing memory use which never gets freed.

This is ultimately npgsql/npgsql#3387: we should be able to call ClearPool() on the cloned connection to have it unreference the internal data source, allowing it to get garbage collected - though in theory, a real user in the application might be using the same connection string, which EF would then cause to get cleared; while quite contrived, it shows how bad the legacy connection mechanism is.

This PR simply changes to sending SELECT 1 to the database, which is what the SQL Server provider does as well. This does have the drawback of returning "exists" (and also "can connect") even when e.g. the password has changed (because there are pooled connections which allow executing SELECT 1), but that's seems like an unimportant edge case (the same pooled connections will be there for actual usage etc.). And the CloneWith() mechanism here has been the source of lots of trouble over the years, without adding much real value.

This also adds the execution strategy to Exists(), which allows retrying on transient errors.

Fixes #3646

Thanks @vonzshik for the initial investigation.

@roji
roji merged commit 3e175da into npgsql:main Oct 30, 2025
14 checks passed
@roji
roji deleted the ExistsLeak branch October 30, 2025 08:35
roji added a commit that referenced this pull request Oct 30, 2025
@roji

roji commented Oct 30, 2025

Copy link
Copy Markdown
Member Author

Backported to 9.0.5 via 36d5508
Backported to 8.0.12 via 3bb3109

roji added a commit that referenced this pull request Oct 30, 2025
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.

Memory Leak on NpgsqlDataSource with password provider

1 participant

Morty Proxy This is a proxified and sanitized view of the page, visit original site.