A terminal-based live monitoring tool for PostgreSQL (similar to top / htop). It periodically reads pg_stat_activity, pg_stat_statements, locks, and table/index summaries with low overhead; for pg_stat_statements it diffs against the previous snapshot and keeps a 5-slot ring buffer (default 2s interval) for the last 10-second activity window.
The TUI is a fixed grid: each box is fed by a specific catalog / view (or a join built on them). Roughly:
| Screen region | PostgreSQL source | What you are looking at |
|---|---|---|
| Live queries (top-left) | pg_stat_activity |
One row per backend on the current DB: pid, user, age, state, truncated query, wait info. |
| Top queries (top-right) | pg_stat_statements (extension) |
Aggregated statement stats: calls, total/mean time, rows, buffer hits — sortable; d toggles cumulative vs last 10s delta window. |
| Locks / blocking (middle) | pg_locks + pg_stat_activity |
Blocked vs blocking pid, wait time, relation, snippet of the blocked query. |
| Tables / indexes (strip) | pg_stat_user_tables + pg_stat_user_indexes |
High seq_scan tables; unused index candidates (idx_scan = 0 among top candidates). |
| Footer | — | Global keybindings (q, r, s, /, …). |
Typical layout (colors omitted; narrow terminals may wrap lines):
[ top-left: live sessions ] [ top-right: statement stats ]
┌─ live queries (pg_stat_activity) ────────┐┌─ top queries [cum sort:total_exec_time] ─┐
│ 1842 app 1.2s active SELECT … ││ calls 12400 total 890.1ms mean 0.07ms | … │
│ 1845 app 12.4s active UPDATE … ││ calls 2100 total 120.5ms mean 0.06ms | … │
│ 1901 report 0.3s idle COMMIT … ││ calls 980 total 45.2ms mean 0.05ms | … │
│ ... ││ ... │
└──────────────────────────────────────────┘└──────────────────────────────────────────────┘
[ middle: who blocks whom ]
┌─ locks / blocking ────────────────────────────────────────────────────────────────────────┐
│ 1842 ← 1903 wait 3.2s public.orders │ SELECT * FROM orders WHERE … │
│ (no blocking pairs) │
└──────────────────────────────────────────────────────────────────────────────────────────┘
[ bottom strip: table + index health snapshot ]
┌─ tables / indexes ────────────────────────────────────────────────────────────────────────┐
│ tables public.orders:seq=12000 public.users:seq=800 ... │
│ unused idx public.idx_foo_created(idx_scan=0) ... │
└──────────────────────────────────────────────────────────────────────────────────────────┘
q quit r resetΔ s sort(tot) d delta(cum) / filter l locked i idle-tx tab focus …
- Node.js 18+ (LTS recommended)
- PostgreSQL 10+ recommended (earlier versions are untested)
On connect, pgpulse reads current_setting('server_version_num') and picks SQL and feature flags accordingly (see src/utils/version.js, src/utils/sql.js, src/collectors/pgServerStats.js).
| Area | Behavior |
|---|---|
pg_stat_activity.backend_type |
Selected only on PG 10+. On older servers the query omits this column (compatible with pre-10 catalogs). |
pg_stat_statements (Top queries panel) |
PG 13+: total_exec_time / mean_exec_time. PG 12 and older: total_time / mean_time, exposed in the UI under the same labels via aliases. Requires the extension (not built-in). |
| Footer: checkpoint / writer stats | PG 16 and older: pg_stat_bgwriter. PG 17+: pg_stat_checkpointer (checkpoint-related stats moved; pg_stat_bgwriter no longer used for this hint). |
Install the extension on the database if you want the Top queries panel:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;Install globally so the pgpulse command is available on your PATH:
npm i -g pgpulseThen run:
pgpulseIf you install better-sqlite3 for optional local snapshot history, you can use --sqlite (requires a native build; typically when developing from a clone with optional deps installed).
A static single-page overview lives at docs/index.html (open locally or publish with GitHub Pages).
Clone the repository, install dependencies, and run locally:
git clone <repo-url> && cd pgpulse
npm install
npm startUnit tests use Vitest. Test files live next to sources: src/**/*.test.js.
npm test # run once
npm run test:watch # watch modeConfiguration: vitest.config.js (environment: node, include: src/**/*.test.js). Pure helpers (utils, engine/delta, SQL builders) are covered; TUI and live DB access are not.
Precedence order:
DATABASE_URL--connection-stringPGHOST,PGPORT,PGUSER,PGPASSWORD,PGDATABASEand optional flags:--host,--port,--user,--password,--database
Examples:
DATABASE_URL=postgres://user:pass@localhost:5432/mydb pgpulsepgpulse --host localhost --port 5432 --user postgres --database app| Flag | Description |
|---|---|
--interval <ms> |
Refresh interval (default 2000) |
--long-threshold <s> |
Long-query threshold; yellow/red highlighting (default 5) |
--export <file> |
Default path for export when using e |
--sqlite <file> |
Optional SQLite snapshot history |
-h, --help |
Help |
| Key | Action |
|---|---|
q |
Quit |
Tab |
Focus panels (activity / statements / locks) |
r |
Reset delta baseline and 10s window |
s |
Sort: total_exec_time → mean_exec_time → calls |
d |
Toggle cumulative vs last 10s delta window |
/ |
Text filter (Enter to apply, Esc to cancel) |
l |
Show only backends involved in locks |
i |
Show only idle in transaction |
Enter |
Full query text for selection (activity / statements) |
t |
Table analysis: pick a user table (high seq_scan sample), then per-table index list + heap/cache stats |
x |
EXPLAIN on selected statement (confirm) |
k |
pg_terminate_backend (confirm) |
e |
Export JSON (--export or timestamped file) |
src/collectors/*: Each runs focused/optimized SQL and returns normalized JSON.src/engine/snapshot.js: Merges all collector output.src/engine/delta.js:pg_stat_statementsdeltas keyed byuserid:dbid:queryidplus a 10s ring.src/ui/layout.js: neo-blessed grid, modals, global shortcuts.ttable analysis: list of hot tables (pg_stat_user_tables+pg_statio_user_tables+ size); Enter loads that table’s indexes frompg_stat_user_indexeson demand.- Footer (2nd line): PostgreSQL server snapshot (via SQL): buffer cache hit % (
pg_stat_databaseblks_hit/read), sessions vsmax_connections,shared_buffers/work_mem(frompg_settings), deadlocks, temp_files, checkpoint pressure hint (pg_stat_bgwriteron PG ≤16,pg_stat_checkpointeron PG 17+). This is not the OS process CPU% or total server RAM — those require OS/agent metrics outside PostgreSQL.
EXPLAINandpg_terminate_backendrequire sufficient privileges; use carefully in production.- Long query text is truncated in the UI; the modal shows the full text.
MIT