When queries run for a long time, they can overload the database, increase CPU usage, and hold onto connections, slowing down your application. The priority is to quickly identify and stop these queries, then optimize them afterward. In this guide, we’ll cover how to detect and terminate long-running queries in PostgreSQL, helping you prevent bottlenecks and outages.
Step 1: Identify Long-Running Queries
PostgreSQL provides a system view called pg_stat_activity
to help identify running queries. This view shows server process IDs (PIDs), sessions, queries, and other metadata for each active query process.
To find long-running queries, you can run the following query:
SELECT
pid,
age(clock_timestamp(), query_start) AS age,
query,
*
FROM
pg_stat_activity
WHERE
query NOT ILIKE '%pg_stat_activity%'
AND query_start IS NOT NULL
AND state = 'active'
ORDER BY
age DESC;
Sample Output:
Here’s an example of what you might see when running the query:
pid | age | query | datid | datname | pid | leader_pid | usesysid | usename | application_name | client_addr | client_hostname | client_port | backend_start | xact_start | query_start | state_change | wait_event_type | wait_event | state | backend_xid | backend_xmin | query_id | query | backend_type
-------+-----------------+---------------------------+-------+----------+-------+------------+----------+-----------------+------------------+-------------+-----------------+-------------+--------------------------------+----------------------------------+----------------------------------+----------------------------------+-----------------+------------+--------+-------------+--------------+----------+---------------------------+----------------
14414 | 00:01:01.244224 | select * from test_table; | 14042 | postgres | 14414 | | 10 | pramodchoudhari | psql | | | -1 | 2024-09-10 14:20:36.3632+05:30 | 2024-09-10 15:12:12.661608+05:30 | 2024-09-10 15:12:12.661608+05:30 | 2024-09-10 15:12:12.661622+05:30 | | | active | | 417622 | | select * from test_table; | client backend
The pid
represents the process ID associated with the query, and the age
column tells you for how long the query has been running.
Step 2: Stop the Long-Running Queries
There are two ways to stop a long-running query:
- Using
pg_cancel_backend
: This will cancel the query but keep the session alive. - Using
pg_terminate_backend
: This will terminate the entire session and free up all the resources.
Example:
If you want to stop a long-running query with PID 14414
, you can use the following commands:
- To cancel the query and keep the session alive:
SELECT pg_cancel_backend(14414);
- To terminate the session and kill the query entirely:
SELECT pg_terminate_backend(14414);
Step 3: Setting statement_timeout
at User Level
You can set different statement_timeout
values for different types of users to balance performance and accommodate long-running queries in specific use cases:
- Webserver User: Set a short timeout to ensure web requests don’t get stuck on long queries, ensuring responsiveness
ALTER ROLE webserver_user SET statement_timeout = '10s';
- Script User: Set a longer timeout to allow more time for scripts, which might be running more intensive operations.
ALTER ROLE script_user SET statement_timeout = '1min';
- Migration User: Set a even longer timeout for database migrations since they might involve extensive operations.
ALTER ROLE migration_user SET statement_timeout = '5min';
Example of statement_timeout
in Action
postgres=# INSERT INTO large_table (name) SELECT 'Row ' || generate_series(1,10000000);
ERROR: canceling statement due to statement timeout
In this case, the query to insert 10 million rows into the table was automatically canceled due to the configured statement_timeout
. This showcases how PostgreSQL protects the system from excessively long queries when statement_timeout
is set.
Summary
- Use
pg_cancel_backend
when you want to stop a long-running query but keep the session alive. - Use
pg_terminate_backend
when you need to forcefully disconnect the session and free up resources. - Set
statement_timeout
at the user level for different roles (webserver, scripts, migrations) to ensure a balanced approach between performance and long-running tasks. - Regularly monitor your database for long-running queries and setup alarms to prevent such performance issues.