-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
206 lines (194 loc) · 16.7 KB
/
index.html
File metadata and controls
206 lines (194 loc) · 16.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
<!DOCTYPE html>
<html lang="en" class="light">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="light dark" />
<meta name="description" content="pgpulse — terminal-based live PostgreSQL monitoring (top/htop style). pg_stat_activity, pg_stat_statements, locks, tables, indexes." />
<title>pgpulse — PostgreSQL live monitor</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = { darkMode: 'class' }
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/bash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/sql.min.js"></script>
<style>
html { scroll-behavior: smooth; }
pre { tab-size: 2; margin: 0; }
pre code { padding: 0; background: transparent; }
code { font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.9em; }
.doc-section { scroll-margin-top: 2rem; }
table { border-collapse: collapse; }
th, td { border: 1px solid #e4e4e7; padding: 0.5rem 0.75rem; text-align: left; }
th { background: #f4f4f5; font-weight: 600; }
.dark th, .dark td { border-color: #3f3f46; }
.dark th { background: #27272a; }
.ascii { font-family: ui-monospace, monospace; font-size: 0.7rem; line-height: 1.35; overflow-x: auto; }
@media (min-width: 640px) { .ascii { font-size: 0.75rem; } }
</style>
</head>
<body class="bg-zinc-50 text-zinc-900 antialiased dark:bg-zinc-950 dark:text-zinc-100 transition-colors">
<div class="max-w-4xl mx-auto px-6 py-12">
<div class="flex items-center justify-between mb-10">
<nav class="flex flex-wrap gap-x-4 gap-y-2 text-sm">
<a href="#overview" class="text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200">Overview</a>
<a href="#layout" class="text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200">Layout</a>
<a href="#install" class="text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200">Install</a>
<a href="#compatibility" class="text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200">PG versions</a>
<a href="#connection" class="text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200">Connection</a>
<a href="#keyboard" class="text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200">Keys</a>
<a href="#metrics" class="text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200">PG metrics</a>
<a href="#architecture" class="text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200">Architecture</a>
</nav>
<button id="theme-toggle" type="button" class="p-2 rounded-lg text-zinc-500 hover:bg-zinc-200 dark:hover:bg-zinc-800 dark:text-zinc-400" aria-label="Toggle dark mode">
<svg class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
<svg class="w-5 h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
</button>
</div>
<header class="mb-14 flex items-start gap-4">
<div class="flex-shrink-0 w-14 h-14 rounded-xl bg-emerald-600/15 dark:bg-emerald-500/20 border border-emerald-600/30 flex items-center justify-center" aria-hidden="true">
<svg class="w-8 h-8 text-emerald-600 dark:text-emerald-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M4 12h3l2 6 4-12 2 6h5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div>
<h1 class="text-4xl font-bold tracking-tight text-zinc-900 dark:text-white">pgpulse</h1>
<p class="mt-2 text-xl text-zinc-600 dark:text-zinc-400">
Real-time PostgreSQL monitoring in the terminal — like <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded text-base">top</code> / <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded text-base">htop</code>, for your database.
</p>
<p class="mt-3 text-zinc-500 dark:text-zinc-500 text-sm max-w-2xl">
Built with Node.js, <code class="bg-zinc-200/80 dark:bg-zinc-800 px-1 rounded">pg</code>, and <code class="bg-zinc-200/80 dark:bg-zinc-800 px-1 rounded">neo-blessed</code>. Polls system views on an interval, tracks <code class="bg-zinc-200/80 dark:bg-zinc-800 px-1 rounded">pg_stat_statements</code> deltas in a short ring buffer for a rolling “last ~10s” window.
</p>
</div>
</header>
<section id="overview" class="doc-section mb-14">
<h2 class="text-2xl font-bold text-zinc-900 dark:text-white mb-4">What it shows</h2>
<ul class="list-disc pl-5 text-zinc-600 dark:text-zinc-400 space-y-2">
<li><strong class="text-zinc-800 dark:text-zinc-200">Live sessions</strong> — <code>pg_stat_activity</code> (duration, state, truncated query, waits)</li>
<li><strong class="text-zinc-800 dark:text-zinc-200">Top statements</strong> — <code>pg_stat_statements</code> when the extension is installed</li>
<li><strong class="text-zinc-800 dark:text-zinc-200">Locks / blocking</strong> — blocked vs blocking backends</li>
<li><strong class="text-zinc-800 dark:text-zinc-200">Tables & indexes</strong> — high sequential scans; unused index candidates</li>
<li><strong class="text-zinc-800 dark:text-zinc-200">Table analysis</strong> — press <kbd class="px-1 bg-zinc-200 dark:bg-zinc-700 rounded">t</kbd> for per-table heap/cache stats and index list</li>
<li><strong class="text-zinc-800 dark:text-zinc-200">Footer</strong> — PostgreSQL-side buffer cache hit ratio, sessions vs <code>max_connections</code>, key memory settings, checkpoint hint (PG17 uses <code>pg_stat_checkpointer</code>)</li>
</ul>
</section>
<section id="layout" class="doc-section mb-14">
<h2 class="text-2xl font-bold text-zinc-900 dark:text-white mb-4">TUI layout (ASCII)</h2>
<pre class="bg-zinc-900 text-zinc-100 p-4 rounded-xl ascii border border-zinc-700"><code>┌─ live queries (pg_stat_activity) ─┐┌─ top queries ─────────────────────┐
│ pid user duration state query … ││ calls / total / mean / query … │
└─────────────────────────────────────┘└───────────────────────────────────┘
┌─ locks / blocking ──────────────────────────────────────────────────────────┐
│ blocked_pid ← blocking_pid wait … │
└─────────────────────────────────────────────────────────────────────────────┘
┌─ tables / indexes (summary strip) ────────────────────────────────────────┘
footer: shortcuts + pg server metrics (cache hit %, conns, shared_buffers, …)</code></pre>
</section>
<section id="install" class="doc-section mb-14">
<h2 class="text-2xl font-bold text-zinc-900 dark:text-white mb-4">Requirements & install</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-3">Node.js 18+ and PostgreSQL 10+ — see <a href="#compatibility" class="text-emerald-700 dark:text-emerald-400 hover:underline">PostgreSQL version compatibility</a> for per-version SQL behavior.</p>
<p class="text-zinc-600 dark:text-zinc-400 mb-2">Install globally so <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">pgpulse</code> is on your <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">PATH</code>:</p>
<pre class="bg-zinc-900 p-5 rounded-xl text-sm overflow-x-auto mb-4"><code class="language-bash">npm i -g pgpulse
pgpulse</code></pre>
<p class="text-zinc-500 dark:text-zinc-500 text-sm mb-4">From a clone: <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">npm install</code> then <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">npm start</code> (see README).</p>
<p class="text-zinc-600 dark:text-zinc-400 text-sm">Optional: <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">better-sqlite3</code> for <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">--sqlite</code> snapshot history (native build).</p>
</section>
<section id="compatibility" class="doc-section mb-14">
<h2 class="text-2xl font-bold text-zinc-900 dark:text-white mb-4">PostgreSQL version compatibility</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">Supported: <strong class="text-zinc-800 dark:text-zinc-200">PostgreSQL 10+</strong> (earlier releases untested). On connect, pgpulse reads <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">server_version_num</code> and chooses SQL accordingly.</p>
<div class="overflow-x-auto mb-4">
<table class="w-full text-sm">
<thead><tr><th>Area</th><th>Behavior</th></tr></thead>
<tbody class="text-zinc-600 dark:text-zinc-400">
<tr><td><code class="whitespace-nowrap">pg_stat_activity.backend_type</code></td><td>Included on <strong>PG 10+</strong> only; omitted on older servers.</td></tr>
<tr><td><code class="whitespace-nowrap">pg_stat_statements</code></td><td><strong>PG 13+</strong>: <code>total_exec_time</code> / <code>mean_exec_time</code>. <strong>PG 12 and older</strong>: <code>total_time</code> / <code>mean_time</code> (shown in the UI with the same labels). Extension must be installed.</td></tr>
<tr><td>Footer checkpoint hint</td><td><strong>PG 16 and older</strong>: <code>pg_stat_bgwriter</code>. <strong>PG 17+</strong>: <code>pg_stat_checkpointer</code>.</td></tr>
</tbody>
</table>
</div>
<pre class="bg-zinc-900 p-5 rounded-xl text-sm overflow-x-auto"><code class="language-sql">CREATE EXTENSION IF NOT EXISTS pg_stat_statements;</code></pre>
<p class="text-zinc-500 dark:text-zinc-500 text-sm mt-2">Run on the target database for the Top queries panel.</p>
</section>
<section id="connection" class="doc-section mb-14">
<h2 class="text-2xl font-bold text-zinc-900 dark:text-white mb-4">Connection</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-3">Precedence: <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">DATABASE_URL</code> → <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">--connection-string</code> → <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">PG*</code> env + CLI flags.</p>
<pre class="bg-zinc-900 p-5 rounded-xl text-sm overflow-x-auto"><code class="language-bash">DATABASE_URL=postgres://user:pass@localhost:5432/db pgpulse
pgpulse --host localhost --port 5432 --user postgres --database app</code></pre>
</section>
<section class="doc-section mb-14">
<h2 class="text-2xl font-bold text-zinc-900 dark:text-white mb-4">CLI options</h2>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead><tr><th>Flag</th><th>Description</th></tr></thead>
<tbody class="text-zinc-600 dark:text-zinc-400">
<tr><td><code>--interval <ms></code></td><td>Poll interval (default 2000)</td></tr>
<tr><td><code>--long-threshold <s></code></td><td>Highlight “long” queries (default 5)</td></tr>
<tr><td><code>--export <file></code></td><td>Default path for JSON export (<kbd>e</kbd>)</td></tr>
<tr><td><code>--sqlite <file></code></td><td>Optional local snapshot DB</td></tr>
<tr><td><code>-h</code>, <code>--help</code></td><td>Help</td></tr>
</tbody>
</table>
</div>
</section>
<section id="keyboard" class="doc-section mb-14">
<h2 class="text-2xl font-bold text-zinc-900 dark:text-white mb-4">Keyboard</h2>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead><tr><th>Key</th><th>Action</th></tr></thead>
<tbody class="text-zinc-600 dark:text-zinc-400">
<tr><td><kbd class="font-mono">q</kbd></td><td>Quit</td></tr>
<tr><td><kbd class="font-mono">Tab</kbd></td><td>Focus panels</td></tr>
<tr><td><kbd class="font-mono">r</kbd></td><td>Reset delta / 10s window</td></tr>
<tr><td><kbd class="font-mono">s</kbd></td><td>Cycle sort (statements)</td></tr>
<tr><td><kbd class="font-mono">d</kbd></td><td>Cumulative vs Δ10s window</td></tr>
<tr><td><kbd class="font-mono">/</kbd></td><td>Filter (Enter apply, Esc cancel)</td></tr>
<tr><td><kbd class="font-mono">l</kbd> / <kbd class="font-mono">i</kbd></td><td>Locked backends / idle in transaction only</td></tr>
<tr><td><kbd class="font-mono">t</kbd></td><td>Table analysis browser + detail</td></tr>
<tr><td><kbd class="font-mono">x</kbd> / <kbd class="font-mono">k</kbd></td><td>EXPLAIN / terminate backend (with confirm)</td></tr>
<tr><td><kbd class="font-mono">e</kbd></td><td>Export JSON</td></tr>
</tbody>
</table>
</div>
</section>
<section id="metrics" class="doc-section mb-14">
<h2 class="text-2xl font-bold text-zinc-900 dark:text-white mb-4">Footer: PostgreSQL metrics</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-3">
The second footer line is built from <strong>SQL-visible</strong> stats: buffer cache hit ratio from <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">pg_stat_database</code>, session count vs <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">max_connections</code>, <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">shared_buffers</code> / <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">work_mem</code> from <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">pg_settings</code>, deadlocks, temp files, and checkpoint pressure.
<strong class="text-zinc-800 dark:text-zinc-200">This is not the host OS CPU% or total RAM</strong> of the database machine — only what PostgreSQL exposes through catalogs.
</p>
<p class="text-zinc-500 dark:text-zinc-500 text-sm">PostgreSQL 17+ uses <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">pg_stat_checkpointer</code> instead of <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">pg_stat_bgwriter</code>.</p>
</section>
<section id="architecture" class="doc-section mb-14">
<h2 class="text-2xl font-bold text-zinc-900 dark:text-white mb-4">Architecture</h2>
<pre class="bg-zinc-100 dark:bg-zinc-900/80 border border-zinc-200 dark:border-zinc-700 rounded-xl p-4 text-sm text-zinc-700 dark:text-zinc-300 overflow-x-auto font-mono">src/
collectors/ activity, statements, locks, tables, indexes, pgServerStats
engine/ snapshot.js, delta.js (statement deltas + 10s ring)
ui/ layout.js, panels/
utils/ sql.js, format.js, version.js, pgServerMetrics.js
index.js CLI entry</pre>
<p class="mt-4 text-zinc-600 dark:text-zinc-400 text-sm">See the repository <code class="bg-zinc-200 dark:bg-zinc-700 px-1 rounded">README.md</code> for development (Vitest) and security notes.</p>
</section>
<footer class="pt-10 border-t border-zinc-200 dark:border-zinc-800 text-sm text-zinc-500 dark:text-zinc-500">
<p>MIT</p>
</footer>
</div>
<script>
hljs.highlightAll();
(function(){
var themeKey = 'pgpulse-docs-theme';
var stored = localStorage.getItem(themeKey);
var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (stored === 'dark' || (!stored && prefersDark)) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
document.getElementById('theme-toggle').addEventListener('click', function(){
var isDark = document.documentElement.classList.toggle('dark');
localStorage.setItem(themeKey, isDark ? 'dark' : 'light');
});
})();
</script>
</body>
</html>