Statement and Transaction Timeouts in Postgres and CockroachDB

One of the worst day-to-day things you can do in a database is lock a table or rows for too long. This will cause other requests to wait or fail. In some cases, you will need a DBA to step in and kill the offending session to release the locks.

There are three timeouts on Postgres and CockroachDB to help prevent this from happening -

  • statement_timeout - the maximum time a single statement can run. If it exceeds this time, it will be stopped.
  • idle_in_transaction_session_timeout - the maximum time a transaction can be idle before it is stopped.
  • transaction_timeout - the maximum time a transaction can run before it is stopped.

These timeouts can be set at the role/user level and the cluster level. They can also be applied when starting a transaction, but I suggest you set them on the role/user or cluster. This way, the database can end a query that is taking too long.

They are very easy to try out. You can find more information on all of them here - https://www.postgresql.org/docs/current/runtime-config-client.html.

For the purposes of this post, I am going to set them for the role bryan, which I am also logged in with.

Timeouts

These timeouts work together in some cases.

If you are not using an explicit transaction, only the statement_timeout applies.

But in an explicit transaction, all three may apply.

Usage of timeouts
Usage of timeouts

For example, in a transaction, if you run a single statement that takes longer than the statement_timeout, it will be stopped even if the total transaction time is less than the transaction_timeout.

A transaction that is idle for longer than the idle_in_transaction_session_timeout will be stopped even if the total transaction time is less than the transaction_timeout.

statement_timeout

To set the statement_timeout for the role bryan to 1 second, run -

ALTER ROLE bryan SET statement_timeout = 1000;

You can check the setting with this command (you may need to disconnect and reconnect to see the change) -

SHOW statement_timeout;

Try running a long statement, such as -

SELECT pg_sleep(2)

It will timeout with an error [57014] ERROR: query execution canceled due to statement timeout.

Then run -

SELECT pg_sleep(0.5)

It will complete successfully.

That’s it, no single statement can run longer than 1 second. This does not affect the length of time a transaction can run for, only the individual statements within a transaction.

idle_in_transaction_session_timeout

To set the idle_in_transaction_session_timeout for the role bryan to 5 seconds, run -

ALTER ROLE bryan SET idle_in_transaction_session_timeout = 5000;

You can check the setting with this command (you may need to disconnect and reconnect to see the change) -

SHOW idle_in_transaction_session_timeout;

To try it out, start a transaction -

BEGIN;
SELECT 1;
-- wait more than 5 seconds here
SELECT 2;
-- you should get an error on this statement

transaction_timeout

To set the transaction_timeout for the role bryan to 10 seconds, run -

ALTER ROLE bryan SET transaction_timeout = 10000;

You can check the setting with this command (you may need to disconnect and reconnect to see the change) -

SHOW transaction_timeout;

To try it out, start a transaction -

BEGIN;
SELECT 1;
-- wait a second or two
SELECT 1;
-- keep repeating until you exceed 10 seconds total
-- you should get an error on one of these statements

The error will be -

ERROR: query execution canceled due to transaction timeout 
SQLSTATE: 57014
comments powered by Disqus

Related