Как закрыть простоя подключений в PostgreSQL автоматически?

Некоторые клиенты подключаются к нашей базе данных postgresql, но оставляют открытыми подключения. Можно ли сообщить Postgresql о закрытии этих соединений после определенного количества бездействия?

TL; DR

ЕСЛИ вы используете версию Postgresql >= 9.2
      THEN используйте решение, с которым я придумал

ЕСЛИ вы не хотите писать какой-либо код
 THEN используйте решение arqnid

Ответ 1

Для тех, кто заинтересован, вот решение, которое я придумал, вдохновленный Craig Ringer:

(...) используйте задание cron, чтобы посмотреть, когда последнее соединение было активным (см. pg_stat_activity), и используйте pg_terminate_backend, чтобы убить старые. (...)

Выбранное решение происходит следующим образом:

  • Сначала мы обновляемся до Postgresql 9.2.
  • Затем мы планируем поток запускать каждую секунду.
  • Когда поток работает, он ищет любые старые неактивные соединения.
    • Соединение считается неактивным, если его состояние равно либо idle, idle in transaction, idle in transaction (aborted) или disabled.
    • Соединение считается старым, если его последнее состояние изменилось более 5 минут.
  • Есть дополнительные потоки, которые делают то же самое, что и выше. Однако эти потоки подключаются к базе данных с другим пользователем.
  • Мы оставляем по крайней мере одно соединение открытым для любого приложения, подключенного к нашей базе данных. (rank())

Это SQL-запрос, выполняемый потоком:

WITH inactive_connections AS (
    SELECT
        pid,
        rank() over (partition by client_addr order by backend_start ASC) as rank
    FROM 
        pg_stat_activity
    WHERE
        -- Exclude the thread owned connection (ie no auto-kill)
        pid <> pg_backend_pid( )
    AND
        -- Exclude known applications connections
        application_name !~ '(?:psql)|(?:pgAdmin.+)'
    AND
        -- Include connections to the same database the thread is connected to
        datname = current_database() 
    AND
        -- Include connections using the same thread username connection
        usename = current_user 
    AND
        -- Include inactive connections only
        state in ('idle', 'idle in transaction', 'idle in transaction (aborted)', 'disabled') 
    AND
        -- Include old connections (found with the state_change field)
        current_timestamp - state_change > interval '5 minutes' 
)
SELECT
    pg_terminate_backend(pid)
FROM
    inactive_connections 
WHERE
    rank > 1 -- Leave one connection for each application connected to the database

Ответ 2

Подключитесь через прокси, например PgBouncer, который закроет соединения после server_idle_timeout секунд.