KDB knowledge

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).

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 functionWhat it gives
pg_stat_activityone 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_statementscalls, 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.

ColumnMeaning
pidthe 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, usenamethe database and the user (for a login without KDB users: the login name)
application_namethe Postgres application_name, or the HTTP User-Agent
client_addrthe client's address (client_port and client_hostname are NULL)
backend_start, xact_start, query_start, state_changewhen the connection opened, its transaction began, its statement began, its state last changed
stateactive (a statement runs), idle, idle in transaction, idle in transaction (aborted)
wait_event_type, wait_eventwhat 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
querythe running statement, or the last one when idle (up to 1024 characters)
backend_typeclient backend

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.

Example: No backend has this pid

SELECT pg_cancel_backend(12345) AS cancelled, pg_terminate_backend(12345) AS terminated;

Result:

cancelled (BOOL)terminated (BOOL)
falsefalse

(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.

ColumnMeaning
query_idthe statement's number: new for every statement, never used twice while the server runs
worker_idthe pid of its connection or request, as pg_stat_activity shows it
worker_poolpgwire (the Postgres port) or http
username, querywho runs it, and its text (up to 1024 characters)
query_start, state_changewhen it started
stateactive, or cancelled while a stopped statement winds down
is_walalways false: KDB applies a commit before it returns, so it has no WAL apply jobs

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.

SettingWhat it does
idle_session_timeoutA 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_intervalWhile 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, _countTCP 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.

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.

ColumnMeaning
queryidthe shape's number: the same for every user
querythe shape, as its user first sent it (up to 1024 characters)
calls, rowshow 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_timemilliseconds
usename, datnameKDB'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

Example: Without a server (kdb sql) nothing is counted

SELECT count(*) AS shapes FROM pg_stat_statements;

Result:

shapes (LONG)
0

(1 row)