Repository navigation
fix(db): closing a table tab force-closes the read-only session other views are using #1215
Copy link
Copy link
Closed
Labels
P1High priority / Core capabilityHigh priority / Core capabilitybugSomething isn't workingSomething isn't workingconnectionsDatabase connections, URI parsing, poolsDatabase connections, URI parsing, poolscoreCore library logic and servicesCore library logic and servicesstabilityTheme parser epic label: stabilityTheme parser epic label: stability
Description
Part of #1212.
Problem
Closing a table browser tab force-closes the read-only session that other views are using.
GenericTableView.dispose()callswidget.delegate.cancel(interruptIfBusy: true)(lib/features/workspace/generic_table_view.dart:289).- Despite the name, the delegates never check whether anything is running. They interrupt the
readOnlyand thetableWriteslot of the connection and database unconditionally:- PostgreSQL:
PostgresTableDataDelegate.cancel(lib/features/postgresql/postgres_table_view.dart:238) - MySQL:
lib/features/mysql/mysql_table_view.dart:237 - SQLite:
lib/features/sqlite/sqlite_table_view.dart:225
- PostgreSQL:
interruptremoves the pool entry and callsforceClose()on its connection. That connection is the same object everyreadOnlycaller of this database holds: the tree catalog, stats dashboards, other open table tabs, the Relations view and MCP calls.
Effects:
- Any of those views that has a statement in flight gets
Not connected to PostgreSQL/Not connected to MySQL/ a closed-connection error. Closing tab A breaks tab B's page load, the tree expansion, or an MCPrun_queryrunning at that moment. - Every following call reconnects, which runs into the concurrent-connect race (fix(db): concurrent connect() on a pooled connection leaks sockets and SSH tunnels, a failed attempt drops the working session #1214) and the lease release bug (fix(db): a lease released after interrupt decrements the pool entry that replaced it #1213).
- A table Save in flight on
tableWriteis cut as well.
Scope
dispose()releases the lease and never closes a shared session.- Track in-flight calls per delegate. Only when this delegate has a statement running, cancel that statement:
- PostgreSQL: a cancel request for the backend (
Connection.cancelPendingStatement()inpostgres3.x) — the socket stays open. - MySQL:
KILL QUERY <connection id>from a separate short-lived connection, or release the lease and let the statement finish. - SQLite: release only (statements are local).
- PostgreSQL: a cancel request for the backend (
- Rename
interruptIfBusyor make it do what it says. - Check the other callers of
interrupt/interruptAllModesfor the same pattern (workspace close, connection switch) and keep force-close only where the whole connection is meant to go away (disconnect, delete).
Acceptance
- Widget test: two table tabs on the same database; close one while the other loads a page (fake delegate that blocks) → the other one finishes without reconnecting.
- Unit test:
disposeof an idle table delegate does not callinterrupt/forceClose. - Unit test:
disposewhile its own statement runs sends a cancel for that statement and leaves the shared connection connected.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
P1High priority / Core capabilityHigh priority / Core capabilitybugSomething isn't workingSomething isn't workingconnectionsDatabase connections, URI parsing, poolsDatabase connections, URI parsing, poolscoreCore library logic and servicesCore library logic and servicesstabilityTheme parser epic label: stabilityTheme parser epic label: stability
Activity
Merge pull request #1221 from QueryaHub/fix/1215-tab-dispose-shared-s…
Merge pull request #1236 from QueryaHub/fix/1215-sequence-routine-dis…
ZhuchkaTriplesix commentedon Oct 9, 2026
Closed by #1221 and #1236.