Repository navigation
fix(models): close has_view connection and dispose evicted engines - #45861
Open
sadpandajoe wants to merge 2 commits into
Open
sadpandajoe wants to merge 2 commits into
sadpandajoe wants to merge 2 commits into
Conversation
…viction DuckDB allows one configuration per database file per process, so a connection that is left open keeps the previous settings alive and opening the same file with edited settings fails with a conflicting configuration error. has_view opens a connection and never releases it; with the default NullPool the connection is only closed on release, so it stays open until the cyclic garbage collector reclaims it. Evicting a database's cached engines on update or delete drops them without disposing, so an idle pooled connection stays open too. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
has_view acquired a connection without releasing it. With the default NullPool a connection is only closed on release, so the database stayed open with its previous settings until the cyclic garbage collector reclaimed the leaked connection. For databases that allow one configuration per file per process, such as DuckDB, opening the edited settings failed in the meantime. The connection is now released with a context manager. Evicting a database's cached engines on update or delete now also disposes them, outside the cache lock, so idle pooled connections are closed. A failure to dispose is logged and never interrupts the update or delete. Connections checked out at that moment stay on the engine's previous pool. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #45861 +/- ##
=======================================
Coverage 84.70% 84.70%
=======================================
Files 3069 3069
Lines 205597 205614 +17
Branches 48327 48330 +3
=======================================
+ Hits 174148 174164 +16
- Misses 28157 28158 +1
Partials 3292 3292
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
sadpandajoe
marked this pull request as ready for review
October 29, 2026 22:45
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SUMMARY
Databases that allow only one configuration per database file per process, such as DuckDB, reject opening a file with different settings while any connection to it is still open in that process (
Connection Error: Can't open a connection to same database file with a different configuration than existing connections). After a connection's settings are edited (for example a rotated access token), Superset could keep a connection with the old settings open in two ways, so the edited settings failed to open until the process released it:Database.has_viewopened a connection withengine.connect()and never released it. With the defaultNullPoola connection is only closed once it is released, so the database stayed open with its previous settings until the cyclic garbage collector reclaimed the leaked connection. Disposing or evicting the engine does not help, because the connection is checked out rather than pooled.has_viewis reached from physical-table metadata discovery whenhas_tableis false._evict_engine_cachedropped a database's cached engines on update or delete without disposing them, so an engine whose pool held an idle connection (for example whenDB_CONNECTION_MUTATORswaps in a pooledpoolclass) kept that connection open after the edit.This PR:
has_viewconnection with a context manager (sameget_view_namescall, same exception fallback, same return value);_evict_engine_cache, after the cache lock is released. A failure to dispose is logged with a static message and never interrupts the update or delete.Root Cause
Two independent ways a process leaves a connection with the old configuration open, reproduced against a real file-backed DuckDB database (DuckDB 1.5.6, duckdb-engine 0.17.0, SQLAlchemy 2.0.55):
has_viewleaked its connection (defaultNullPool, no mutator needed).Limits (stated plainly)
This change does not claim to resolve every way these errors can appear:
Engine.dispose()closes idle pooled connections; connections checked out at that moment are not released deterministically;A cross-worker retirement of stale engines and disposal of unsaved-database engines are possible follow-ups; they need their own design (a settings generation that is ordered with the commit).
BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
Not applicable (backend only). Regression guards, RED on the parent commit and GREEN with the fix:
test_has_view_closes_its_connectiontest_engine_evicted_on_update_releases_pooled_connectionBoth fail on the parent commit with
sqlalchemy.exc.OperationalError: (_duckdb.ConnectionException) Connection Error: Can't open a connection to same database file with a different configuration than existing connectionsand pass with the fix.test_engine_cache_eviction_survives_dispose_failurecovers the dispose-failure handling.TESTING INSTRUCTIONS
The two regression guards alone:
pytest tests/unit_tests/models/core_test.py -k "pooled_connection or has_view_closes" -p no:cacheprovider.ADDITIONAL INFORMATION
No new dependency, no migration, no change to the cache key or the
nullpooldefault. For engines usingNullPool, disposal is a no-op.