Skip to content

Fix(monitoring): count session cards from each engine's own status (#1426) - #1809

Open
JaredAung wants to merge 9 commits into
libredb:mainfrom
JaredAung:fix/session-activity
Open

JaredAung wants to merge 9 commits into
libredb:mainfrom
JaredAung:fix/session-activity

Conversation

@JaredAung

Copy link
Copy Markdown
Contributor

Description

The Active, Idle, In TX, and Wait cards counted PostgreSQL's pg_stat_activity words (active, idle, idle in transaction, and any waitEventType). Other engines pass their own status text through, so the cards read 0 above a list of real sessions. On PostgreSQL an idle backend waiting on Client / ClientRead was counted as waiting, and Studio's other pooled monitoring connections were listed as user sessions.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test addition or update

Related Issue

Closes #1426

Changes Made

  • ActiveSessionDetails.activity is the card a session belongs on (active, idle, idle-in-transaction, waiting, other). The engine's own word stays in state for the row.
  • Monitoring → Sessions, Monitoring → Overview, and Admin → Operations count the cards from activity. A refused read, and a list that never classifies, both read N/A. An empty list still reads 0.
  • MySQL classifies COMMAND (Query active, Sleep idle, anything else other). Oracle classifies STATUS (INACTIVE idle; ACTIVE is waiting only for a wait class other than Idle or CPU). SQL Server classifies status (sleeping and dormant idle, suspended waiting, running or runnable waiting only while wait_type is set). Redis classifies CLIENT LIST flags (b waiting, every other flag including N idle).
  • PostgreSQL counts Wait only for an active session whose wait_event_type is set and is not Client. The pool sets application_name to libredb-studio, and getActiveSessions() leaves those connections out.
  • Session duration on PostgreSQL is measured to clock_timestamp(), so a statement that started after the reading transaction does not read below zero.
  • Provider docs for MySQL, Oracle, SQL Server, Redis, and PostgreSQL describe the classification.
  • Tests: the three panels, driven by MySQL, Oracle, SQL Server, and Redis shaped rows; provider tests for the classified activity on those four engines; PostgreSQL (an idle client wait stays idle, a lock wait is waiting, Studio's own application_name is filtered out).

Testing

  • I have tested this locally
  • I have added/updated tests
  • All existing tests pass

Test Environment

  • LibreDB Studio Version: 0.18.3
  • Browser:
  • OS: macOS
  • Node.js/Bun Version:
  • Database Type: PostgreSQL, MySQL, Oracle, SQL Server, Redis

Screenshots (if applicable)

Checklist

  • My code follows the project's code style guidelines
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have updated the documentation accordingly
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • The required CI test job passes the 100% line-coverage gate (bun run test:coverage and bun run coverage:check)
  • If I changed src/lib/db/providers/, I updated the matching docs/providers/ documentation and tests/integration/db/ tests in the same PR (provider triad)
  • Any dependent changes have been merged and published

Additional Notes

Engines that do not set activity keep showing N/A on these cards rather than a 0 that looks like a measurement. That includes engines whose state already used the words active and idle: MongoDB (active / idle from the operation), SQLite and ClickHouse (every listed session is active), and Databend and Neo4j (Query and Running rewritten to active, with any other status left as its own word).

@codecov

codecov Bot commented Oct 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@cevheri

cevheri commented Oct 17, 2026

Copy link
Copy Markdown
Member

FYI: preparing new release

@cevheri cevheri left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, Jared. The direction is right and the tests are solid: every new assertion fails on the old code. A few changes before I can merge it.

  1. Rebase on main. The branch conflicts with main in src/lib/api/object-route.ts, only in a docblock's line pointers, and main has moved a lot since your CI run. Pinning line numbers there is our mistake. Rebase and re-measure those pointers against the merged postgres.ts and redis.ts rather than taking either side.
    Done when: the PR shows no conflicts and the required checks are green on the rebased head.

  2. Oracle counts on-CPU sessions as waiting. When a session is not in a wait, V$SESSION keeps its last wait in EVENT and WAIT_CLASS, so an ACTIVE session on CPU whose last wait was User I/O reads as waiting. There is no CPU wait class. Select s.STATE and count waiting only for ACTIVE with STATE = 'WAITING' and a class other than Idle. Update the docs/providers/oracle.md row.
    Done when: a provider test with a realistic row (ACTIVE, WAITED SHORT TIME, User I/O) reads active, and one with STATE = 'WAITING' reads waiting.

  3. Five engines regress to N/A. MongoDB, SQLite, ClickHouse, Databend and Neo4j already counted Active and Idle correctly on main, and now read N/A because they set no activity. Set it in each provider, with its doc row and provider test (the provider triad in CLAUDE.md), and fix the Databend and Neo4j docblocks that still say the panels count state.
    Done when: each of the five has a provider test asserting activity, and the panels show counts for them, not N/A.

  4. Operations refused read is untested. Flipping the unavailable argument in OperationsTab.tsx keeps every test green.
    Done when: OperationsTab.test.tsx asserts the four cards read N/A on a refused read, and that test fails if the argument is flipped.

@cevheri cevheri added postgresql enhancement New feature or request labels Oct 19, 2026
@cevheri

cevheri commented Oct 21, 2026

Copy link
Copy Markdown
Member

I'm working on a VS Code extension for LibreDB Studio. I'll review the open PRs once it's ready.
Thanks for your patience!

https://marketplace.visualstudio.com/items?itemName=libredb.libredb-studio

@cevheri

cevheri commented Oct 23, 2026

Copy link
Copy Markdown
Member

any update for CR

Cards were matching PostgreSQL status words, so every other engine read 0. A session now carries the card it belongs on, and a list that was not classified reads N/A.
Query is active, Sleep is idle, and every other command stays other, with the raw command left on the row.
INACTIVE is idle, and ACTIVE counts as waiting only for a wait class other than Idle or CPU.
Sleeping and dormant are idle, suspended is waiting, and running counts as waiting only while wait_type is set.
An idle client wait stays idle, a lock wait counts as waiting, and pooled connections named libredb-studio are left out of the session list. Duration is measured to clock_timestamp() so it cannot read below zero.
A CLIENT LIST flag string containing b is waiting. Every other flag, including the N fallback, is idle.
@JaredAung
JaredAung force-pushed the fix/session-activity branch from aff1d53 to fc5d15a Compare October 24, 2026 03:57
@JaredAung

Copy link
Copy Markdown
Contributor Author

Thank you for the reviews. Just got the chance to address the CRs.

  1. Rebased onto current main. The only conflict was the line pointers in src/lib/api/object-route.ts. I re-measured them against the merged postgres.ts and redis.ts rather than taking either side. The PR has no conflicts, and the required checks are green on this head.

  2. Oracle selects s.STATE and counts waiting only for an ACTIVE session with STATE = 'WAITING' and a wait class other than Idle. The provider test covers ACTIVE / WAITED SHORT TIME / User I/O as active, and STATE = 'WAITING' as waiting. The docs/providers/oracle.md row matches.

  3. MongoDB, SQLite, ClickHouse, Databend, and Neo4j set activity, each with a provider test and a doc row. The Databend and Neo4j comments now say the panels count activity. Those cards show counts.

  4. OperationsTab.test.tsx asserts the four cards read N/A when the session read is refused, and that assertion fails if the unavailable argument is flipped.

This branch has not been deployed

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

Labels

enhancement New feature or request postgresql

Projects

None yet

2 participants