PostgreSQLIX · Locks, Blocking and DeadlocksLocks
Cancel or terminate
What you'll learn
- Choose between cancel and terminate from the target session state
- Predict what each verb releases and what it leaves behind
- Decide whether killing a given session is safe before doing it
- Apply timeouts so that manual intervention is rarely needed
Prerequisites
Verified against PostgreSQL 18.x · PostgreSQL (comparison targets) 17.11, 16.15 · PostgreSQL (support calendar) 18, 17, 16, 15, 14 supported · pgBackRest 2.59.1 · PgBouncer 1.25.2 · Patroni 4.1.5 · Ubuntu (host baseline) 26.04 LTS · 2026-08-27
Two functions, and the choice between them is made under time pressure with incomplete information. The commonly repeated summary — “cancel stops the query, terminate kills the connection” — is true and does not tell you what you need to know, which is what each one releases.
That was measured rather than assumed, and one of the results contradicts the folklore.
Case 1: the session is running a statement
pg_cancel_backend(pid) sends a signal asking the backend to stop what
it is doing. The statement ends with an error.
$ psql -U postgres -At -c "SELECT pg_cancel_backend(1876)"-- client output of the cancelled session:
BEGIN
UPDATE 1
ERROR: canceling statement due to user request
-- its state five seconds later:
pid | state | backend_xid | xmin_age
------+-------------------------------+-------------+----------
1876 | idle in transaction (aborted) | |
-- locks it still holds on the table:
(0 rows)Read the last two blocks carefully, because this is the part that is usually stated wrongly.
The cancel aborted the transaction. backend_xid is NULL,
backend_xmin is NULL, and no relation locks remain. The session is
in idle in transaction (aborted) — it exists, it holds a connection
slot, and it holds nothing else.
Verified again with a session holding a REPEATABLE READ snapshot:
$ psql -U postgres -c "SELECT pid, state, age(backend_xmin) AS xmin_age FROM pg_stat_activity WHERE pid=1913"before cancel: 1913 | active | xmin_age 0
after cancel: 1913 | idle in transaction (aborted) | xmin_age NULLSo cancelling an active session does release its hold on the vacuum horizon. If the goal is to free locks or unblock vacuum, and the session is running something, cancel is sufficient.
Case 2: the session is idle in transaction
This is the case that matters, because it is the one causing the damage in lesson VII-05.
$ psql -U postgres -At -c "SELECT pg_cancel_backend(1950)"before: 1950 | idle in transaction | xmin_age 0
pg_cancel_backend returns: t
after cancel: 1950 | idle in transaction | xmin_age 0The cancel did nothing. It returned true, because the signal was
delivered successfully — but there was no statement to cancel, so the
session is unchanged: same state, same snapshot, same locks.
pg_terminate_backend is the only verb that works here.
$ psql -U postgres -At -c "SELECT pg_terminate_backend(1950)"-- returns: t
-- the client:
FATAL: terminating connection due to administrator command
server closed the connection unexpectedly
-- afterwards:
SELECT count(*) FROM pg_stat_activity WHERE pid=1950; --> 0Deciding whether it is safe
Before either verb, three questions.
What is it? Read application_name, usename, client_addr and
query. A session from the analytics host running a long aggregate is a
different decision from one belonging to the payment service.
SELECT pid, usename, application_name, client_addr, state,
now() - xact_start AS xact_age,
now() - state_change AS in_state_for,
backend_type,
left(query, 100) AS query
FROM pg_stat_activity
WHERE pid = 12345;
What will be lost? An active session in a transaction loses
everything since BEGIN. If it is a four-hour data migration, that is
four hours. If it is idle in transaction, it is doing nothing and
losing nothing matters less.
Will it come back? Many clients reconnect and retry immediately. If the session is a runaway query from a service that will re-issue it, you have bought seconds. Fix the source, or the loop continues.
Bulk termination
Occasionally you need to clear a class of sessions rather than one.
-- everything idle in transaction for over ten minutes
SELECT pid, application_name, now() - state_change AS idle_for,
pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state IN ('idle in transaction', 'idle in transaction (aborted)')
AND now() - state_change > interval '10 minutes'
AND backend_type = 'client backend'
AND pid <> pg_backend_pid();
Three guards in that query are not optional: backend_type excludes
background processes, pid <> pg_backend_pid() stops it terminating
itself partway through, and the state filter restricts it to sessions
that are doing nothing.
Run the SELECT without the pg_terminate_backend call first.
Look at what it returns. Then add the call.
What to take from this
- Check
statebefore choosing the verb. It determines which one works. - Cancel on an
activesession aborts the transaction and releases its locks and snapshot. - Cancel on an
idle in transactionsession returnstrueand does nothing. Terminate is the only option there. - Never
kill -9a backend; the postmaster restarts the cluster. - Anti-wraparound vacuum, concurrent index builds and backups deserve thought before termination.
- Doing this by hand regularly means a timeout is missing.
Cross-course references
- Linux for Production Sysadmins — Part VI (Processes) covers the
signal semantics underneath
pg_cancel_backendandpg_terminate_backend, and whykill -9is a different act entirely. - Observability for Production Sysadmins — Part CIX (Incident investigation workflows) covers recording what was cancelled and why, which is the part most often skipped under pressure.
Quiz
Knowledge check · 6 questions
Q1. A session is idle in transaction and has been holding the vacuum horizon for two hours. An operator runs pg_cancel_backend against it and the function returns true. What has changed?
Q2. Why should kill -9 never be used against a PostgreSQL backend process?
Q3. An operator needs to cancel and terminate other users' sessions during incidents but should not be a superuser. What is the correct grant?
Q4. Which sessions deserve deliberate thought before being terminated? Select all that apply.
Q5. Cancelling an active session that holds a REPEATABLE READ snapshot releases that snapshot, so vacuum is no longer held back by it.
Q6. You have identified the root of a blocking chain. Describe how you would decide what to do with it.
Passing score: 75%. Answers are checked in this browser.