NOTIFY — 通知を生成する
NOTIFYchannel
[ ,payload
]
NOTIFY
コマンドは、現在のデータベース内で事前に指定チャネル名についてLISTEN
コマンドを実行したクライアントアプリケーションに「ペイロード」文字列(省略可能)を持つ通知イベントを送ります。
通知はすべてのユーザから可視です。
channel
NOTIFY
は同一のPostgreSQLデータベースにアクセスするプロセスの集合に対する単純なプロセス間通信の仕組みを提供します。
通知の際にペイロード文字列を送信することができます。
また、データベース内のテーブルを使用して通知者から(1つまたは複数の)リスナに追加的なデータを渡すことにより、構造化されたデータを渡す高度な仕組みを構築することができます。
通知イベントとしてクライアントに渡される情報には、通知チャネル名と通知を行うセッションのサーバプロセスのPID、ペイロード文字列(指定されていなければ空文字列)が含まれます。
各データベースにおいて使用される通知チャネル名とその意味についての定義は、データベース設計者に任されています。
通知チャネル名には、データベース内のテーブル名と同じものを使用するのが一般的です。
通知イベントは本質的に「このテーブルを変更しました。変更された箇所を確認してください」ということを意味するものです。
しかし、NOTIFY
コマンドとLISTEN
コマンドでは、そのような関連付けは強制されていません。
例えば、データベース設計者は、1つのテーブルに対する異なる種類の変更を通知するために、複数の異なる通知チャネル名を使用することができます。
他の方法としてペイロード文字列を使用して各種様々な状況に対応させることもできます。
特定のテーブルが変更されたことを通知するためにNOTIFY
を使用する場合、NOTIFY
をテーブル更新時に発行されるルール内に配置すると便利です。
こうすると、通知はテーブルが変更された時に自動的に行われるので、アプリケーションプログラマが通知の実行を忘れるといった事故を防ぐことができます。
NOTIFY
とSQLトランザクションの間には、いくつかの重要な相互作用があります。
まず、NOTIFY
がトランザクション内部で実行された場合、通知イベントはトランザクションがコミットされない限り配送されません。
トランザクションがアボートされた場合、NOTIFY
だけでなく、そのトランザクション内で行われたコマンドが全て無効化されるので、これは妥当といえます。
しかし、通知イベントが即座に配送されることを期待していた場合、当惑するかもしれません。
次に、監視中のセッションがトランザクション処理中に通知信号を受け取った場合、そのトランザクションが(コミットもしくはアボートされて)完了するまで、通知イベントは接続しているクライアントに配送されません。
この理由も同じです。トランザクションに通知が配送された後にそのトランザクションがアボートされた場合、何とかして通知を取り消したくなりますが、サーバはいったんクライアントに送信した通知を「取り戻す」ことはできません。
したがって、通知イベントはトランザクションとトランザクションの合間にのみ配送されます。
結論として、NOTIFY
を使用してシグナルの実時間処理をするアプリケーションではトランザクションを短くしておかなければなりません。
同じチャネル名が、同一トランザクションから同じペイロード文字列で複数回通知される場合、
データベースサーバは1つの通知のみを伝えるように決定することがあります。
一方、異なるペイロード文字列を持つ通知は常に別の通知として伝えられます。
同様に別のトランザクションからの通知が1つの通知にまとめられることは決してありません。
重複する通知インスタンスを後で削除する場合は例外ですが、NOTIFY
は同一トランザクションからの通知は送信された順番に配送されることを保証します。
また異なるトランザクションからのメッセージがトランザクションのコミット順で配送されることも保証します。
NOTIFY
を実行するクライアント自身が、その通知の通知チャネルを監視していることはよくあります。
この場合、同じ通知名を監視する他のセッションに対するのと同じように通知イベントが戻ってきます。
アプリケーションのロジックにもよりますが、これは無駄な作業になることがあります。
例えば、そのセッションが書き出したばかりのデータベースに対する更新を調べるためにテーブルの再読み込みを行う場合などが考えられます。
通知元セッションのサーバプロセスのPID(通知イベントメッセージ内にあります)と、自分自身のPID(libpqで得られます)が同じかどうか調べることで、こういった余計な作業を回避できます。
PIDが同じであれば、その通知イベントは自分自身から跳ね返ってきたものであり、無視することができます。
channel
シグナルとして送られる通知チャネル名です(任意の識別子)。
payload
通知と一緒に通信される「ペイロード」文字列です。 これは単純な文字列リテラルとして指定されなければなりません。 デフォルトの設定では、8000バイト未満でなければなりません。 (バイナリデータまたは大規模な情報を渡さなければならないのであれば、データベーステーブル内に格納しレコードのキーを送信することが最善です。)
送信済みだがすべての監視セッションでは処理されていない通知を保持するためのキューが存在します。
このキューがいっぱいになると、NOTIFY
を呼び出すトランザクションのコミットに失敗します。
キューはかなり大きなもの(標準のインストレーションで8ギガバイト)であり、ほとんどすべての環境で十分な大きさであるはずです。
しかしセッションがNOTIFY
を実行した後に長期間のトランザクションに入った場合、キューからクリーンアップできなくなります。
キューの半分までたまると、ログファイル内にクリーンアップを妨げているセッションを指し示す警告が現れるようになります。
この場合、クリーンアップ処理が進むように、確実にそのセッションでその現在のトランザクションを完了させるようにしなければなりません
NOTIFY
を実行したトランザクションでは二相コミットを準備することはできません。
psqlから監視/通知処理の設定と実行を行います。
LISTEN virtual; NOTIFY virtual; Asynchronous notification "virtual" received from server process with PID 8448. NOTIFY virtual, 'This is the payload'; Asynchronous notification "virtual" with payload "This is the payload" received from server process with PID 8448. LISTEN foo; SELECT pg_notify('fo' || 'o', 'pay' || 'load'); Asynchronous notification "foo" with payload "payload" received from server process with PID 14728.
標準SQLにはNOTIFY
はありません。