Skip to content

fix: resolve high CPU usage and memory leaks at idle (#368)#369

Merged
datlechin merged 5 commits intomainfrom
fix/368-high-cpu-usage
Mar 18, 2026
Merged

fix: resolve high CPU usage and memory leaks at idle (#368)#369
datlechin merged 5 commits intomainfrom
fix/368-high-cpu-usage

Conversation

@datlechin
Copy link
Collaborator

@datlechin datlechin commented Mar 18, 2026

Summary

Root cause: A cascading UI re-render loop where every 30-second health monitor ping, every tab switch, and every window focus change triggered unconditional activeSessions dictionary writes that invalidated all open windows' views — even when nothing meaningful changed.

Changes (16 fixes across 15 files)

Critical — Idle CPU elimination:

  • Guard updateSession() against no-op writes using isContentViewEquivalent + driver identity comparison
  • Skip onStateChanged callback for routine healthy↔checking health monitor ping cycles
  • Route switchToSession through guarded updateSession (lastActiveAt-only changes are no-ops)
  • Add per-connection connectionStatusVersions dictionary so each window observes only its own connection's counter

High — Correctness & Performance:

  • Track all driver operations (fetchTables, fetchColumns, executeSchemaChanges) in queriesInFlight to prevent health monitor TOCTOU races on shared non-thread-safe C driver handles
  • Fix activeCoordinators memory leak by moving registerForPersistence() from init() to markActivated()
  • Move DatabaseRowProvider.prefetchRows DB fetch off MainActor with tracked task and dedup guard

Medium — Startup & UI:

  • Move Keychain migration to Task.detached for non-blocking startup
  • Move plugin bundle.load() off MainActor via nonisolated static method
  • Replace loadConnections() disk I/O in ContentView.init with in-memory lookup
  • Change WindowLifecycleMonitor to weak NSWindow references with purgeStaleEntries()
  • Guard redundant .connecting status writes during reconnect attempts
  • Use targeted reloadData(forRowIndexes:columnIndexes:) for FK metadata instead of full table reload
  • Debounce JSON syntax highlighting with 100ms DispatchWorkItem
  • Replace O(n²) undo batch index shift with O(n log n) binary search
  • Add [weak self] to nested DispatchQueue.main.async in SQLEditorCoordinator

Performance impact

With 5 open connections at idle:

  • Before: 10 MainActor dispatches + 10 onChange closures evaluated every 30 seconds, plus full re-render storm on every tab switch
  • After: Zero MainActor dispatches during healthy ping cycles; updateSession no-ops skip dictionary writes entirely; each window only observes its own connection

Test plan

  • Open 3+ database connections in separate windows
  • Leave app idle for 2+ minutes — verify CPU stays near 0% in Activity Monitor
  • Switch between connection windows — verify no visual flicker in other windows
  • Disconnect one connection — verify window closes, others unaffected
  • Open ConnectionSwitcherPopover — verify all sessions listed correctly
  • Reconnect a connection — verify lazy-load fires correctly
  • Scroll large result sets — verify smooth prefetch (no main thread blocking)
  • Edit JSON cell — verify syntax highlighting responds smoothly
  • Undo batch row insertion (100+ rows) — verify no UI freeze

Summary by CodeRabbit

  • Bug Fixes

    • Better window lifecycle handling to avoid memory leaks and stale window entries
    • Reduced unnecessary UI updates for connection health/state transitions
  • Performance

    • Background plugin loading and improved startup responsiveness
    • Debounced JSON syntax highlighting for smoother typing
    • Targeted data-grid refreshes for foreign-key columns only
    • Cancellable, windowed prefetching for faster, more stable data loading

Loading
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.

1 participant