transaction_timeout

transaction_timeout (NEW in PostgreSQL 17) sets the maximum total duration of any single transaction in a session — measured from the start of the transaction…

MEDIUM RISK
Safe first move

Because it is FATAL, set it above your real p99 transaction time with headroom and ensure the pool reconnects.

Tier 1 — Lab-Proven — Measured behavior and safety boundaries are available below.

Measured Effect

summary

MEASURED the total-transaction cap firing and the session being TERMINATED (FATAL). A transaction ran several short statements (pg_sleep(0.4)) separated by short client gaps — each statement+gap well under the cap, but the running TOTAL crossing it. Wall-clock was measured around the call so the give-up point is visible.

observations

transaction timeoutscenariooutcomeelapsed snote
2smulti-statement txn, total > 2sFATAL — connection terminated2.4smoking gun: terminating connection due to transaction timeout mid-transaction; the end-of-txn marker never prints
10ssame txn, total ~2sCOMMIT — marker prints3.2under a generous cap the whole transaction completes normally
1sBEGIN; pg_sleep(3)FATAL — session terminated1.xno follow-up statement in the same session is possible — the connection is GONE

session survives

It does NOT — unlike statement_timeout (#26) and lock_timeout (#27) which raise a non-fatal cancel and keep the connection, transaction_timeout raises a FATAL: the backend is terminated, the connection is closed, and the transaction rolls back. The server log shows FATAL: terminating connection due to transaction timeout (captured for every firing).

reading

It is a hard cap on a whole transaction's wall-clock duration, not a performance dial. Once the running total of a transaction passes the limit, the server terminates the connection (FATAL) and rolls the transaction back; the elapsed at termination ≈ the cap (here ~2.4s for a 2s setting, the small extra being client/exec overhead). It catches what statement_timeout and idle_in_transaction_session_timeout miss because it sums BOTH active and idle time inside the transaction.

honesty note

transaction_timeout is a server-side timer; the chosen seconds are lab values, the mechanism (terminate-on-total-exceeded, FATAL, connection closed, txn rolled back) is deterministic and box-independent. Measured elapsed includes a few hundred ms of docker-exec/client overhead, which is why a 2s setting shows ~2.4s. It does not apply to prepared (two-phase) transactions.

Reproduced Danger

Sizing / Safe Range

Workload Guidance

Unlock Incident Pro

The free sections tell you what to do. Pro is the measured proof — the exact failure signature, safe-ceiling formula, and per-workload defaults, each captured on real PG 14–18 runs.

  • Reproduced Danger
  • Sizing / Safe Range
  • Workload Guidance
Unlock Incident Pro

transaction_timeout (NEW in PostgreSQL 17) sets the maximum total duration of any single transaction in a session — measured from the start of the transaction (or of a single-statement transaction) until it commits or rolls back. It bounds the WHOLE transaction: time spent running statements AND time spent idle between statements inside the transaction. If the limit is exceeded, the backend is terminated with a FATAL error — FATAL: terminating connection due to transaction timeout — which CLOSES the connection and rolls back the transaction (it is the heavier hammer; statement_timeout and lock_timeout only cancel a statement and keep the session alive). The default is 0, meaning unlimited. It fills the gap left by its siblings: statement_timeout caps a SINGLE statement's run time (so a transaction made of many short statements escapes it), and idle_in_transaction_session_timeout caps only IDLE time inside a transaction (so a continuously-active long transaction escapes it) — transaction_timeout catches both because it caps the total. It does NOT apply to prepared (two-phase) transactions. context=user, so it can be set per session, role, database, or cluster-wide and takes effect immediately. Because it does not exist before PG 17, any safety relying on it is absent on PG 14-16.

name

transaction_timeout

setting default

0 (disabled — unlimited)

unit

ms

vartype

integer

category

Client Connection Defaults / Statement Behavior

context

user

min val

0

max val

2147483647

enumvals

(n/a — integer ms)

boot val

0

reset val

0

pending restart

false

short desc

Sets the maximum allowed duration of any transaction within a session (not a prepared transaction).

ground truth note

Captured literally from pg_settings on PG 14-18 (-18) on 2026-06-20. The parameter is ABSENT on PG 14, 15, 16 (introduced_in=pg17) and PRESENT on PG 17 and 18 with setting=0, boot_val=0, reset_val=0 (default_changed=false between 17 and 18). context=user (session-changeable, no reload/restart). vartype=integer, unit=ms, min_val=0 (0 disables), max_val=2147483647. category='Client Connection Defaults / Statement Behavior', short_desc='Sets the maximum allowed duration of any transaction within a session (not a prepared transaction).'

Common Pitfalls

  • Assuming transaction_timeout exists on PG 14-16 — it is NEW in PG 17; setting it on older servers raises unrecognized configuration parameter and the intended protection is absent.
  • Setting it below the duration of a legitimate batch/ETL transaction — it will terminate the connection and roll back real work.
  • Forgetting it is FATAL (session terminated), not a per-statement cancel like statement_timeout/lock_timeout — clients are disconnected and must reconnect.
  • Expecting statement_timeout or idle_in_transaction_session_timeout alone to catch a long multi-statement transaction — they don't; only transaction_timeout caps the total.
  • Setting it tighter than statement_timeout by accident, so transaction_timeout fires first (FATAL) instead of statement_timeout (non-fatal) for a long single statement.
  • Expecting it to bound prepared (two-phase) transactions — it explicitly does not.

Evidence and References

  • PostgreSQL 17 docs: Runtime Config — Client Connection Defaults / Statement Behavior (transaction_timeout — terminate any session whose transaction exceeds the limit; 0 disables; does not apply to prepared transactions; NEW in PG 17).
  • PostgreSQL 17 release notes: addition of transaction_timeout (bounds total transaction duration, complementing statement_timeout and idle_in_transaction_session_timeout).
  • PostgreSQL docs: statement_timeout, lock_timeout, idle_in_transaction_session_timeout, idle_session_timeout (the rest of the timeout family — per-statement run time, lock wait, in-transaction idle, and out-of-transaction idle respectively).
  • PostgreSQL docs: pg_stat_activity and server log — terminating connection due to transaction timeout is the FATAL emitted when the cap is hit.
  • Lab capture 2026-06-20: — a multi-statement transaction whose total crossed a 2s cap was terminated (FATAL) at ~2.4s while the same txn under 10s committed ; the version cliff ( unrecognized configuration parameter vs accepted) and the coverage gap (same ~2s txn escapes statement_timeout=1s and idle_in_transaction_session_timeout=1s but is terminated by transaction_timeout=1.5s) ; boundary min=0 disables, range error on -1, context=user, identical /; pg_settings ground truth PG 14-18 (-18) confirming absent 14-16, present 17-18.

Technical Reference

Category
Client Connection Defaults / Statement Behavior
Type
integer (ms (milliseconds; 0 disables))
Blast radius
session
Severity
Moderate
Context
user
Versions
17, 18 (introduced in 17; absent on 14, 15, 16)
Reviewed
2026-06-20

Severity scale: 1 Minimal · 2 Low · 3 Moderate · 4 High · 5 Critical.

Keep going

Related & next steps