Timeouts
Which Postgres timeouts CapyDB sets for you, which ones it deliberately leaves alone, and how to set the rest so they survive the pooled endpoint.
Postgres has four timeouts that matter to an application. CapyDB sets exactly one of them for you, because it is the only one where a sensible default exists that cannot break correct code.
What your cell is set to
| Setting | CapyDB default | Why |
|---|---|---|
idle_in_transaction_session_timeout | 10 minutes | Kills a session that opened a transaction and then stopped doing anything. This is the single most damaging state an application can leave a database in - see Long-running transactions - and no correct application relies on idling inside a transaction for ten minutes. |
statement_timeout | not set | A long analytical query and a runaway query look identical from here. Only you know which is which. |
lock_timeout | not set | A migration waiting behind a long-running read is correct behaviour. Capping it would break migrations that work today. |
transaction_timeout | not set | Kills active transactions too, so it would cut legitimate long imports and bulk jobs. |
The default only kills sessions that are idle inside a transaction. A query that runs for an hour is never affected by it; a session that ran BEGIN, did one SELECT, and then went to lunch is.
Setting the rest
Set them per role, not per session. ALTER ROLE writes the setting into the role itself, so it applies to every connection that role opens - including connections borrowed through the pooled endpoint, where a session-level SET would land on whichever server connection you happened to get.
-- Applies to every connection this role opens, on both endpoints.
ALTER ROLE your_role SET statement_timeout = '30s';
ALTER ROLE your_role SET lock_timeout = '10s';Verify it took:
SELECT rolname, rolconfig FROM pg_roles WHERE rolname = 'your_role';To scope a timeout to one piece of work instead, use SET LOCAL inside a transaction - it is reverted at commit and is safe on the pooled endpoint:
BEGIN;
SET LOCAL statement_timeout = '5min'; -- this report only
SELECT ...;
COMMIT;A plain session-level SET statement_timeout = '30s' on the pooled endpoint (port 6432) applies to a server connection you are about to hand back. The next transaction may get a different one, so the setting silently is not there. Worse, a session-level setting the pooler does not reset can persist on that server connection and affect whoever borrows it next. Use ALTER ROLE or SET LOCAL.
Choosing a statement_timeout
The useful pattern is different limits for different work, which roles give you for free:
-- The web app: nothing should take this long on a request path.
ALTER ROLE app_web SET statement_timeout = '10s';
-- Background workers: batch jobs legitimately run longer.
ALTER ROLE app_worker SET statement_timeout = '5min';
-- Reporting: long by design.
ALTER ROLE app_reports SET statement_timeout = '30min';Give each of those roles its own connection string and you have separated the workloads without any extra infrastructure. A runaway report can no longer hold a connection that checkout needed.
What a timeout looks like when it fires
| Setting | SQLSTATE | Message |
|---|---|---|
statement_timeout | 57014 | canceling statement due to statement timeout |
lock_timeout | 55P03 | canceling statement due to lock timeout |
idle_in_transaction_session_timeout | 25P03 | terminating connection due to idle-in-transaction timeout |
57014 and 55P03 cancel the statement and leave the transaction alive but aborted - roll back before issuing anything else. 25P03 terminates the whole connection, so your client's pool needs to reconnect; every mature driver does this automatically.
Retrying a 55P03 after a short randomised delay is usually right. Retrying a 57014 immediately usually is not - the query was too slow once and will be too slow again.
Alerts
Usage threshold alerts on storage and connections - warning at 80%, critical at 95%, delivered to the dashboard, your webhooks, and the org billing email.
Long-running transactions
Why one open transaction can make a whole database slowly get worse, how to find the session responsible, and what the Long-running transaction advisory is telling you.