Skip to content

fix(coordinator): stop restored tabs from auto-running queries and spamming errors on launch (#1796)#1798

Merged
datlechin merged 2 commits into
mainfrom
fix/1796-restore-no-autorun
Jul 1, 2026
Merged

fix(coordinator): stop restored tabs from auto-running queries and spamming errors on launch (#1796)#1798
datlechin merged 2 commits into
mainfrom
fix/1796-restore-no-autorun

Conversation

@datlechin

Copy link
Copy Markdown
Member

Fixes #1796.

Problem

Quitting TablePro with tabs open, then reopening, re-ran every restored table tab's data load. On PostgreSQL these failed and stacked a modal "Query Execution Failed" sheet per tab, with no way to stop them. The same connection worked fine in DBeaver.

Root cause (three compounding defects)

  1. Dual, uncoordinated restoration. AppKit NSWindowRestoration and the app's own AppLaunchCoordinator reopen were both armed under the default Reopen Last Session, arbitrated only by a 150ms timing guard. When restoration ran slower than 150ms, both fired and opened two window sets for the same connection. Each window then auto-ran the same query against the one shared connection; overlapping queries on a single PostgreSQL wire connection fail (works in DBeaver because it runs one query at a time).
  2. Every restored tab auto-executed. Each restored tab opens as its own window and eagerly loaded its data, so N restored tabs meant N concurrent queries.
  3. Failure presentation was blind to origin. A background restore failure showed the same modal dialog as a user-initiated query. The skipAutoExecute flag meant to prevent this was dead on the restore path.

Fix

  • One deterministic restoration path. Disabled AppKit window restoration for editor windows and made the app's own reopen the single path. No race. Window frames and tab state already persist independently, so nothing is lost. Reopen now honors the app's Startup Behavior setting regardless of the macOS "Close windows when quitting an app" system setting.
  • Load the front tab, defer the rest. Only the frontmost restored tab loads its data immediately; other restored tabs open as unselected native tabs and load the first time you switch to them. The deferred trigger fires from both windowDidBecomeKey and the populate path, so it is immune to ordering.
  • Restore failures show inline, not modal. New TableLoadTrigger threaded end-to-end; a restore-triggered failure sets the inline error banner and skips the modal. User-initiated runs keep the dialog. Replaced needsLazyLoad: Bool with pendingLoadTrigger: TableLoadTrigger? so the reason survives the wait-for-connection gap.

This matches Apple's state-restoration guidance (restore UI state, load data on user intent) and how DataGrip and TablePlus behave.

Tests

  • New RestoreWindowPlanTests (front-tab resolution, all branches) and TableLoadTriggerTests.
  • Updated MainContentCoordinatorLazyLoadTests (deferred no-op, become-key consume, restore-trigger carry) and MultiWindowRestorationTests (load-timing round-trip).

Notes

  • SwiftLint --strict clean on changed files.
  • Behavior change worth review: editor windows no longer use native NSWindowRestoration; the app's own reopen drives session restore.

@mintlify

mintlify Bot commented Jul 1, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Jul 1, 2026, 11:18 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8d2359bd22

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

guard let deferredId = deferredRestoreLoadTabId,
deferredId == tabManager.selectedTabId else { return }
deferredRestoreLoadTabId = nil
lazyLoadCurrentTabIfNeeded(trigger: .restore)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore schema before loading deferred tabs

When a restored background table tab becomes key after the connection is already established, this new deferred path goes straight to lazyLoadCurrentTabIfNeeded. Unlike the pending-load path in MainContentView+Helpers.consumePendingLoad, it never compares the tab's saved schemaName with session.currentSchema or calls restoreSchemaAndRunQuery, so a PostgreSQL tab restored from a non-current schema can run its first load and metadata/row-count fetches under the schema left active by another restored tab. Route deferred restore consumption through the same database/schema restoration step before loading.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8dac706d78

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

for: restorePayload.id
)
WindowManager.shared.openTab(payload: restorePayload)
WindowManager.shared.openTab(payload: restorePayload, activate: activate)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don’t let background restores replace the command target

When restoring multiple tabs, this now opens every non-front tab with activate: false, but each restored window still runs configureWindow, which unconditionally assigns CommandActionsRegistry.shared.current even if window.isKeyWindow is false. In a multi-tab restore, whichever background tab finishes configuring last can become the fallback target for menu/keyboard commands, so actions like Run Query/New Tab may operate on a hidden restored tab until the user changes focus again; either avoid publishing for non-key background restores or republish the key window after opening them.

Useful? React with 👍 / 👎.

@datlechin datlechin merged commit cfaedc7 into main Jul 1, 2026
3 checks passed
@datlechin datlechin deleted the fix/1796-restore-no-autorun branch July 1, 2026 12:36
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.

When i Quit Table Pro and reopen, it tries to execute all queries and failing

1 participant