pg_stat_activity and pg_stat_statements
See who is connected and what runs now (pg_stat_activity, query_activity(), GET /api/sessions), stop a statement or close a connection (pg_cancel_backend, pg_terminate_backend, CANCEL QUERY, DELETE /api/sessions/<pid>), close idle and vanished clients (idle_session_timeout, tcp_keepalives_*), and see the totals of every statement shape (pg_stat_statements).
- Category: Postgres protocol
- Keywords: query_activity, CANCEL QUERY, cancel_query, pg_sleep, idle_session_timeout, client_connection_check_interval, tcp_keepalives_idle, tcp_keepalives_interval, tcp_keepalives_count, keepalive, 57P01, 57P05, 57014, /api/sessions, CLIENT LIST, CLIENT KILL, kill session, pg_stat_activity, pg_stat_statements, pg_stat_statements_reset, pg_cancel_backend, pg_terminate_backend, pg_backend_pid, running queries, running statements, idle in transaction, wait_event, wait_event_type, kill query, kill connection, terminate connection, statement statistics, query statistics, slow statements, query totals, --stat-statements, KDB_STAT_STATEMENTS, queryid, mean_exec_time, total_exec_time, 42501
- See also: Protection - timeouts, limits, safe updates, read-only (cancel), EXPLAIN (explain-analyze), Health, info, check and backup over HTTP (slow-queries), Monitor panel (memory, processor, caches), PostgreSQL protocol (port 8832), Sessions and cursors over HTTP
These are the views and functions PostgreSQL tools use to watch a server. They work the same over the Postgres port and over HTTP (POST /exec), and they show both kinds of client.
| View or function | What it gives |
|---|---|
pg_stat_activity | one row per connection and HTTP session, and per HTTP request while it runs |
pg_cancel_backend(pid) | stops the statement that backend runs |
pg_terminate_backend(pid) | also closes its connection or HTTP session |
pg_backend_pid() | the pid of your own connection (or HTTP request) |
query_activity(), CANCEL QUERY id, cancel_query(id) | QuestDB's names: the running statements, and stopping one of them |
pg_sleep(seconds) | waits; a cancel or a timeout stops it at once |
GET /api/sessions, DELETE /api/sessions/<pid> | the same list as JSON, and closing one (Redis' CLIENT LIST and CLIENT KILL) |
pg_stat_statements | calls, times and rows of every statement shape since the server started |
pg_stat_statements_reset() | starts the totals again |
pg_locks, pg_blocking_pids(pid) | the table locks held and waited for, with the same pids; who a waiting pid waits for (LOCK TABLE and pg_locks) |
kdb sql and kdb shell have no server around them: there the views are empty and the functions answer false.
pg_stat_activity
Syntax:
SELECT pid, usename, application_name, client_addr, state, wait_event_type, wait_event, query_start, query
FROM pg_stat_activity
[WHERE pid <> pg_backend_pid()]
The columns are PostgreSQL 14's, in its order.
| Column | Meaning |
|---|---|
pid | the backend's number. A Postgres connection's is its pg_backend_pid() (the key of its cancel requests). An HTTP request without a session gets one of its own, from 1073741824 up |
datname, usename | the database and the user (for a login without KDB users: the login name) |
application_name | the Postgres application_name, or the HTTP User-Agent |
client_addr | the client's address (client_port and client_hostname are NULL) |
backend_start, xact_start, query_start, state_change | when the connection opened, its transaction began, its statement began, its state last changed |
state | active (a statement runs), idle, idle in transaction, idle in transaction (aborted) |
wait_event_type, wait_event | what an active statement waits for: Lock / relation (another statement's tables), Lock / tuple (a row another transaction locked), Lock / advisory; an idle Postgres connection shows Client / ClientRead. NULL when not waiting |
query | the running statement, or the last one when idle (up to 1024 characters) |
backend_type | client backend |
- Rights: an admin (and the
--token) sees every row in full. Other users see every row, but only their own rows' details: for another user's backendqueryis<insufficient privilege>and the times, state, address and wait columns are NULL, as in PostgreSQL. - Notes:
- An HTTP session (POST /session) is one backend from its start to its close. An HTTP request without a session is listed only while it runs.
datidandusesysidare the oidspg_databaseandpg_rolesshow; usedatnameandusename.- The same list, as JSON, is in
GET /api/stats(running), which the console's Monitor panel shows.
Example: Without a server (kdb sql) nobody else is connected
SELECT count(*) AS others FROM pg_stat_activity WHERE pid <> pg_backend_pid();
Result:
| others (LONG) |
|---|
| 0 |
(1 row)
pg_cancel_backend and pg_terminate_backend
Syntax:
pg_cancel_backend(pid)
pg_terminate_backend(pid [, timeout])
pg_cancel_backend stops the statement the backend runs now; the connection stays. pg_terminate_backend also closes it: a Postgres connection gets FATAL: terminating connection due to administrator command (SQLSTATE 57P01) and is closed at once (an open transaction is rolled back), an HTTP session is closed as DELETE /session closes it.
- Returns:
truewhen a backend has that pid (also when it is idle and there was nothing to stop),falsewhen none has, NULL for a NULL pid. - Rights: an admin may stop every backend; other users only their own. Otherwise:
must be a member of the role whose query is being canceled or member of pg_signal_backend(SQLSTATE42501, HTTP 403). - Notes:
- The stopped statement gets
canceling statement due to user request(SQLSTATE57014, HTTP 408), as with a cancel request. It stops within milliseconds: the scans look at the stop flag once per batch of rows. timeoutis accepted and not waited for.- Over TLS a terminated idle connection is closed without the
57P01message (two threads must not write one TLS session).
- The stopped statement gets
Example: No backend has this pid
SELECT pg_cancel_backend(12345) AS cancelled, pg_terminate_backend(12345) AS terminated;
Result:
| cancelled (BOOL) | terminated (BOOL) |
|---|---|
| false | false |
(1 row)
Syntax:
-- Stop everything user etl has run for more than 5 minutes
SELECT pid, pg_cancel_backend(pid) FROM pg_stat_activity
WHERE usename = 'etl' AND state = 'active' AND query_start < now() - INTERVAL '5 minutes';
-- Close the connections left idle in a transaction
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle in transaction';
query_activity, CANCEL QUERY and cancel_query
Syntax:
SELECT query_id, worker_id, worker_pool, username, query_start, state_change, state, is_wal, query
FROM query_activity()
CANCEL QUERY query_id
cancel_query(query_id)
QuestDB's way to see and stop running statements. query_activity() has one row per statement running now, over HTTP and the Postgres port, itself included.
| Column | Meaning |
|---|---|
query_id | the statement's number: new for every statement, never used twice while the server runs |
worker_id | the pid of its connection or request, as pg_stat_activity shows it |
worker_pool | pgwire (the Postgres port) or http |
username, query | who runs it, and its text (up to 1024 characters) |
query_start, state_change | when it started |
state | active, or cancelled while a stopped statement winds down |
is_wal | always false: KDB applies a commit before it returns, so it has no WAL apply jobs |
- Returns:
CANCEL QUERYanswerstrueor fails withquery to cancel not found in registry [id=...](SQLSTATE42704) when no statement runs with that id.cancel_query(id)answerstruewhen it stopped one,falsewhen none runs with that id, NULL for NULL. - Rights: an admin sees and stops every statement; other users see only their own and may stop only their own (otherwise SQLSTATE
42501, HTTP 403). - Notes: the stopped statement ends with
canceling statement due to user request(57014); its connection stays.pg_stat_activity's ownquery_idcolumn is PostgreSQL's (a number for the statement's shape) and stays NULL.
Example: Without a server (kdb sql) nothing else runs
SELECT count(*) AS running FROM query_activity();
Result:
| running (LONG) |
|---|
| 0 |
(1 row)
Example: No statement runs with this id
SELECT cancel_query(123456) AS cancelled;
Result:
| cancelled (BOOL) |
|---|
| false |
(1 row)
Syntax:
-- Stop every statement that has run for more than a minute
SELECT query_id, cancel_query(query_id) FROM query_activity()
WHERE query_start < now() - INTERVAL '1 minute' AND query NOT LIKE '%query_activity%';
pg_sleep
Syntax:
pg_sleep(seconds)
Waits that many seconds (a fraction is fine), then returns an empty value, as PostgreSQL's void. A cancel, pg_terminate_backend, CANCEL QUERY or the statement timeout stops it within about 10 ms. NULL or 0 returns at once.
Example: A short wait
SELECT pg_sleep(0.01) IS NOT NULL AS waited;
Result:
| waited (BOOL) |
|---|
| true |
(1 row)
Idle and vanished clients
Syntax:
SET idle_session_timeout = '10min' -- 0 = off (the default)
SET client_connection_check_interval = '5s' -- 0 = off (the default)
SET tcp_keepalives_idle = 60 -- seconds; 0 = the server's
SET tcp_keepalives_interval = 10
SET tcp_keepalives_count = 5
kdb serve --tcp-keepalives-idle 60 --tcp-keepalives-interval 10 --tcp-keepalives-count 5
PostgreSQL 14's settings, for one Postgres connection.
| Setting | What it does |
|---|---|
idle_session_timeout | A connection that waits longer than this for its next query, outside a transaction, is closed with FATAL: terminating connection due to idle-session timeout (SQLSTATE 57P05). An open BEGIN counts under idle_in_transaction_session_timeout instead (idle transactions). Milliseconds as a number, or '2s', '5min'. |
client_connection_check_interval | While a statement runs, the server looks this often whether the client is still there, and stops the statement when it went away, so a long query does not run on for nobody. At most about 5 times a second. |
tcp_keepalives_idle, _interval, _count | TCP keepalive of this connection: seconds without traffic before the first probe, seconds between probes, probes before the connection is given up. SHOW reads the value back from the socket. 0 = the server's (the --tcp-keepalives-* flags, KDB_TCP_KEEPALIVES_*, tcp_keepalives_* in kdb.conf), else the system's. |
- Notes:
- Keepalive is on for every HTTP and Postgres connection, as in PostgreSQL, so a client that vanished without closing (a laptop shut, a NAT that forgot it) is noticed and its locks are given back. The server flags set the timings for both ports.
- A wrong value is refused with SQLSTATE
22023and changes nothing. Idle and interval go up to 32767 s, the count up to 127. - The connections the server closed are in the activity log (
GET /api/activity, kindsession). - These are settings of the Postgres port.
SHOW idle_session_timeoutthere answers0until it is set, then as PostgreSQL writes it (2s,1min).
GET /api/sessions and DELETE /api/sessions/<pid>
Syntax:
GET /api/sessions
GET /api/activity?sessions=1
DELETE /api/sessions/<pid>
GET /api/sessions is pg_stat_activity as JSON: every Postgres connection, HTTP session and running HTTP request, with pid, protocol (pg or http), user, database, application_name, client_addr, state, wait_event_type, wait_event, query, the times in Unix milliseconds (backend_start_ms, xact_start_ms, query_start_ms, state_change_ms) and query_id (the running statement's, for CANCEL QUERY; null when idle). GET /api/activity?sessions=1 adds the same list to the activity log's answer, so one poll shows what happened and what runs now.
DELETE /api/sessions/<pid> is pg_terminate_backend(<pid>): the statement stops, an open transaction rolls back, a Postgres client gets 57P01. It answers {"pid":..,"terminated":true}, 404 when no session has that pid, 403 when it is another user's and you are not an admin, 400 for a pid that is not a number.
Syntax:
curl -H "Authorization: Bearer $KDB_TOKEN" http://localhost:9900/api/sessions
curl -X DELETE -H "Authorization: Bearer $KDB_TOKEN" http://localhost:9900/api/sessions/42
pg_stat_statements
Syntax:
SELECT query, calls, total_exec_time, mean_exec_time, max_exec_time, rows
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10
pg_stat_statements_reset([userid, dbid, queryid])
kdb serve --stat-statements on|off (env KDB_STAT_STATEMENTS; default on)
The totals of every statement shape the server ran: the statement with each literal replaced by $1, $2, ..., so WHERE id = 5 and WHERE id = 7 count together. Spacing, comments and the case of words do not make a new shape. No extension needs to be created.
| Column | Meaning |
|---|---|
queryid | the shape's number: the same for every user |
query | the shape, as its user first sent it (up to 1024 characters) |
calls, rows | how often it ran, and the rows it returned or changed in all |
total_exec_time, min_exec_time, max_exec_time, mean_exec_time, stddev_exec_time | milliseconds |
usename, datname | KDB's own columns: the user and the database (userid and dbid are the same oid for everyone) |
plans, *_plan_time, shared_blks_*, local_blks_*, temp_blks_*, blk_*_time, wal_* | PostgreSQL's columns KDB does not count: 0 |
- Returns: one row per user, database and shape.
- Notes:
- Only statements that ended without an error count, as in PostgreSQL. The console's own polling does not count.
- One HTTP request is one statement, even when it holds several.
- At most 5000 shapes are kept (PostgreSQL's
pg_stat_statements.max). When a new one comes, the 5% least-called make room. The totals are kept in memory: a restart starts them again. Statements a procedure or trigger runs count in theCALLor write that ran them (pg_stat_statements.trackistop). - It costs a few hundred nanoseconds per statement: the first 16 KB of the text are read once for the shape.
--stat-statements offswitches it off; the view is then empty. - Other users' shapes show
<insufficient privilege>instead of the text to a user who is not an admin. pg_stat_statements_reset()needs an admin. With aqueryid(the third argument,0= all) only that shape starts again.
Example: Without a server (kdb sql) nothing is counted
SELECT count(*) AS shapes FROM pg_stat_statements;
Result:
| shapes (LONG) |
|---|
| 0 |
(1 row)