Skip to content

Idle CPU stays at ~35% when restored Query Tool tabs sit at the "Connect to server" password prompt #10386

Description

@htcom-code

Describe the bug

When pgAdmin restores Query Tool tabs on startup and those tabs cannot connect because no password is saved, each tab opens a "Connect to server" dialog and its connection bar stays in the (Obtaining connection) state, showing an indeterminate CircularProgress spinner.

Those spinners keep animating for as long as the dialogs wait for input, which can be indefinitely. With four such tabs the application sits at ~35% CPU while completely idle, with no user interaction and no queries running.

The state is reached without doing anything unusual: Save the application state? defaults to True (web/pgadmin/misc/__init__.py:131), so simply quitting with Query Tool tabs open and restarting reproduces it whenever the server's password is not saved.

To Reproduce

Steps to reproduce the behavior:

  1. Connect to a server whose password is not saved (Save password? unchecked).
  2. Open four Query Tool tabs against that server.
  3. Leave Preferences → Miscellaneous → User Interface → Save the application state? at its default (True).
  4. Quit pgAdmin and start it again.
  5. The four tabs are restored. Each fails with fe_sendauth: no password supplied and opens a "Connect to server" dialog; the connection bar of each shows (Obtaining connection) with a spinner.
  6. Leave the dialogs untouched and watch CPU usage.

Expected behavior

While a dialog is waiting for the user, pgAdmin should be idle. A progress spinner should only animate while a request is actually in flight, not for the entire time a modal waits for input.

Measurements

Taken on the renderer via CDP Performance.getMetrics, as 5-second differences:

Metric Per second
RecalcStyleCount 480.68
RecalcStyleDuration 0.16 s (≈16% of one core)
LayoutCount 0.40
ProcessTime 0.32 s
ThreadTime 0.30 s

480/s is 4 iframes × 120 Hz — the display is a 120 Hz ProMotion panel, so every spinner animates at 120 fps. LayoutCount stays near zero, so this is pure style recalculation from the CSS animations, not layout.

Isolation, same session:

Condition Idle CPU
As restored (baseline) 34.6%
Query Tool iframe requestAnimationFrame loop blocked 35.6% (no effect)
The 8 spinner animations paused via Animation.pause() 0.8%

Each MUI CircularProgress runs two CSS animations, so four tabs is eight animations. Pausing them alone removes essentially all of the idle cost, which makes the spinners the cause rather than a correlate. That works out to roughly 9% CPU per stuck tab on a 120 Hz display; it will be lower on a 60 Hz one.

Cause in the code

web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx opens the password dialog on HTTP 428 but leaves obtaining_conn set to true for the whole time the dialog is open. ConnectionBar renders the spinner directly off that flag:

// web/pgadmin/tools/sqleditor/static/js/components/sections/ConnectionBar.jsx:52
if(connecting) {
  return <CircularProgress style={{height: '18px', width: '18px'}} />;
}

Both connection paths do this — initializeQueryTool() (the restore path above) and updateQueryToolConnection(). Only the cancel callback of the first one clears the flag.

The ERD tool already avoids this: ERDTool.jsx sets conn_status: CONNECT_STATUS.FAILED before calling connectServerModal(), so its spinner stops while the dialog waits. The Query Tool does not do the equivalent.

A related defect on the same path

In updateQueryToolConnection(), the 428 branch's cancel callback is only /*This is intentional (SonarQube)*/. Cancelling that dialog therefore leaves obtaining_conn at true permanently — the connection bar keeps spinning with no dialog on screen and no way to clear it short of closing the tab — and the returned promise is never settled.

Suggested fix

Clear obtaining_conn before showing the dialog and set it again only when a connect request is actually issued. Since connectServer() also re-opens the dialog itself after a wrong password, it needs to report that transition back to the caller for the retry case to be covered too.

Desktop (please complete the following information):

  • OS: macOS 26.6.2 (Apple silicon, 120 Hz ProMotion display)
  • pgAdmin version: 9.17
  • Mode: Desktop (Electron)
  • Package type: macOS app bundle

Additional context

ps-reported %CPU is an average since process start, not an instantaneous value, so it understates this considerably; the numbers above come from CDP metric deltas. Note also that document.getAnimations() returns nothing useful from the top-level document here — pgAdmin hosts tools in iframes, so the animations are only visible via iframe.contentDocument.getAnimations().

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions