49.2. ロジカルデコーディングのコンセプト #

49.2.1. ロジカルデコーディング #

ロジカルデコーディングは、データベースのテーブルへの恒久的な更新を、一貫性があって、データベース内部の状態に関する詳細な知識がなくても容易に理解できる形式として取得するプロセスです。

PostgreSQLにおいてロジカルデコーディングは、記憶装置のレベルで更新を記述する書き込み先行ログの内容を、タプルやSQL文のストリームといったアプリケーション固有の形式にデコードすることによって実装されています。

49.2.2. レプリケーションスロット #

ロジカルレプリケーションの文脈ではスロットは、元のサーバで行われた変更と同じ順序でクライアント上でリプレイできるようなストリームを表します。 それぞれのスロットは、単一のデータベース上の変更操作の連鎖をストリームとして流します。

注記

またPostgreSQLには、ストリーミングレプリケーションスロットがあります (27.2.5参照)。しかし、ここでの説明とは少し違う使い方がされています。

それぞれのレプリケーションスロットはPostgreSQLクラスタの中で一意な識別子を持っています。 スロットは、そのために使用される接続とは独立しており、クラッシュセーフです。

ロジカルスロットは、通常の操作においては、各々の変更操作を一度だけ送出します。 それぞれのスロットにおける現在位置は、チェックポイントのときにだけ永続的になります。 ですからクラッシュすると、スロットは以前のLSNに戻ってしまうかもしれませんし、サーバの再起動時には最近の変更が再送されることになります。 ロジカルデコーディングのクライアントは、同じメッセージを複数回扱うことによる好ましくない結果を避けることに対して責任を追っています。 クライアントはデコーディングの際に最後に確認したLSNを記録し、繰り返されるデータをスキップしたり、(レプリケーションプロトコルを使う場合に)サーバに開始時点を決めさせるのではなく、記録しておいたLSNからデコーディングを始めるように要求するかもしれません。 レプリケーション進捗追跡機能はこの目的のために設計されています。 replication originsを参照してください。

単一のデータベース中に、お互いに独立した複数のスロットが存在しても構いません。 それぞれのスロットは自分自身の状態を持っており、データベース更新のストリーム上の別の場所から変更データを受信する異なる消費者があり得ます。 多くのアプリケーションにとっては、各消費者に対して個別のスロットが必要となるでしょう。

ロジカルレプリケーションスロットは、受信者の状態については関知しません。 同時にでなければ、同じスロットを使う複数の異なる受信者を持つことさえできます。 その場合は、直近の受信者がストリームの消費を終了した時点から更新データを受信するだけです。 どの時点でも1つのスロットからの変更を消費できるのは1つの受信側だけです。

《機械翻訳》ロジカルレプリケーションスロットは、ホットスタンバイ上にも作成できます。 システムカタログから必要な行をVACUUMが削除するのを防ぐためには、スタンバイ上でhot_standby_feedbackを設定する必要があります。 それでも、必要な行が削除されるとスロットは無効になります。 プライマリとスタンバイの間に物理スロットを使用することを強くお勧めします。 そうしないと、hot_standby_feedbackは動作しますが、接続が生きている間だけです(例えばノードの再起動で破壊されます)。 その場合、プライマリはスタンバイ上のロジカルデコーディングが必要とするシステムカタログ行を削除するかもしれません(スタンバイ上のcatalog_xminについては知らないので)。 既存のスタンバイ上のロジカルスロットも、プライマリ上のwal_levellogicalよりも小さくなると無効になります。 これはスタンバイがWALストリームでそのような変更を検出したときにすぐに行われます。 これは、遅延しているWAL送信者がある場合に、プライマリ上のwal_levelパラメータの変更までの間に、一部のWALレコードがデコードされないことを意味します。

《機械翻訳》論理スロットの作成には、現在実行中のすべてのトランザクションに関する情報が必要です。 プライマリでは、この情報は直接使用できますが、スタンバイでは、この情報をプライマリから取得する必要があります。 したがって、スロットの作成は、プライマリで何らかのアクティビティが発生するまで待機する必要があります。 プライマリがアイドル状態の場合、スタンバイで論理スロットを作成するには、pg_log_standby_snapshot関数を呼び出すとかなり時間がかかることがあります。

注意

レプリケーションスロットは、クラッシュをまたがって永続し、消費者の状態については関知しません。 スロットを使う接続がない場合でも、消費者が必要としているリソースが削除されることを防ぎます。 これによりストレージが消費されます。何故ならば、関連するWALもシステムカタログの行も、レプリケーションスロットが必要とする限りVACUUMによって削除されないからです。 極端な場合、トランザクションIDの周回(25.1.5を参照)を防ぐためのデータベース停止をもたらす可能性があります。 したがって、必要でなくなったスロットは削除すべきです。

49.2.3. 出力プラグイン #

出力プラグインは、書き込み先行ログの内部データ表現を、レプリケーションスロットの消費者が必要とする形式に変換します。

49.2.4. スナップショットのエクスポート #

ストリーミングレプリケーションのインタフェースを使って新しいスロットを作ると(CREATE_REPLICATION_SLOT参照)、スナップショットがエクスポートされます(9.27.5参照)。 このスナップショットはまさにその時点でのデータベースの状態を示しており、スナップショット以後のすべての変更は更新ストリームに含まれるようになります。 このことを利用して、スロットが作られた際のデータベースの状態をSET TRANSACTION SNAPSHOTを使って読み込むことにより、新しいレプリカを作ることができます。 このトランザクションは、その時点のデータベースの状態をダンプするために使用することができます。 また、スロットに含まれるデータを使って、ダンプした後で行われた更新を失うことなくデータベースを更新できます。

スナップショットの作成はいつでも可能なわけではありません。 とりわけ、ホットスタンバイに接続するときは失敗します。 スナップショットのエクスポートが必要ないアプリケーションは、NOEXPORT_SNAPSHOTオプションを使ってスナップショットのエクスポートを抑止できます。