Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】PostgreSQL LISTEN/NOTIFY Queue Full: A 512 KiB Reproduction

2026年09月19日に「Dev.to」が公開したITニュース「PostgreSQL LISTEN/NOTIFY Queue Full: A 512 KiB Reproduction」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

PostgreSQLのNOTIFYキューが満杯になる原因は、LISTEN中のトランザクションがCOMMITされず、通知が処理されないため。実験でキュー容量を制限し、LISTEN中のトランザクションを放置すると、NOTIFYが失敗した。キュー容量を増やしてもこの問題は解決しない。

ITニュース解説

PostgreSQLのデータベースは、さまざまなアプリケーションがデータを保存し、利用するための基盤となる。その中で「LISTEN/NOTIFY」という機能は、データベース内で特定のイベントが発生した際に、その情報を他の接続(アプリケーションなど)にリアルタイムで通知するための仕組みである。これは、たとえばWebアプリケーションで新しいデータが追加されたときに、即座に画面を更新するといった用途に使われることが多い。通知はイベント発生元が「NOTIFY」で送信し、通知を受け取りたい側は「LISTEN」で特定のチャネルを購読する。

この便利な機能には、通知を一時的に保管するための「NOTIFYキュー」と呼ばれる領域が存在する。しかし、このキューが満杯になると、「ERROR: too many notifications in the NOTIFY queue」というエラーが発生し、それ以上通知を送れなくなる問題が起きることがある。このエラーがどのような状況で発生し、その原因がどこにあるのかを再現実験を通じて詳しく見ていこう。

実験はPostgreSQL 17という最新のバージョンと、仮想環境を簡単に構築できるDockerを組み合わせて行われた。データベースへの操作は、PostgreSQLのコマンドラインツールであるpsqlのセッションを二つ使い、それぞれ通知を送る側と受け取る側(リスナー)の役割を担わせた。通常、PostgreSQLのNOTIFYキューは数ギガバイトという非常に大きな容量がデフォルトで設定されているため、満杯にするのは容易ではない。そこで今回の実験では、max_notify_queue_pagesという設定値を意図的に64に制限した。PostgreSQLのページサイズが8 KiB(キロバイト)なので、これによりキューの最大容量はわずか512 KiB(8 KiB * 64ページ)となり、キューが満杯になる状況を簡単に再現できる環境を整えた。

まず、通知を受け取る側のリスナーセッションで、「demo」という名前のチャネルを購読するためにLISTEN demo;コマンドを実行した。さらに、通知を受け取った際に何らかの処理を行うかのように見せるため、BEGIN;コマンドでトランザクションを開始したまま、COMMIT;ROLLBACK;を実行せずにアイドル状態(何も操作しない状態)で放置した。この「トランザクションを開いたまま放置する」という状態が、この問題の重要な鍵となる。

次に、別の接続セッションから、pg_notify()という関数を使って「demo」チャネルに対して通知を大量に送信した。今回は、キューを素早く満杯にするため、それぞれの通知を比較的大きなデータ量に設定し、合計100回の通知を連続して送った。すると、その結果は明確に現れた。100回の通知のうち、最初の64回は成功裏に送信されたが、残りの36回は送信に失敗したのである。この時点で、PostgreSQLは「ERROR: too many notifications in the NOTIFY queue」というエラーを報告し、キューの使用率は100%に達していた。これは、設定された512 KiBの容量が完全に消費され、それ以上通知を格納するスペースがなくなったことを意味する。

なぜキューが満杯になったのか、そしてなぜリスナー側がBEGIN;の状態であると問題が発生するのか。PostgreSQLのNOTIFYキューは、通知が送信されてから、それを購読しているすべてのリスナーに配信されるまで、その通知を保持し続ける。重要なのは、通知がリスナー側のトランザクション内で処理されるまで、キューから削除されないという点である。今回の実験では、リスナーセッションがBEGIN;コマンドでトランザクションを開始したまま、COMMIT;ROLLBACK;を実行せずに放置されていた。このため、通知はリスナーに配信されてはいたものの、リスナー側のトランザクションが終了していないため、通知キューから「完全に処理済み」として削除されることがなかったのである。結果として、キューは解放されずに通知が蓄積され続け、最終的に設定された容量に達して満杯になってしまったのだ。

その後、通知を受け取る側のリスナーセッションに戻り、COMMIT;コマンドを実行して開いたままだったトランザクションを終了させた。すると、トランザクションの終了と同時に、キューに保留されていたすべての通知が正式に処理済みとなり、キューから削除された。実際にSELECT pg_notification_queue_usage();という関数でキューの使用状況を確認すると、値が0に戻っていることが確認できた。これにより、キューが正常にクリアされ、再び通知を受け入れる準備ができたことがわかる。

この実験から得られる重要な教訓は、max_notify_queue_pagesという設定値を増やしてNOTIFYキューの容量を大きくしても、この問題の根本的な解決にはならないということである。キューの容量を増やせば、一時的に多くの通知を保持できるようになるかもしれないが、リスナー側でトランザクションが適切に終了されない限り、通知はキューに残り続け、いずれは再び満杯になってしまう。つまり、キューの容量自体が問題なのではなく、リスナー側のアプリケーションが通知を処理した後に、そのトランザクションを速やかにCOMMIT;またはROLLBACK;で終了させ、通知キューからのクリーンアップを促すことが極めて重要である。

システムエンジニアを目指す上では、PostgreSQLのようなデータベースがどのように内部的に動作し、設定値が機能にどのような影響を与えるかを理解することが不可欠である。特にLISTEN/NOTIFYのようなリアルタイム連携機能を使う際には、通知の生成と消費のバランス、そしてトランザクション管理の重要性を考慮することが、安定したシステム運用には不可欠となる。今回の実験は、一見すると単なるキュー容量の問題に見えても、その背後にはトランザクションの適切な管理が深く関わっていることを示している。

関連コンテンツ

関連IT用語