論理レプリケーションのテーブル同期ワーカ – PostgreSQL 14でコミットされた機能の先行紹介:技術者Blog
PostgreSQLインサイド

Peter Smith

Fujitsu Australia Software Technology
Senior Software Development Engineer

はじめに

富士通OSSチームは、OSSコミュニティーの他のメンバーと協力して、PostgreSQLの論理レプリケーションを強化するためのコードを提供しています。
論理レプリケーションのパブリッシャー(PUBLISHER)/ サブスクライバー(SUBSCRIBER)モデル設計の基本的な部分は、サブスクリプションを実装するためにバックグラウンド「ワーカ」プロセスがどのように使用されるかということです。
このブログでは、サブスクリプションワーカプロセスの背景を説明し、テーブル同期ワーカに対して行った機能エンハンスの一部を解説しています。

背景

Peter
私たちがこの分野で行ってきたことの多くは、ユーザーに見える部分ではありません。筋道を立てて変更内容を説明できるように、まずは背景について情報を伝える必要があると考えます。

ワーカ

サブスクリプションのレプリケーションは、次の2種類のバックグラウンドワーカによって実行されます。

  1. 1個の適用ワーカ
  2. 1個以上のテーブル同期ワーカ

CREATE SUBSCRIPTIONコマンドは、単一の適用ワーカを起動します。この適用ワーカは、N個のテーブル同期ワーカ(サブスクリプションがサブスクライブするテーブルごとに1つ)を起動します。
適用ワーカプロセスは長期間存続します。サブスクリプションが削除されるまで存在します(エラーがなければ)。テーブル同期ワーカプロセスは一時的です。各テーブル同期ワーカは、関連付けられたテーブルの「同期」フェーズにのみ存在します。
テーブル同期ワーカの主な働きは、(CREATE SUBSCRIPTIONが実行された時点で)パブリッシュされたテーブルのすべての行をCOPYして、レプリケートされたテーブルを初期化することです。
テーブル同期ワーカと適用ワーカはどちらも、未適用のレプリケーションメッセージ(例えば、サブスクライバー側でCRUD(Create、Read、Update、Delete)操作をレプリケートするためのメッセージ)の受信と処理が可能です。

図1:適用ワーカプロセスとテーブル同期ワーカプロセス
図1:適用ワーカプロセスとテーブル同期ワーカプロセス

複数のテーブルを持つサブスクリプションのプロセスは次例のようになります(walsenderはパブリッシャーノードにあり、論理レプリケーションワーカーはサブスクライバーノードにあります)。

postgres: logical replication worker for subscription 16571            ・・・(注1)
postgres: walsender postgres ::1(40932) START_REPLICATION
postgres: logical replication worker for subscription 16571 sync 16403 ・・・(注2)
postgres: walsender postgres ::1(40934) COPY
postgres: logical replication worker for subscription 16571 sync 16385 ・・・(注2)
postgres: walsender postgres ::1(40936) COPY
  • 注1
    適用ワーカと、そのwalsender
  • 注2
    2個のテーブル同期ワーカと、そのwalsender

テーブル同期の状態

レプリケーションメッセージは、テーブル同期ワーカが起動してコピーするのと同時に、継続的に受信される場合があります。すべてのワーカが同じレプリケーションメッセージを含む異なるストリームを使用しているため、タイミングによっては、同じメッセージを処理するために、テーブル同期ワーカの処理が適用ワーカより前で実行されたり、後ろで実行されたりする場合があります。
互いに干渉(同じメッセージの二重処理)しないように、適用ワーカとテーブル同期ワーカは、「状態(state)」を介して互いの同期を取り合います。

図2:状態(states)を使用した適用ワーカーとテーブル同期ワーカーのプロセス間通信
図2:状態(states)を使用した適用ワーカーとテーブル同期ワーカーのプロセス間通信

図中に記載されている注の説明は以下のとおりです。

  • 注3
    STATE_FINISHEDCOPYはPostgreSQL 14で導入された新規の状態です。
  • 注4
    テーブル同期の状態は、共有メモリー内にあるものと、システムカタログ内にあるものがあります。SYNCWAITとCATCHUPは共有メモリーにあります。システムカタログにあるものは、pg_subscription_relシステムカタログに問い合わせて、サブスクライブされた各テーブルのテーブル同期の状態(「srsubstate」)で確認できます。

例として、以下は5つのテーブルがあるサブスクリプションです。これらの状態は、2個のテーブル同期ワーカがまだデータをコピーしていて、他の3個はすでに完了していた時点でキャプチャーされたものです

test_sub=#
test_sub=# SELECT * FROM pg_subscription_rel;
srsubid | srrelid | srsubstate | srsublsn
---------+---------+------------+------------
   16571 |   16385 | d          |             ・・・(注5)
   16571 |   16403 | d          |             ・・・(注5)
   16571 |   16412 | r          | 0/11A31128  ・・・(注6)
   16571 |   16421 | r          | 0/11A2F350  ・・・(注6)
   16571 |   16394 | r          | 0/11A2F510  ・・・(注6)
(5 rows)
  • 注5
    2個のテーブル同期ワーカが、まだデータをコピーしています
  • 注6
    3個のテーブル同期ワーカが、すでに完了しています

状態コード

「srsubstate」のコードの状態について以下に示します。

コード 状態
i STATE_INIT
d STATE_DATASYNC
f STATE_FINISHEDCOPY(PostgreSQL 14で新規追加)
w STATE_SYNCWAIT
c STATE_CATCHUP
s STATE_SYNCDONE
r STATE_READY

copy_data = falseオプション

前述のように、テーブル同期ワーカの主な目的は、初期のテーブルデータCOPYおよび同期を行うことです。しかし、オプションとしてcopy_data=falseオプションを指定してSUBSCRIPTIONを作成すると、すべてのコピー処理がスキップされます。この場合、テーブル同期ワーカは状態がSTATE_READYに設定された状態で起動されます。これにより、テーブル同期ワーカは直ちに終了します。

テーブル同期エラー

テーブル同期ワーカの処理中にエラーが発生した場合(例えば、DATASYNCフェーズ中にデータの主キー違反が発生する可能性があります)、テーブル同期ワーカはエラーをログに記録して終了します。
適用ワーカは、まだSTATE_READYに達していないサブスクライブテーブルをすべて認識しているため、しばらくするとテーブル同期ワーカが消失したことが検出され、別のテーブル同期ワーカが再起動されて、置き換えられます。
同じ(またはいずれかの)エラーが再び発生すると、この置き換えられたテーブル同期ワーカも失敗し、別の再起動されたテーブル同期ワーカが置き換わってしまいます。このエラー → 起動 → エラー → 起動のループは、次のいずれかとなるまで発生し続けます。

  1. 問題の原因(例えば、主キー違反)が解決され、テーブル同期ワーカがエラーなしで完了できるようになる。または、
  2. 問題のあるテーブルがサブスクリプションから削除される。

テーブル同期ワーカの拡張

テーブル同期ワーカには、次のような改善点があります。

永続的なスロットと起点の追跡

論理レプリケーションスロットは、サブスクリプション向けのwalsenderのために、どんなマスターWALファイルを保持する必要があるかを追跡する、PostgreSQLが使用するメカニズムです。各スロットは、元のサーバーで行われた変更と同じ順序で再生されるようなストリームを表します。スロットは、ストリーム内の現在位置に関する情報も保持します。
各テーブル同期ワーカには、関連づけられたレプリケーションスロットがあります。これらは以前は一時的なスロットであり、各テーブル同期ワーカが生きている間だけメモリーに残りました。テーブル同期ワーカが予期せずクラッシュした場合は、スロットが失われます。そして置換用のテーブル同期ワーカが起動されると、一時スロットが新しく作成されるところから開始し、初めからすべての処理を繰り返します。
テーブル同期ワーカは、一時的なスロットではなく、永続的なスロットを使用するように変更されました。レプリケーション「起点」の情報は、どのデータがすでにレプリケートされたかを追跡するためにスロットに保存されるため、永続的なスロットを使用すると、クラッシュ / 再起動後に、最後に記録されたチェックポイントからレプリケーションを再び選択できるようになります。
これらのスロットは、以下例のように「pg_<サブスクリプションOID>_sync_<テーブルrelid>_<システムID>」という命名規則で自動生成されます。

pg_16571_sync_16403_6992073143355919431

以下のその他のエンハンスも、テーブル同期ワーカが永続的なスロットを使用することで利用可能になります。

テーブル同期の新しい「状態」- STATE_FINISHEDCOPY

初期のデータ・コピー・フェーズは非常にコストがかかる場合があります。デフォルトの場合(copy_data=true)、テーブル同期ワーカはすべてのテーブルデータをCOPYします。これは、何ギガバイトものデータになる可能性があり、完了までに時間がかかる場合があります。
前述のように、テーブル同期ワーカ内でエラーが発生した場合は、新しいテーブル同期ワーカを再起動して置き換えます。これまでは、再起動されたテーブル同期ワーカがDATASYNC状態から再起動され、COPYが再び発生することを意味していました。
そこで、テーブル同期の新しい状態「STATE_FINISHEDCOPY」が導入されました。
この状態は、コピーフェーズが完了し、テーブルデータがコミットされたときに割り当てられます。テーブル同期のスロットは永続的になるため、「起点」情報もこのコミット・チェックポイントで更新されます。
FINISHEDCOPYの状態が設定された後に、いくつかのサブシーケンスにおいてテーブル同期ワーカを再起動する必要があるようなエラーが発生すると、コードロジックは(高価な)コピーステップがすでに完了したことを認識します。レプリケーションは最後の既知の起点から再起動されるため、このコピーステップは繰り返されません。

マルチトランザクションの実装

以前は、テーブル同期ワーカは、エラー発生の有無によってコミットされたかどうかに関係なく、単一のトランザクションで実行されていました。
テーブル同期ワーカは、複数のトランザクションを実装するよう拡張されました。

  1. 初期コピーの部分(DATASYNCからFINISHEDCOPY)は1つのトランザクションで実行されるようになりました。
  2. レプリケーションメッセージ処理部分は、別の(独自の)トランザクションで実行されるようになりました(これにより、レプリケーションメッセージのテーブル同期ワーカ処理が、適用ワーカと同じくマルチトランザクションになります)。

新しいFINISHEDCOPY状態と組み合わせると、コピー部分を独立してコミットできるようになります。また、レプリケーション「起点」のトラッキングは永続的スロットに記録されるため、すでにコミットされているデータはスキップできます。

その他の貢献

富士通はPostgreSQLの論理レプリケーションの領域で、他にも多くのバグ修正や小さな改善を行っており、定期的に他のパッチのレビューに参加しています。
コミュニティーの協力のもと行ったPostgreSQL 14の変更を以下に示します。

メリット

テーブル同期ワーカの改善により、論理レプリケーション機能の以下の改良に役立ちました。

  • 障害発生時の堅牢性の向上
  • 効率性の向上(既にコミットされている場合に、コストのかかるテーブルの再コピーを回避できるシナリオ)
  • 一貫性の向上(適用ワーカと同じマルチ・トランザクション・ロジックを使用)
  • 安定性の向上(バグ修正によって)

今後に向けて

富士通OSSチームは論理レプリケーションの改善を支援し続けており、PostgreSQL 15でコミットしたいと考えているいくつかのエンハンスを既に行っています。このトピックに関する他のブログにも注目してください。

2021年9月10日公開

富士通のソフトウェア公式チャンネル(YouTube)

本コンテンツに関するお問い合わせ

お電話でのお問い合わせ

Webでのお問い合わせ

当社はセキュリティ保護の観点からSSL技術を使用しております。

ページの先頭へ