PostgreSQL は、特定の処理の実行時間を制御することで、データベース操作を管理・最適化するためのさまざまなタイムアウト設定を提供しています。 これらのタイムアウトは、特にトラフィックが多い環境や複雑なクエリを扱う環境において、システムの安定性とパフォーマンスを確保するうえで非常に重要です。それぞれ順に見ていきましょう。
lock_timeout
lock_timeout は、トランザクションがデータベースオブジェクトのロックを取得するまで待機する時間を制御し、その時間を超えるとエラーで処理を中止します:
ERROR: canceling statement due to lock timeoutDDL 操作におけるロックキューの問題
DDL 操作は、他のすべてのロックタイプと競合する ACCESS EXCLUSIVE ロックを必要とするため、ブロッキングの連鎖を引き起こします:
- ロック取得の順序: 最初のトランザクションが基本的なロックを保持 → DDL が排他アクセスを待機 → 後続のすべてのトランザクションが DDL の後ろで待機
- 連鎖的な影響: タイムアウトの制限がなければ、ブロックされた 1 つの DDL がデータベース全体の動作を停止させてしまう可能性があります
ロックキューの具体例
Transaction 1: Running SELECT (has ROW SHARE lock)
Transaction 2: Waiting for ALTER TABLE (needs ACCESS EXCLUSIVE lock)
Transaction 3: Waiting for SELECT (needs ROW SHARE lock)
Transaction 4: Waiting for INSERT (needs ROW EXCLUSIVE lock)この問題を緩和するには、lock_timeout を設定した専用の DDL ユーザーを作成します:
-- 適切なタイムアウトを設定した専用ユーザーを作成
CREATE ROLE ddl_user WITH LOGIN PASSWORD 'secure_password';
ALTER ROLE ddl_user SET lock_timeout = 10000; -- 10 秒statement_timeout
statement_timeout は、単一のクエリが実行を許可される最大時間の上限を設定します。 クエリがこの制限時間を超えると、PostgreSQL は自動的にそのクエリを中断し、エラーを返します。
ERROR: canceling statement due to statement timeout1 つの simple-Query メッセージに複数の SQL ステートメントが含まれる場合、タイムアウトは各ステートメントに個別に適用されます。statement_timeout により、長時間実行されるクエリがリソースを過剰に消費したり、データベースのパフォーマンス問題を引き起こしたりするのを効果的に防げます。
idle_in_transaction_session_timeout
idle_in_transaction_session_timeout は、トランザクション内でセッションがアイドル状態のまま留まれる最大時間を制御します。セッションがトランザクション内で指定したタイムアウト時間を超えてアイドル状態を続けると、PostgreSQL は自動的にセッションを終了し、進行中のトランザクションをロールバックします。
ERROR: terminating connection due to idle-in-transaction timeoutユーザー入力を待っていたり、データベースとは無関係な処理を行っていたりして、トランザクションを開いたままにしてしまうアプリケーションを想像してみてください。トランザクションが開いたまま長時間アイドル状態になると、テーブルや行のロックを保持し続け、他のトランザクションがそれらのリソースにアクセスできなくなる可能性があります。idle_in_transaction_session_timeout を設定すれば、こうしたアイドルセッションを自動的に終了させ、リソースが不必要に占有されないようにできます。重要なロックを保持していない場合でも、開いたままのトランザクションは、そのトランザクションからのみ可視である可能性のある「最近デッドになったタプル」のバキュームを妨げます。そのため、長時間アイドル状態が続くと、テーブルの肥大化(bloat)の原因になり得ます。
idle_session_timeout
idle_session_timeout は、セッションが自動的に終了されるまでアイドル状態でいられる最大時間を制御します。トランザクション内でアイドル状態のセッションにのみ適用される idle_in_transaction_session_timeout とは異なり、idle_session_timeout は、トランザクション内かどうかにかかわらず、アイドル状態のすべてのセッションに適用されます。
ERROR: terminating connection due to idle session timeoutコネクションプーラーやその他のミドルウェアを使用している場合は注意してください。こうしたレイヤーは、予期しない接続の切断にうまく対応できないことがあるためです。idle_session_timeout は通常、対話的(インタラクティブ)なセッションに対して設定します。対話的な処理用に別の Postgres ユーザーを作成し、それに対して idle_session_timeout を設定するのが良いプラクティスです。
ALTER ROLE interactive_user SET idle_session_timeout = 600000; -- 10 分transaction_timeout

Postgres 17 で transaction_timeout が導入され、18 でも変更なくそのまま利用できます。ドキュメントより:
トランザクション内で指定した時間を超えて続くセッションを終了します。この制限は、(BEGIN で開始される)明示的なトランザクションと、単一のステートメントに対応する暗黙的に開始されたトランザクションの両方に適用されます。
一般的な Web サービスは、主に 3 つのコンポーネントで構成されます:
- Web サーバー
- アプリケーションサーバー
- データベースサーバー
長時間続く接続を防ぐために、通常は Web サーバーとアプリケーションサーバーの両方に接続タイムアウトを設定します。Web サーバーやアプリケーションサーバーがすでに接続を切断しているのに、トランザクションの処理を続けるのは無駄です。transaction_timeout が導入される前は、長時間のトランザクションを防ぐ信頼できる方法がありませんでした。statement_timeout と idle_in_transaction_session_timeout の両方を設定していても、短いステートメントと短い待機時間の繰り返しで構成されるトランザクションは、依然として開いたままになってしまうのです。
これらを組み合わせて使い始めると、1 つ落とし穴があります。transaction_timeout を statement_timeout や idle_in_transaction_session_timeout と一緒に設定した場合、3 つのうち最も短いものが優先され、それより長い設定は実質的に無視されます。したがって、transaction_timeout = 30s と statement_timeout = 60s を設定しても、30 秒のトランザクションの中で 60 秒分のステートメント実行時間が確保されるわけではありません。先にトランザクションの上限が発動します。粗い(大きい)制限を細かい(小さい)制限より大きく保つか、あるいは細かい方の設定は最初から行わないようにしましょう。
なぜ PostgreSQL は、これほど分かりやすい transaction_timeout 機能の導入にこれほど時間がかかったのか、と思うかもしれません。まあ、遅くてもやらないよりはましですね! ちなみに、MySQL にもこの機能はありません。
idle_replication_slot_timeout
Postgres 18 では idle_replication_slot_timeout が追加されました。これはここまで紹介してきたものとは毛色が異なりますが、知っておく価値があります。ここまでの 5 つの設定はいずれもクエリやセッションの寿命を制限するものでした。この設定は、レプリケーションスロットがアイドル状態でいられる寿命を制限します。
放置されたレプリケーションスロットは、それ自体が静かに進行する災いです。スロットが存在し続ける限り、Postgres は消費されていない WAL を保持し続け、その WAL はディスクが満杯になるまで蓄積されていきます。idle_replication_slot_timeout を設定すると、しきい値を超えてアイドル状態が続いたスロットをサーバーが無効化(invalidate)できるようになります。アイドル時間は、スロットの pg_replication_slots.inactive_since を基準に計測されます。
# postgresql.conf でのみ設定可能。デフォルトの 0 で無効
idle_replication_slot_timeout = '24h'注意点が 2 つあります。1 つ目は、この設定は postgresql.conf またはコマンドラインでのみ設定でき、セッションごとには設定できないことです。2 つ目は、無効化はしきい値を超えた瞬間ではなく、次のチェックポイントのタイミングで行われることです。そのため、「スロットがアイドル状態になりすぎた」時点と「スロットが無効化された」時点との間には、ある程度のラグが生じます。また、WAL を予約しないスロットや、プライマリから同期されているスタンバイ側のスロットには適用されません。