トップ  >  Slony-I: PostgreSQL のためのレプリケーションシステム - そのコンセプト

Slony-I: PostgreSQL のためのレプリケーションシステム - そのコンセプト

Slony-I: A replication system for PostgreSQL - Concept

Copyright (c) 2003-2004, PostgreSQL Global Development Group

訳注 (Sun Mar 14 2010): 本稿は、slony1-1-1.2.14 に同梱される "Slony-I: A replication system for PostgreSQL - Concept" からの、SIOS Technology, Inc. による翻訳です。原文と和訳との内容に差異が認められる場合には、原文の内容が優先されます。

著者 / Author

ヤン・ヴィーク (米国 ペンシルバニア州 ホーシャム Afilias USA INC.)

Jan Wieck (Afilias USA INC. Horsham, Pennsylvania, USA)

要約 / ABSTRACT

当文書では、Slony-I レプリケーションのデザインが目標とするところについて解説し、また、その実装について技術的に概観します。Slony-I は、PostgreSQL ORDBMS のための、次世代のレプリケーションシステムの一員となります(訳注: 今となっては古参です)。

This document describes the design goals and technical outline of the implementation of Slony-I, the first member of a new replication solutions family for the PostgreSQL ORDBMS.

1. Slony-I のデザインが目指すところ / Design goals

この章では、Slony-I の基本デザインが目指し、最終的に Slony-I の実装に反映されるであろうデザインについて概観します。

This chapter gives a brief overview about the principle design goals that will be met in final product.

Slony-I の開発における「大目的」は、マスター・スレーブ型のレプリケーションシステムを実現することです。ここでの要求としては、大規模なデータベースを、妥当な台数のスレーブへと複製するための諸機能や性能が挙げられます。そこで、PostgreSQL 用に既にリリースされているいくつかのレプリケーションシステムを分析してみたのですが、残念なことに、これらのシステムは大元のデザインの段階ではそれらの機能を実現することを計画して設計されてはいないため、事後的な機能追加では、それらの特性を実現できないことが分かりました。

The big picture for the development of Slony-I is to build a master-slave system that includes all features and capabilities needed to replicate large databases to a reasonably limited number of slave systems. The analysis of existing replication systems for PostgreSQL has shown that it is literally impossible to add a fundamental feature to an existing replication system if that feature was not planned in the initial design.

この章で定義されるのは Slony-I の中核的な機能ではありますが、初期段階のリリースではまだ実装されていないものがあるかも知れません。しかし仮に未実装であったとしても、対応するメタデータや管理構造を予め組み込んでおくことで、将来、すでに運用が始まっているシステムへの影響を最小限に抑えつつ、新機能を追加することができるようになるでしょう。

The core capabilites defined in this chapter might not all get fully implemented in the first release. They however need to be an integral part of the metadata and administrative structures of the system to be added later with minimal impact to a running system.

既存のデータベース・レプリケーションのシステムを調べてみて分かることは、「一つの実装で何でもできるようにしよう」という考え方は適切ではないということです。Slony-I がターゲットとしているのは、データセンターやバックアップサイトに置かれる類のデータベースなので、常時、全てのノードが利用可能状態にあることが想定されます。よって、特定のノードのダウンタイムが長時間に渡るような場合には、そのノードは、管理者の操作によってシステムから外されるか、あるいは無効化される必要があります。また、Slony-I が対象としない用途としては、たまにしか接続されないノード(外回りの営業マンのマシンのノードなど)の同期や、マルチマスターのシステム、同期レプリケーションなどが挙げられます。これらは、将来の Slony ファミリーの実装によって実現されるかも知れません(訳注: 同期レプリケーションを実現する「Slony-II」という話もありましたが、最近聞きません)。

The number of different replication solutions available supports the theory that "one size fits all" is not true when it comes to database replication. Slony-I is planned as a system for data centers and backup sites, where the normal mode of operation is that all nodes are available. Extended periods of downtime will require to remove or deactivate the node in question in the configuration. Neither offline nodes that only become available sporadic for synchronization (the salesman on the road) nor multimaster or synchronous replication will be supported and are subject to a future member of the Slony family.

1.1. マスターから複数のカスケードスレーブへの複製 / Master to multiple cascaded slaves

Slony-I システムの基本構造は、一つのマスターノードと、それに連なる一つ以上のスレーブのノードです。必ずしもすべてのスレーブノードが、マスターノードから直接的にレプリケーションデータを受け取る必要はありません。複製元からデータを受け取ったノードが、それをさらに次のノードに転送するように設定こともできます。

The basic structure of the systems combined in a Slony-I installation is a master with one or more slaves nodes. Not all slave nodes must receive the replication data directly from the master. Every node that receives the data from a valid source can be configured to be able to forward that data to other nodes.

それらの特長の背景には、以下の 3 つの意図があります。一つ目は、スケーラビリティの確保です。各ノードの中でもとりわけマスターノードは、クライアント・アプリケーションからの多数の更新トランザクションを受理しなければなりません。そのため、全てのスレーブノードからの、レプリケーションのためのクエリをこなすキャパシティには制約が生じます。この制約を踏まえつつ多数のリードオンリーのスレーブシステムを実現するためには、カスケードでの構成が可能である必要があります。

There are three distinct ideas behind this capability. The first is scalability. One database, especially the master that receives all the update transactions from the client applications, has only a limited capability to satisfy the slave nodes queries during the replication process. In order to satisfy the need for a big number of read-only slave systems it must be possible to cascade.

二つ目は、バックアップサイトへの接続に必要とされるネットワークの帯域を小さくすることです。これによって、複数のスレーブを遠隔地に配置することが可能となります。

The second idea is to limit the required network bandwidth for a backup site while keeping the ability to have multiple slaves at the remote location.

3 つ目は、フェイルオーバを実現することです。一つのマスターと複数のスレーブからなる構成においては、マスターがフェイルした時点で、すべてのスレーブノードが同一の同期状態にあるという状況は、まず有り得ません。複数スレーブの内の 1 つのノードがマスターに昇格できることを保証する不可欠な条件は、残存システムのすべてが、お互いにデータの状態について同調できることです。一旦コミットされたトランザクションをロールバックすることはできませんので、ここで言う「状態」とは、残存するスレーブノード内での、最も直近の同期状態を指します。もしも、直近の状態にあるノードが新しくマスターになるノードとは異なるノードである場合には、それ以外の全ノードとの差分を迅速に生成し、それを新しいマスターノードに適用してから、マスター昇格を行なう必要があります。

The third idea is to be able to configure failover scenarios. In a master to multiple slave configuration, it is unlikely that all slave nodes are exactly in the same synchronization status when the master fails. To ensure that one slave can be promoted to the master it is necessary that all remaining systems can agree on the status of the data. Since a committed transaction cannot be rolled back, this status is undoubtly the most recent sync status of all remaining slave nodes. The delta between this one and every other node must be easily and fast generated and applied at least to the new master (if that's not the same system) before the promotion can occur.

1.2. 稼働中の環境への導入と構成 / Hot installation and configuration

Slony-I レプリケーション・システムへの要件として、稼働中の実運用環境へのインストール及びアンインストールが、クライアント・アプリケーションを停止することなく行なえることが挙げられます。つまり、稼働状態のまま、マスターシステム上で初期設定を行ない、スレーブ側の設定をしたのち、スレーブの初期データをマスターの状態へ追いつかせるためのコピーをする機能が必要となります。

It must be possible to install and uninstall the entire replication system on a running production database system without stopping the client application. This includes creating the initial configuration on the master system, configuring one or more slaves, copying the data and catching up to a full running master-slave status.

また、設定の変更に関わる要件の一つとして、カスケードされたスレーブノードが、データの供給元を迅速に変更できることが挙げられます。とりわけ、先の項で触れたフェイルオーバの際に、この点が重要になります。たとえば、第一レベルのスレーブをマスターに昇格させる際には、それ以外の各第一レベルスレーブの設定を、新しいマスターからレプリケートするようにリダイレクトさせたのちに、新しいマスターのワークロードを下げるために、カスケードされているスレーブの一部、あるいは全てを、新マスター以外の第一レベルのスレーブからレプリケートするように設定すべきでしょう。

訳注: 要するに、フェイルオーバを機に特定ノードに負荷が集中してしまうと良くないので、柔軟にデータ授受の関係を変えられるようにしましょうということです。

Changing the configuration also includes that a cascaded slave node can change its data provider on the fly. Especially for the failover scenario mentioned in the former section it is important to have the ability to promote one of the first level slaves to the master, redirect the other first level slaves to replicate from the new master and lower the workload on the new master by redirecting some or all of its cascaded slaves to replicate from another first level slave.

さらに、稼働状態にあるデータベースへのインストールと構成変更を可能とする機能無くしては、レプリケーションソフトウェア自身を、新しいバージョン(現在インストールされているものと、メタデータの点で互換性を持たない)へアップグレードすることはできません。

Hot installation and configuration change is further the only way to guarantee the ability to upgrade the replication software itself to a new version that is incompatible with the existing one in its metadata.

しかし、たとえこの機能が実現したとしても、スレーブのアップグレードの際には、スレーブの一旦停止は必要となります。将来的に提供されるのは、新しいバージョンを古いバージョンと併存して入れる機能となり、それによって、新しいスレーブが構築・開始されてから、既存の古い方をシステムから削除できるようになるでしょう(訳注: 現行実装で試していないので、実のところはどうなのか、やってみてから追記します)。

Even if this is given, upgrading the slaves will not work without interrupting the slave. What will be provided at least is the ability to install a new version in parallel to the old one, so that a new slave can be created and started before an existing one gets removed from the system.

1.3. データベース・スキーマの変更 / Database schema changes

スキーマ変更を複製する機能については度々議論されるところではあるのですが、あいにく、これを実装するために必要なフックを提供しているデータベースシステムは、あまりありません。PostgreSQL にしても、スキーマ変更の際に呼ばれるトリガを定義する機能を持ち合わせていないため、透過的な方法では、スキーマ変更を複製することは不可能です。PostgreSQL のコアのシステムに手を入れて良いのであれば話は別ですが。

Replicating schema changes is an often discussed problem and only very few database systems provide the necessary hooks to implement it. PostgreSQL does not provide the ability to define triggers called on schema changes, so a transparent way to replicate schema changes is not possible without substantial work in the core PostgreSQL system.

さらに言うと、大抵の場合、データベース・スキーマの変更は決して、単発かつ隔離された DDL 文を、稼働中のシステム上の任意のタイミングで実行すれば良いというものではありません。むしろそれらの変更は、一連の DDL 文や DML 文からなり、複数のデータベースオブジェクトや多数のデータ変更を伴います。例をあげるならば、新しく追加されたカラムを初期値で初期化する更新、などです。

Moreover, very often database schema changes are not single, isolated DDL statements that can occur at any time within a running system. Instead they tend to be groups of DDL and DML statements that modify multiple database objects and do mass data manipulation like updating a new column to its initial value.

Slony-I レプリケーション・システムでは、いずれは、SQL スクリプトの実行を、レプリケーションプロセスの一部として組み込む機能を持つでしょう(訳注: 現在ではすでに、slonik で言うと "execute script" コマンドとして実装されています)。

The Slony-I replication system will have a mechanism to execute SQL scripts in a controlled fashion as part of the replication process.

1.4. 複数バージョンのデータベース / Multiple database versions

データベース自体のバージョンを上げられることを担保するために、このシステムは、異なる PostgreSQL バージョン間でのレプリケーションができるようにする必要がありま

To aid in the process of upgrading from one database version to another, the system must be able to replicate between different PostgreSQL versions. す。

マスターのデータベースのアップグレードは、スレーブへのフェイルオーバによって実現されます。Slony-I のような純粋な非同期マスター・スレーブシステムでは、決して、トランザクションロスなしでのフェイルオーバは実現できません。コミットされたトランザクションが欠損なく確実にフェイルオーバさせるためには同期でのレプリケーションが必要ですが、これは Slony-I では、将来に渡ってもサポートされることはありません。そのため、マスター変更のために強制フェイルオーバをする際には、クライアントアプリケーションから見ると、短い断絶が必要になります。その断絶の間に、スレーブシステムはマスターのデータに追いつき、マスターに昇格してからのち、クライアントからの要求を新マスターとして受け付け始めます。

A database upgrade of the master must be doable by failing over to a slave. A pure asynchronous master slave system like Slony-I will never be able to provide the ability to failover with zero transaction loss. True failover with zero loss of committed transactions is only possible with synchronous replication and will not be supported by Slony-I. Therefore, this administrative forced failover for the purpose of changing the master will need brief interruption of the client application to let the slave system catch up and become the master before the client resumes work, now against the promoted new master.

1.5. バックアップと PITR(ポイント・イン・タイム・リカバリ) / Backup and point in time recovery

訳注: この項のフィーチャは、Slony-I-2.0.3 の時点でも、まだ実装されていない模様です。また、PostgreSQL 自体がデータベース・クラスタのレベルでの PITR 機能を備えたのは 2005 年の ver. 8.0 からであり、原文の執筆時点では、まだ利用できていませんでした。

何故バックアップやリカバリがレプリケーション・システムのトピックとして挙がってくるのかは、理解しづらいかも知れません。Slony-I のデザインの一主題としてそれらを取り上げる理由は、一つには、PostgreSQL データベース・システムが、PITR リカバリの機能を完全に欠いているからであり、もう一つには、フェイルオーバを含むシステムデザインとしては、アプリケーションによるデータ破壊に対処できるようにしなければ、機能として不完全だと言わざるを得ないからです。

It is not necessarily obvious why backup and recovery is a topic for a replication system. The reason why it is subject to the design of Slony-I is that the PostgreSQL database system lacks any point in time recovery and a system design that covers failover would be incomplete without covering an application fault corrupting the data.

技術的なデザインについては後述しますが、スレーブシステムをバックアップのために用いることは、比較的容易です。さらにいずれは、それぞれのスレーブ(カスケードのスレーブを持っていようがいまいが)が、一定の遅延の後でデータ変更を適用することもできるようになるでしょう。高可用性が目的であるならば、バックアップをリストアする時間や PITR をする時間を待っていることはできません。現在入手可能なバックアップメディアは、十分には速くありません。そこで、レプリケーションデータを 1 時間遅れで適用し続けるスレーブがあったとしましょう。このスレーブは、論理的に、過去 60 分以内の任意の時点での状態を以て、マスターに昇格することができます。少なくとも 1 台の他のノード(マスターか、あるいは遅延なしのスレーブ)が過去一時間分のログ情報を持っていてくれるならば、バックアップノードは特定の時点へ追いつくことができますし、さらに、マスターに昇格することも可能です。新マスターが、旧マスターが実際にクエリを処理した速さよりも速くレプリケートすることができるとすれば(そうでなければ追いつくことはできないわけですが)、遅延分の時間よりも短い時間で追いつくことができます。

The technical design presented later in this document will make it relatively easy to use one or more slave systems for backup purposes. In addition it will be possible to configure single slaves with or without cascaded slaves to apply replication data after a delay. In high availability scenarios there is usually no time to restore a backup and do a point in time recovery. The affordable backup media are just not fast enough. A slave that applies the replication data with a 1 hour delay can be promoted to the master at logically any point in time within the past 60 minutes. Provided at least one other node (the master or any other node that does not replicate with a delay) has the log information for the last hour and is available, the backup node can be instructed to catchup until a specific point in time and then be promoted to the master. Assuming that the node can replicate faster than the master was able to work (how does it keep up otherwise), this would take less time than the delay it had.

2. 技術概観 / Technical overview

この章では、Slony-I の各コンポーネントと、論理的操作について解説します。

This chapter explains the components and the logical operation of Slony-I.

2.1. ノード、セット、フォワード / Nodes, Sets and forwarding

Slony-I レプリケーション・システムは、テーブルと、シーケンスの通番を複製することができます。シーケンス番号の複製には若干の困難が伴うため、2.3 でより詳細に見て行きます。

The Slony-I replication system can replicate tables and sequence numbers. Replicating sequence numbers is not unproblematic and is discussed in more detail in section 2.3.

テーブルとシーケンスのオブジェクトは、論理的には、「セット」内にグループ化されます。各セットはオブジェクトのグループを含み、それらのオブジェクトは、同一のマスターをオリジン(起源)としている他のオブジェクトからは独立しています。簡潔に言うならば、外部キー制約やシーケンスによって相互に関係するテーブルどうしは、同一のセットに含まれる必要があるということです。

Table and sequence objects are logically grouped into sets. Every set should contain a group of objects that is independant from other objects originating from the same master. In short, all tables that have relationships that could be expressed as foreign key constraints and all the sequences used to generate any serial numbers in these tables should be contained in one and the same set.

図 1 (Figure 1) に示されるレプリケーションの構成では、2 つのデータセットのそれぞれが、異なるオリジンノードを持っています。両データセットを Node C へレプリケートする場合、Node C は、必ずしも Set 1 のオリジンと、直接に通信をする必要はありません。この例においては、各ノードは完全な冗長性を持っています。見ての通り、たとえ Node C がフェイルしたとしても、Set 1 と Set 2 のマスターは残ります。また、Node A がフェイルした場合には、Node B が両セットのマスターに昇格します。少々分かりづらい状況は、Node B がフェイルする場合です。

Figure 1 illustrates a replication configuration that has 2 data sets with different origins. To replicate both data sets to Node C it is not required that Node C really communicates with the origin of Set 1. This scenario has full redundancy for every node. Obviously if Node C fails, the masters of Set 1 and Set2 are still alive, no problem. If Node A fails, Node B can get promoted to the master of both sets. The tricky situation is if Node B fails.

Node B がフェイルすると、Node C が Set 2 のマスターに昇格する必要が生じるのと同時に、引き続き Set 1 を Node A からレプリケートしなければなりません。それを実現するために Node A が知っておかなければならないことは、Node C 自体の情報と、Node C が Set 1 を購読しているということです。Slony-I のルールとして、各ノードはレプリケーションのためのログ情報を保持し続け、そして、そのセットのすべての購読ノードがデータをレプリケートし終えた時点で、ようやく削除ができるようになります。

In the case Node B fails, Node C needs to get promoted to the master of Set 2 and it must continue replicating Set 1 from Node A. For that to be possible, Node A must have knowledge about Node C and its subscription to Set 1. Generally speaking, every node that stores replication log information must keep it until all subscribers of the affected set are known to have replicated that data.

Slony-I では、ロジックを単純にするために、全てのノード・セット・購読関係の構成情報は、全てのノードに伝達され、そして保持されます。しかしながら、当該ノードで購読していないセットに関しては、そのセットがノードのデータベース上には存在すらしないため、そのノードは、それらのセット内にどのようなテーブルやシーケンスが含まれるかの情報を持っていません。

To simplify the logic, the configuration of the whole network with all nodes, sets and subscriptions will be forwarded to and stored on all nodes. Because the sets, a node is not subscribed to must not even exist in its database, this does not include the information about what tables and sequences are included in any specific set.

訳注: sl_set を見れば、クスラタ内にセットが存在することは分かりますが、subscribe をしない限り、sl_table や sl_sequnece に、テーブルやシーケンスの情報は入りません。当然、実体もあるかどうかは分かりません。

2.2. データベースの活動のロギング / Logging database activity

Slony-I では AFTER ROW トリガでレプリケーションが実現されています。トリガは、NEW と OLD の行を解析することで、SQL 文が実行された際に、実際に各行に加えられた変更を表すログを再構築します。ログ内で特定の行を一意に決定するために、複製対象のテーブルは何らかの UNIQUE 制約を持っている必要があります。これは、任意の型からなる複合キーでも構いません。一意なキーが見つからない場合には、Slony-I は、インストールの際に int8 のカラムをテーブルに追加します(訳注: Slony-I-2 以降、自動的には追加されなくなったと聞きました。後で見ておきます)。UPDATE イベントの際に変更が加えられなかったフィールドに関する情報については、ログ文に含まれることはありません。この方式では、確かにレプリケーションサイクルの間に蓄積されるログ情報は増加してしまいます。しかし、既存のレプリケーションシステムに関する調査を行なった結果、どのテーブルが複製されるべきかの情報を保持し、最終の値をレプリケーション時にフェッチする方式には、いくつかの欠点があることが分かります:

Slony-I will be an AFTER ROW trigger based replication system that analyses the NEW and OLD rows to reconstruct the meaningful pieces of an SQL statement representing the change to the actual data row. To identify a row in the log, the table must have some UNIQUE constraint. This can be a compound key of any data types. If there is none at all, the Slony-I installation process needs to add an int8 column to the table. Unmodified fields in an UPDATE event will not be included in the statement. Some analysis of existing replication methods has shown that despite the increase of log information that must be stored during replication cycles, this technology has several advantages over a system that holds information about which application tables need to be replicated, but will fetch the latest value at the time of replication from the current row.

安定性 / Stability: 時系列情報が失われてしまうと重複キーの衝突が発生し得るのですが、これは容易には解決できません。最も単純なケースとして以下に例示するのは、2 つの行の交換が行なわれる場合の、ユニーク制約を持つフィールドにおける問題です:

There are possible duplicate key conflicts that are not easy solvable when losing history information. The simplest case to demonstrate is a unique field where two rows swap their value like

UPDATE table SET col = 'temp' WHERE col = 'A';
UPDATE table SET col = 'A' WHERE col = 'B';
UPDATE table SET col = 'B' WHERE col = 'temp';

値 'temp' に対する一手間のステップを省いてしまうと、レプリケーション・エンジンとしては、この更新の複製を行うことが可能となる順序が分からなくなってしまいます。

Without doing the extra step over the 'temp' value, there is no order in which the replication engine can replicate these updates.

分割 / Splitting: Slony-I は、DB のアクティビティの複製を、数秒間分のワークロードに相当する小さな単位に分割して実行します (後ほど 2.4.1 で詳述します)。この分割は、シリアライザブルな (直列化が可能な) 2 つのスナップショットのビジビリティ (可視性) 境界に基づいて行なわれます。そのため、スレーブ側では、とある整合性のとれた状態から、他の整合状態へと一気に跳ぶ際に、マスターにおける複数のトランザクションが一括で発生したように見えます。ヒストリ情報が失われてしまうとこの処理は不可能であり、できることはせいぜい、最後の同期ポイントから現在の状態へとジャンプすることだけになります。その場合、何らかの理由でレプリケーションがしばらく停止してしまうと、その間にマスターでなされた全変更に追いつくためには、極めて巨大なトランザクションを一回で適用しなければならなくなります。その際には同時に、前述した通り、重複キーが発生するリスクも増大してしまいます。

Slony-I will split the entire amount of replication activity into smaller units covering a few seconds of workload as described in section 2.4.1. This will be done on the visibility boundaries of two serializable transactions. So the slave systems will leap from one consistent state to another as if multiple master transactions would have been done at once. Without history information this is not possible and the slave only has the chance to jump from its last sync point to now. If it was stopped for a while for whatever reason, it must catch up in one big transaction covering the whole work done on the master in the meantime, increasing the duplicate key risk mentioned above.

レプリケーション・データの遅延適用によるポイント・イン・タイム・スタンバイ機能は、ログ分割の機能を必要とします (1.5 にて既述)。

The point in time standby capability via delayed application of replication data, described in 1.5., needs this splitting as well.

フェイルオーバ / Failover:
1 台のマスターと複数台のスレーブからなる構成では、マスターがフェイルした際に、どのスレーブが一番「進んで」いるかを判別することは容易なのですが、2 つのスレーブの間の更新行の差分を得ることは、まず不可能となります。マスターがフェイルしたら、いずれかのスレーブをマスターに昇格させなければなりませんが、それ以外のスレーブは、新しいマスターに対して再同期しなければならないのです。

While it is relatively easy to tell in a master to multiple slave scenario which of the slaves is most recent at the time the master fails, it is nearly impossible to tell the actual row delta between two slaves. So in the case of a failing master, one slave can be promoted to the master, but all other slaves need to be re-synchronized with the new master.

パフォーマンス / Performance:
ローテートされるログテーブルにログ情報を蓄積することによって、レプリケーションエンジンが 1 ステップのレプリケーションに必要なデータを取得する際に、たった 1 つのテーブルから、少ないクエリでデータ取得が可能になります。これに対して、複製時にアプリケーション・テーブルから現在の状態を取得するシステムの場合には、「複製されるテーブルごとに」同数のクエリを発行せねばならないうえに、それらのクエリは、そのテーブルとアプリケーションデータテーブルとの結合を必要とするでしょう。これでは明らかに、複製対象となるテーブル数が増加するのに比例して、パフォーマンスは悪化します。分割不能な完全差分の適用が必要となってしまうため、せっかくの PostgreSQL の、メモリ上でのハッシュ結合も用いられません。その結果、レプリケーション・システムは、マスターの負荷が落ち着くまでは、最新状態に追いつくことができなくなるでしょう。

Storing the logging information in one or very few rotating log tables means that the replication engine can retrieve the actual data for one replication step with very few queries that select from one table only. In contrast to that a system that fetches the current values from the application tables at replication time needs to issue the same number of queries per replicated table and these queries will be joining the log table(s) with the application data table. It is obvious that this systems performance will be reverse proportional to the number of replicated tables. At some time the complete delta to be applied, which can not be split as pointed out already, will cause the PostgreSQL database system to require less optimal than in memory hash join query plans to deal with the number of rows returned by these queries and the replication system will be unable to ever catch up unless the workload on the master drops significantly.

通常環境下では、ログは 1 つのテーブルに集積されつつ、一定時間ごとに削除され、VACUUM されます (2.4.4. にて後述)。空き領域を有する妥当な大きさのテーブルへの挿入であれば、INSERT の操作は、空のテーブルに対して追加拡張される場合よりも優れたパフォーマンスを示します。これはなぜかというと、PostgreSQL での空き領域の管理では、複数バックエンドが、同時実行で、個々のブロックに対して新たなタプルを追加することが可能だからです。さらに、末尾への追加拡張は、既存ブロックの再利用に比べると高コストです。なぜならば、これらのブロックは OS のファイルシステムのメタデータでのファイルサイズ増加を伴ないますので、そのデータは、その時点では絶対にキャッシュ上には見つからないからです。Slony-I では、ログの保存先テーブルを切り替える機構も備えています。これは、ログテーブルが既に、妥当なサイズ以上に大きくなってしまっていて、VACUUM FULL 以外では小さくすることができない場合に、VACUUM FULL に起因するテーブル排他ロックが、クライアントアプリケーションの問い合わせを完全に阻害してしまうことを避けるためです。

The log will under normal circumstances be collected in one log table, deleted from there periodically and the table vacuumed (see section 2.4.4.). A reasonably large table with sufficient freespace has a better performance on INSERT operations than an empty table that gets only extended at the end. This is because the free space handling in PostgreSQL allows multiple backends to simultaneously add new tuples to different blocks. Also extending a table at the end is more expensive than reusing existing blocks as those blocks can never be found in the cache and need filesystem metadata changes in the OS due to increasing the file size. A log switching mechanism to another table will be provided for the case that a log table had once grown out of reasonable size, so that it is possible to shrink it without doing a VACUUM FULL which would cause an exclusive lock on the table, effectively stopping the client application.

ログの各行に含まれる情報は、現在のトランザクション ID、ローカルノードの ID、影響を受けたテーブルの ID、ログのアクション・シーケンス番号、そして、スレーブ側で同様の変更を再現するための SQL 文を再構築するのに必要な情報です。アクション・シーケンスの取得は AFTER ROW トリガによってなされますので、その番号の昇順での割り振りは自動的に、同一テーブルへのコンカレントな更新と衝突することなしに行なわれます。その順序は、必ずしも更新を実際に行なった順序と同一になる必要はありませんし、可視化された順序、すなわちコミットされた順序である必要もありません。しかしながら、この論理的な昇順で行なわれるトランザクションの順序で実行された文は、可視になる度にグループ化され、スレーブ側で同じ結果をもたらすでしょう。これを、"agreeable order (訳注: 良好な順序、とでも?)" と呼びます。

Each log row will contain the current transaction ID, the local node ID, the affected table ID, a log action sequence number and the information required to reconstruct the SQL statement that can cause the same modification on a slave system. Since the action sequence is allocated in an AFTER ROW trigger, its ascending order is automatically an order that is not in conflict with the order in which concurrent updates happened to the base tables. It is not necessarily the exact same order in which the updates really occured, and it is for sure not the order in which those updates became visible or in other words their transactions committed. But statements executed in this order within logically ascending groups of transactions, grouped by the order in which they became visible, will lead to the exact same result. This order is called agreeable order.

2.3. シーケンスの複製 / Replicating sequences

PostgreSQL のシーケンス生成機能は、同時実行性の点で、とてもよく最適化されています。そのため、シーケンスで保証されるのは、重複する番号が振り出されないということだけです。シーケンス生成はロールバックをしないので、ギャップが生じ得ます。また、別の問題として、シーケンスにはトリガをかけられない点が挙げられます。

Sequence number generators in PostgreSQL are highly optimized for concurrency. Because of that they only guarantee not to generate duplicate ID's. They do not roll back and can therefore generate gaps. Another problem is that triggers cannot be defined on sequence numbers.

PostgreSQL のシーケンスは 64 ビットの整数です。そこで一つの案として、全数値域を複数セグメントに分けてしまい、各ノードにそれらを割り当て、いずれマスターに昇格させた際には、ユニークなレンジを使わせるということもできます。このようにしてシーケンスは、レプリケーションのプロセス内では無視してしまうことも可能です。欠点としては、バックアップ/リストアの際に、看過できない不整合を起こし得ることや、再調整を怠ったまま間違ったリストアをしてしまうリスクが高いことが挙げられます。

Since sequences in PostgreSQL are 64 bit integers, it would be quite possible to split the entire available number range into multiple segments and assign each node that will eventually be promoted to the master its own unique range. This way, sequences can be simply ignored during the replication process. The drawback is that they cannot be ignored in the backup/restore process and the risk of restoring the wrong backup without re-adjusting the sequences is high.

別の可能性としては、ユーザ定義の関数を用いてシーケンスを置き換え、番号はテーブル内に保持することが考えられます。しかし、これでは同時実行性が損なわれ、クライアントにとって、シーケンス生成が大きなボトルネックとなってしまいます。

Another possibility is to use a user defined function and effectively replace sequences by a row held in a replicated table, destroying thus the concurrency and making sequences a major bottleneck in the entire client application.

さらに別のアプローチとしては、そもそもシーケンスを複製せずに、スレーブがマスターに昇格する時点ですべてを調整するという手段もあります。しかしこれには、少なくとも一回、シーケンスで生成された値を持っている全テーブルについてフルスキャンが必要となります。これでは、フェイルオーバのプロセスに、長い時間がかかってしまいます。

Yet another approach seen is not to replicate sequences, but to adjust them at the time a slave would be promoted to master. This requires at least one full table scan on every table that contains sequence generated values and can mean a significant delay in the failover process.

Slony-I で実際に採用されているのは、以下のような手順です。シーケンスの番号を生成するには、標準的には nextval() と setval() 関数を用いますが、pg_proc カタログのそれらのエントリを、別の名前と別の OID に変更してしまうのです。かわりにカスタム版の関数で置き換え、それらが、オリジナルの nextval(), setval() を呼ぶと同時に、設定テーブルを見て、シーケンスのレプリケーションが行われているかどうかをチェックするようにします。シーケンスのレプリケーションが行なわれていたら、これらの関数は、シーケンス変更ログのテーブルに、複製のためのアクションを挿入します。ログテーブルには、決して更新は行なわれませんし、クリーンアップのプロセスが消去するのは過去のログエントリだけですので、このプロセスを行なっても、シーケンス取得のトランザクションによって同時実行性が損なわれることはありません。アボートされたトランザクションでは、確保されたシーケンスが失われますが、これは無視して構いません。なぜならば、次回のシーケンスの確保の際には、それらの数は飛ばされるからです。

The approach Slony-I will take is a different one. The standard function that generates sequence numbers, nextval(), as well as setval(), will be moved out of the way by creating a new pg_proc catalog entry with another name and Oid for it. Their places will be taken by new custom functions that will call the original nextval() or setval() function and then check the configuration table if the sequence is replicated. In the case of sequence replication, the function will insert a replication action row into the log table. Since no updates are ever done to the log table and the cleanup process only removes log entries that are in the past, this will not block concurrent transactions from allocating sequences. The fact that an aborted transaction will loose the allocated sequence can be ignored because it will be skipped on the next allocation anyway.

訳注: 現行版では実装が異なります。テーブルの場合には、トリガを用いて、同期で sl_action_seq をインクリメントしつつ sl_log_* テーブルへとログを記録しますが、これに対してシーケンスの場合には、slon の同期スレッドが sync_interval ミリ秒ごとに関数 createEvent() を呼び出して SYNC イベントを生成し、その際、sl_seqlastvalue ビューをポーリングして、変更があったら sl_seqlog へログを書きこむようになっています。つまり、asynchronous なタイミングでは何もしていません。それと、スレーブ側でもシーケンスの取得ができてしまいます(次回同期の時点で巻き戻されますが)。

スレーブでの処理は、シーケンス調整の際に連番を戻してしまわないように注意深く行なう必要があります。なぜならば、アクションレコードのシーケンスでは、agreeable order を保証する副作用(アプリケーションテーブルの行ロック)がありますが、シーケンスの場合にはこれが存在しないからです。シーケンス番号の確保が、論理的に BEFORE ROW トリガの前のタイミングで起きると、置き換えられた nextval() の中ではオリジナルの nextval() 呼び出しとログ記録の挿入の間で競合状態が発生する可能性があり、これは同時実行性の観点から、シリアライズするべきではないと考えます。

The slave must be carefull during the replication not to adjust the sequence number backwards, because the side effect that guarantees the agreeable order of action record sequences, the row lock on the applications table, does not exist for sequences. The allocation of sequence numbers happens logically at a time even before a BEFORE ROW trigger would fire and inside of our replacement nextval() function there is a race condition (the gap between calling the original nextval() and inserting the log record) that we do not want to serialize for concurrency reasons.

2.4. ノードデーモン / The node daemon

Slony-I では、レプリケーションに加わっている各データベースを、「ノード」と呼びます。各データベースは、必ずしも別々のサーバ上で動作している必要はありません。さらには、同一の postmaster 上で動いていても構いません。2 つの異なるデータベースは、2 つの異なるノードとして扱われます。

In Slony-I every database that participates in a replication system is a node. Databases need not necessarily reside on different servers or even be served by different postmasters. Two different databases are two different nodes.

レプリケーションシステム内の各データベースごとに、「slon」と呼ばれるノードデーモンが起動されます。このデーモンは「レプリケーションエンジン」であり、マスター・スレーブ双方として働く、ハイブリッドなプログラムです。Slony-I のシステムでは、ノードデーモンのレベルでは、マスターであるかスレーブであるかの区別は、あまり重要ではありません。なぜならば、各ノードの役割は、データベースのレベルではなく、セットのレベルで決定されるからです。slon は、以下に述べる役割を担います。

For each database in the replication system, a node daemon called Slon is started. This daemon is the replication engine itself and consists of one hybrid program with master and slave functionality. The differentiation between master and slave is not really appropriate in Slony-I anyway since the role of a node is only defined on the set level, not on the database level. Slon has the following duties.

2.4.1. ログデータの分割 / Splitting the logdata

ログデータを論理的昇順に、いくつかのグループに分割することは、それほど難しい処理ではありません。Slony-I のデーモンは、設定されたタイムアウト時間が経過するごとに、ローカルに保持されている、ログのアクション・シーケンス番号が変わったかどうかをチェックします。もしこれが変わっていたら、SYNC イベントを生成します。システムが生成する全てのイベントは、シリアライザブルな一つのトランザクションの中で生成され、一つのオブジェクトをロックします。よって、イベントの順序は、イベントが生成・コミットされた順序通りになっていることが保証されます。

Splitting the logdata into groups of logically ascending transactions is much easier than someone might imagine. The Slony-I daemon will check in a configurable timeout if the log action sequence number of the local node has changed and if so, it will generate a SYNC event. All events generated by a system are generated in a serializable transaction and lock one object. It is thus guaranteed that their event sequence is the exact order in which they are generated and committed.

イベントは、メッセージコードとペイロード情報と共に、このイベントを生成したシリアライザブルなトランザクションのスナップショット情報を保持します。2 つの昇順に並んだ SYNC イベントの間にコミットされたすべてのトランザクションは、以下のように定義できます(訳注: "xip" は "transactions in progress" の意):

An event contains among the message code and its payload information the entire serializable snapshot information of the transaction, that created this event. All transactions that committed between any two ascending SYNC events can thus be defined as

SELECT xid FROM logtable
    WHERE (xid > sync1_maxxid OR
          (xid >= sync1_minxid AND xid IN (sync1_xip)))
    AND   (xid < sync2_minxid OR
          (xid <= sync2_maxxid AND xid NOT IN (sync2_xip)));

2.4.5 で後述しますが、実際に用いられるクエリは、もう少し複雑です。しかし、原理としては、このようにシンプルなものとなっています。ローカルノードのデーモンは、ローカルのログ・アクションのシーケンスだけをチェックし、シーケンスに変更があるようならば、ログ行を挿入して NOTIFY を生成します。

The real query used in the activity described in section 2.4.5. is far more complicated. Yet the general principle is this simple and after all, the daemon on the local node only checks the local log action sequence, inserts a row and generates a notification if the sequence has changed.

2.4.2. メッセージの交換 / Exchanging messages

Slony-I システム上のすべての設定変更(たとえば、ノードの追加、セットの購読開始、同・停止、セットへのテーブル追加、等々)は、システム内を、イベントの形で伝達されます。イベントの生成は、イベント情報をテーブルへ挿入することで行なわれ、LISTEN をしているすべてのリスナへ NOTIFY が発行されます。SYNC メッセージも、これと同様のメカニズムによって伝達されます。

All configuration changes like adding nodes, subscribing or unsubscribing sets, adding a table to a set and so forth are communicated through the system as events. An event is generated by inserting the event information into a table and notifying all listeners on the same. SYNC messages are communicated with the same mechanism.

Slony-I のシステム設定には、全てのノードについて、そのノードが、どのノードに対してどのイベントを問い合わせるかの情報が含まれています。

The Slony-I system configuration contains information for every node which other it will query for which events.

図 2 (Figure 2) の、5 つのノードからなるイベントフローでは、ノード間の直接の接続は下記の 4 つだけです:

Figure 2 illustrates the event flow in a configuration with 5 nodes, where direct connections only exist between the following combinations of nodes.

  • NodeA ←→ NodeB
  • NodeA ←→ NodeC
  • NodeC ←→ NodeD
  • NodeC ←→ NodeE

いずれのデーモンも、イベント提供元のすべてのノードに対して、リモート DB 接続を張ります(図 2 から見て取れるように、イベント提供元のノードは、必ずしもイベントのオリジンノードではありません)。デーモンは、PostgreSQL の LISTEN/NOTIFY の仕組みを利用して、相互にイベント生成について通知しあいます。

Every daemon establishes remote database connections to the nodes, from where it receives events (which as shown in figure 2 is not necessarily the event origin). The daemons use the PostgreSQL LISTEN/NOTIFY mechanism to inform each other about event generation.

新しいイベントを受け取ると、デーモンプロセスはそのイベントを処理するトランザクション内で、今度は自分自身のイベントテーブルへと、そのイベントを挿入します。イベントはこのように転送され、あるイベントがチェーン上の次の受け手ノードに到達した時点では、そのイベントに必要な全てのデータは、確実に転送元ノード上に保存されており、受け手側から利用可能な状態にあることが保証されます。

When receiving a new event, the daemon processes it and in the same transaction, inserts it into its own event table. This way the event gets forwarded and it is guaranteed, that all required data is stored and available on the forwarding node when the event arrives on the next receiver in the chain.

ノード D や E 上で生成されたイベントは、システム上で数ノードを経て移動してから、初めてノード B で見えるようになります。SYNC メッセージを含むいくつかのイベントは、イベントオリジンとオリジンを同じくするセットを購読していて初めて、重要性が生じます。

The fact that an event generated on node D or E will travel a while before it is seen by node B is good. Events including SYNC messages are only important for any node if it is subscribed to any set that originates on the same node, the event originates from.

ノード A をオリジンとするデータセットがあったとして、今それを、ノード B と ノード C が購読しているとします。ノード B, ノード C ともに、フォワード(転送)はオンになっています。このデータセットは、ノード D も購読しています。実際の購読イベントは、データセットのオリジンノードであるノード A で生成され、各ノード間で伝播し、セットにある全購読者へと伝わります。もし全ての購読者に伝わらないとなると、ノード B とノード C は、(トランザクションのシーケンスの)時間上のいつの時点でノード D がセットを購読し始めたかが分かりませんし、また、D へと転送すべき複製データを保持する必要があるのがどうかが分からなくなってしまいます。ノード D が購読を開始して、ノード C のイベントキューを参照し SYNC イベントを得た時点で、ノード C はもう既にその SYNC イベント以前のすべての複製の差分を適用済みであることが保証されます。そしてノード C は、ノード D がこれ以降、SYNC イベントに対応する更新差分を必要とすることを知っていることになります。

We assume a data set originating on node A that is currently subscribed on nodes B and C, both with forwarding enabled. This data set now should be subscribed by node D. The actual subscribe event must be generated on node A, the origin of the data set, and travel within the flow of SYNC events to all subscribers of the set. Otherwise, node B and C would not know at which logical point in time node D subscribed the set and would not know that they need to keep replication data for possible forwarding to D. When node D receives the event by looking at node C's event queue, it is guaranteed that C has processed all replication deltas until the SYNC event prior to this subscribe event and that C currently knows that D possibly needs all following delta's resulting from future SYNC events.

ノード C と同様、ノード B もまた、イベントフローの論理的な意味での同一時点で、ノード D による購読開始のメッセージを受け取ります。そして、この時点以降ノード B は、C がフェイルした時に備えて、差分データを保持する必要があることを了解します。この差分が利用されるケースは、ノード C が、データのスナップショットや購読開始のメッセージ自体をノード D に渡す以前に落ちてしまい、ノード D がノード B を、代わりのデータプロバイダとして設定し直す場合です。

Likewise will node B receive the subscribe message at the same logical point in time within the event flow and know, that it from this moment on has to keep delta information for the case that node C might fail at any time, even before it would be able to provide the current data snapshot or even the subscribe message itself to D and D would be reconfigured to talk to B as a substitute provider.

ちなみに、図 2 の、ノード A をオリジンとするセットの構成は、著者がプロトタイプを開発する際に用いていた構成です。ノード A が継続的に、オンラインでアプリケーションから書き込みアクセスされてる間でも、Slony-I をインストール・スタートすることが可能でした。

As a side note, the configuration in figure 2 with a set originating on node A is the very setup the author used during the development of the prototype. The entire configuration can be installed and started while node A is constantly online and write accessed by an application.

2.4.3. イベントのコンファーム / Confirming events

イベント型の大半は、設定変更に関するものです。例外は、SYNC と SUBSCRIBE イベントで、これらは後ほど 2.4.5, 2.4.6 で詳述します。

The majority of event types are configuration changes. The only exceptions are SYNC and SUBSCRIBE events covered more detailed in sections 2.4.5. and 2.4.6.

設定変更のイベントは、必要な情報をたずさえてノード間を伝播し、イベントデータ行内のローカル設定を変更します。このようにして、それぞれ変更するデータの多い少ないはありますが、Slony-I の制御情報テーブル内の行の保存や削除は行なわれます。

Configuration change events carry all necessary information to modify the local configuration information in the event data row. Processing consists more or less of storing or deleting a row in one of the Slony-I control tables.

ローカルのノードデーモンは、そのイベントを処理しているものと同一のトランザクション内で、ローカルのテーブル(訳注: sl_confirm テーブル)へ、コンファーム行を挿入します。コンファーム行には、イベントのオリジン、イベントシーケンス番号、ローカルノード番号が含まれます。

In the same transaction the local node daemon processes the event, he will insert a confirmation row into a local table that matches the events origin, the event sequence number and the local node ID.

イベント配送のメカニズムに立ち返ると、デーモンは同一のコンファーム行を、接続されている各リモートノードのコンファームテーブルへも挿入します。そして、それらテーブルに関して NOTIFY を行ないます。リモートのノードデーモンはそのテーブルへ LISTEN をし、新たなコンファーム行を抽出、ネットワーク上へ送出します。このようにして、クラスタ内の全てのノードは、ローカルノードが無事にイベントを処理したことを知ることができます。

Reverse to the event delivery mechanism, the daemon will now insert the same confirmation row into the confirmation table of every remote node it is connected to, and NOTIFY on that table. The remote node daemon will LISTEN on that table, pick up any new confirmation rows and propagate them through the network. This way, all nodes in the cluster will get to know that the local node has successfully processed the event.

2.4.4. クリーンアップ / Cleaning up

ここまで見てきたように、Slony-I のシステム上では、多数のイベントが生成されます。多数のコンファームや(望むらくは)それ以上の数のトランザクションのログデータもあります。言うまでもありませんが、一定時間が経過したら、これらのデータは削除されなければなりません。ノードデーモンは定期的に、イベントやコンファーム、ログテーブルをクリーンアップします。この処理には、2 つのステップを要します。

So far we have generated may events, confirmations and (hopefully) even more transaction log data. Needless to say that we need to get rid of all that after a while. Periodically the node daemon will clean up the event, confirm and log tables. This is done in two steps.

1. コンファームのデータを削除します。全てのノードは、すべてのイベントをオリジンごとの昇順で処理するので、<origin, receiver> の組ごとに必要なコンファーム行は、高いイベントシーケンス番号を持つ行だけになります。

The confirmation data is condensed. Since all nodes process all events per origin in ascending order, we only need the row with the highest event sequence number per <origin, receiver>.

2. 古いイベントログのデータを削除します。2.4.5 で後述しますが、最後の SYNC イベントは削除してはいけません。ですので、まずは、まだ他の全てのノードによってコンファームされているわけではないイベントの内で、オリジンごとに最小のイベントシーケンス番号を持っている SYNC イベントを SELECT します。見つかった SYNC イベントごとに、それより古いイベントをオリジンから取り除き、同時に、対応するログデータの内で、削除された SYNC イベントのスナップショット情報的に見て可視なものも取り除きます。

Old event and log data is removed. As we will see in section 2.4.5. we need to keep the last SYNC event per origin. Thus we select the SYNC event with the smallest event sequence per origin, that is not yet confirmed by all other nodes in the cluster and loop over that result set. Per SYNC found we remove all older events from that origin and all log data from that origin that would be visible according to the snapshot information in the SYNC.

大きなボリュームを持つログデータが積まれると、ログのスイッチ機構が、ノードごとに利用される場合があります。なぜこのような機構が必要かというと、この状況下においては、VACUUM FULL だけが、ディスクスペースを再度確保するための唯一の手段だからです。ご存知の通り、VACUUM FULL はテーブルに排他ロックをかけてしまいますので、クライアントアプリケーションの活動を確実に止めてしまいます。ログのスイッチングモードに入ると、ログ記録用のトリガとファンクションは、代替テーブルの利用を始めます。ノードがスイッチモードにある間は、ログデータは、論理的には、2 つのログテーブルを結合したものになります。クリーンアッププロセスが、古い方のログテーブルが空になったことを検知すると、ログのスイッチモードは終了し、スイッチングモード中のすべてのトランザクションが終了するのを待ちます。そして、古い方のログテーブルを TRUNCATE します。

For the case that large volumes of log data once accumulated a log switching mechanism will be provided on a per node base. This is required since the only other way to reclaim the disk space would be a full vacuum, which grabs an exclusive lock on the table, thus effectively stopping the client application. After entering the switching mode, the triggers and functions inserting into the log table will start using an alterate table. While the node is in the switching mode, the log data is logically the union between the two log tables. When the cleanup process detects that the old log table is empty, it ends the log switching mode, waits until all transactions that could possibly have seen the system in switching mode have ended and truncates the old log table.

2.4.5. データのレプリケーション / Replicating data

あるリモートノードから SYNC を受信すると、ローカルノードは、そのイベントを生成したリモートノードをオリジンとするセットを購読しているかをチェックします。購読していないならば、単に他のイベントと同様、そのイベントにコンファームをかけ、それでおしまいです。他のノードは(少なくともこのローカルノードのためには)、イベントに対応するログデータを保持する必要がなくなります。なぜならば、このローカルノードは、もう決してこの SYNC イベント以前の情報を要求することはないからです。

Upon receiving a remote SYNC the node checks if it is actually subscribed to any set originating on the node that generated the event. If it is not, it simply confirms the event like any other and is done with it. All other nodes do not need to keep the log data (at least not for this node) because it will never ask for log information prior to this SYNC event.

もしも、そのオリジンからセットを購読しているならば、データのレプリケーションが、次に述べるステップに従って実行されます。

If it is subscribed to one or more sets from that origin, the actual replication works in the following steps.

1. ローカルノードは、SYNC イベントのオリジンから購読しているセットに対応する転送情報を提供してくれる、全てのリモートノードをチェックします。

The node checks that it has connections to all remote nodes that provide forward information for any set that is subscribed from the SYNC events origin.

図 3 (Figure 3) の例では、ノード B は、セット 1 だけをレプリケートするよう設定されています。ノード C も同様に、セット 2 だけをレプリケートします。レポーティング用途として、ノード D は両セットを購読しています。しかし、ノード A のワークロードを可能な限り低く保つために、セット1 はノード B から、セット 2 はノード C からレプリケートします。

Figure 3 illustrates a scenario where node B is configured to replicate only set 1. Likewise is node C configured to replicate only set 2. For reporting purposes node D is subscribed to both sets, but to keep the workload on the primary node A as low as possible, it replicates set 1 from node B and set 2 from node C.

このような分散データパスを持ってはいますが、ノード A で生成された SYNC イベントは、両セットにおいて意味を持ち、なおかつ、最後の SYNC イベント以降に(訳注: かつ、このイベント以前に)生成された両セットのすべてのログデータは、ノード D に一括のトランザクションで適用されねばなりません。よって、ノード D は、両ノードがすでに SYNC イベントを終了して初めて、その複製を開始することができます。

Despite of this distributed data path, the SYNC event generated on node A is meant for both sets and all the log data for both sets that has accumulated since the last SYNC event must be applied to node D in one transaction. Thus, node D can only proceed and start replicating if both nodes have already finished applying the SYNC event.

2. ノードデーモンは、SYNC イベントのオリジンに一致するセットを提供しているリモートノードのアクティブなログテーブルから、イベントのシーケンス番号順ソートをして、行を SELECT します。SELECT されるデータは、リモートノードが提供するセットに含まれるテーブルからのものだけに制限されます。さらに、最後の SYNC と現行 SYNC との間のものだけに制限されます。図 3 の例では、ノード D は ノード B に対して、以下のような問い合わせをすることになります:

What the node daemon does now consists logically of selecting a union of the active log table of every remote node providing any set from the SYNC events origin in log action sequence order. The data selected is restricted to the tables contained in all the sets provided by the specific node and constrained to lay between the last and the actual SYNC event. In the example of figure 3, node D would query node B like

SELECT * FROM log
    WHERE log_origin = id_of_node A
    AND   log_tableid IN (list_of_tables_in_set_1)
    AND   (log_xid > last_maxxid OR
          (log_xid >= last_minxid
          AND log_xid IN (last_xip)))
    AND   (log_xid < sync_minxid OR
          (log_xid <= sync_maxxid
          AND log_xid NOT IN (sync_xip)))
    ORDER BY log_origin, log_actionseq;

購読プロセスの都合上、実際には、これら条件の OR リストが用いられます。リモートノードがログスイッチのモードにある場合には、ログテーブルの和に対してクエリが行なわれます。幸いなことに、PostgreSQL のクエリ最適化は充分に賢いので、この場合には、ログテーブルの origin と actionseq のインデックススキャンを用いますが、最適化の結果、ソートは不要であると結論づけてくれます。

Well, at least for theory starters. In practice because of the subscribe process it will be an OR'd list of those qualifications per set, and during the log switching of the queried node it will do this whole thing on a union between both log tables. Fortunately PostgreSQL has a sufficiently mature query optimizer to recognize that this is still an index scan along the origin and actionseq of the log table that does not need sorting.

3. レプリケーション元のノードへと発行されたクエリの結果セットは、レプリケーション先のノードへとマージされ、そして、ローカルのデータベースへも適用されました。それらの行は正しくソートされて入って来ますので、ノードがマージを行なう際には、1 行ずつ先読みをするだけで、一気に処理することができます。レプリケーションされているテーブルのトリガは、SYNC の処理中は無効化されています。ローカルのテーブルにトリガが仕掛けられているならば、セットのオリジンノードにある同一テーブルでも、同様にトリガは定義されています。トリガによるすべての活動は、もしそれが複製されているテーブルに影響するものである場合には、同様に複製されます。ですので、スレーブ側ではトリガを再度実行する必要はありません。もしトリガが働いてしまうと、マスターとスレーブとで不整合が起きる可能性があります。

All these remote result sets are now merged on the replicating node and applied to the local database. Since they are coming in correct sorted, the node can merge them on the fly with a one row lookahead. Triggers defined on any replicated table will be disabled during the entire SYNC processing. If there is a trigger defined on a table, it would be defined on the same table on the set origin as well. All the actions performed by that trigger, as long as they are actions that affect replicated tables, will get replicated as well. So there is no need to execute the trigger on the slave again and depending on the trigger code, it could even lead to inconsistencies between the master and the slave.

4. さて、以上の一連の処理を要求した SYNC イベントは、他のイベントと同様にローカルにも蓄積され、ローカルのトランザクションはコミットされます。そして、コンファームが、他のイベントと同じように送出されます。

The SYNC event that caused all this trouble is stored as usual, the local transaction committed and the confirmation sent out as for all other events.

2.4.6. セットの購読 / Subscribing a set

セットの購読操作は、セットのオリジンで行なわれる必要があります。なぜならば Slony-I のシステムでは、オリジン上で実際に使用されているセットの、つまり、アプリケーションがコンカレントに修正を加えているセットの購読だけが許可されているからです。より大きなデータセットでは、データのスナップショット・コピーをとるのに、結構な時間がかかります。スナップショット・コピーが行なわれている間、そのセットに対応する、潜在的な各データプロバイダは、将来、ログデータを要求する購読者が現れることをすでに知っています。セットのオリジンでの SUBSCRIBE イベントの生成で保証されるのは、各ノードがこのイベントを受信するのは、オリジンからの来た同一の 2 つの SYNC イベントの間であるということです。よって、それらのノードは皆、ログデータの保存を、論理的な同一時点で開始することができます。

Subscribing to a set is an operation that must be initiated at the origin of the set. This is because Slony-I allows subscribing to sets that are actually in use on their origin, the application is concurrently modifying the sets data. For larger data sets it will take a while to create a snapshot copy of the data, and during that time all nodes that are possible replication providers for the set must know that there will be a new subscriber maybe asking for log data in the future. Generating the SUBSCRIBE event on the sets origin guarantees that every node will receive this event between the same two SYNC events coming from the origin of the set. So they will all start preserving the log data at the same point.

SUBSCRIBE イベントは、いくぶん特殊です。というのも、このイベントは、セットのデータプロバイダとなるノードから直接受けとらねばならないからです。なぜならば、新たな購読者ノードは、ログデータのプロバイダノードから、初期のスナップショットデータも受け取る必要があるからです。

SUBSCRIBE events are a little special in that they must be received directly from the node that is the log data provider for the set. This is because the log data provider is the node from which the new subscriber will copy the initial snapshot as well.

SUBSCRIBE イベントが受信された際、購読方法は、以下のいずれの条件に合致するかによって決定されます。一つ目は、ログデータのプロバイダがセットのオリジンであり、よって、新たな購読者は第 1 レベルである場合です。二つ目は、プロバイダが転送スレーブであり、新たなノードはそこからのカスケードである場合です。

When the SUBSCRIBE event is received from the correct node, the exact procedure how to subscribe depends on whether the log data provider is the sets origin so the new subscriber is a first level slave, or if is with respect to the set a forwarding slave and the new node cascades from that.

1. セット内の全てのテーブルについて、スレーブはテーブルの設定に関して問い合わせ、それをローカルに保存します。同時に、これらのテーブルにトリガを仕掛けます。

For all tables that are in the set, the slave will query the table configuration and store it locally. It will also create the replication trigger on all these tables.

2. セット内の全テーブルの全トリガが無効化されます。これは、データコピーの処理を高速化するだけではなく、外部キーの衝突を起こさないようにするためでもあります。間違った順序でのコピーや依存のループによって、衝突が起こる可能性があるからです。

All triggers on the tables in the set get disabled to speed up the data copy process and to avoid possible foreign key conflicts resulting from copying the data in the wrong order or because of circular dependencies.

3. 各テーブルについて、PostgreSQL は COPY コマンドを発行し、データストリームを転送します。

For each table it will use the PostgreSQL command COPY on both sides and forward the data stream.

4. トリガを、再度有効化します。

The triggers get restored.

5a. データのコピー元ノードもスレーブである場合(カスケード構成である場合)には、1 トランザクションとしてコピーされたデータは、オリジンが発行した最後の可視な SYNC イベントの時点の状態のものです。コピー開始以降に起きた変更については、このトランザクションにとっては不可視となります。よって、ローカルのセットの SYNC 状態は、その SYNC イベント時点のものとなります。

If the node we copied the data from is another slave (cascading), we have just copied the entire set in exactly the state at the last visible SYNC event from the sets origin inside of our current transaction. Whatever happened after we started copying the set is invisible to this transaction yet. So the local sets SYNC status is remembered as that and we are done.

5b. コピー元がセットのオリジンである場合の問題は、セットのデータは、ある SYNC ポイントの状態から他の状態へ「跳躍」することがないということです。この場合、SUBSCRIBE イベント以前の最後の SYNC イベント時点と、最後の SYNC イベント以降の、すでに見えてる全アクションシーケンスを用いてコピーを行います。これらのデータ行はすでにローカルへコピーされ適用されていることになるので、後で次の SYNC イベントを処理する際には、明示的にフィルターで除外せねばなりません。この手順が適用されるのは、最初の SYNC、つまりオリジンからの直接のセットの購読開始の直後に生成されたものだけになります。

If the node we received the initial copy from is the sets origin, the problem is that the set data does not "leap" from one SYNC point to another. In this case we need to use the last SYNC event before the SUBSCRIBE event we are currently processing plus all action sequences that we already see after that last SYNC. We have copied the data rows with those actions applied already, so when later on processing the next SYNC event, we have to explicitly filter them out. This only applies to the first SYNC event that gets created after subscribing to a new set directly from its origin.

6. 他のイベントと同様、SUBSCRIBE イベントはローカルに保存され、トランザクションがコミットされるとイベント処理にコンファームがかかります。

As usual, the SUBSCRIBE event is stored local, the transaction committed and the event processing confirmed.

2.4.7. 保存とアーカイブ / Store and archive

カスケード構成を実現するには、2.4.5 で見てきたように、ログデータをマージし適用した後で、ローカルのログデータテーブルにもそれを保存する必要があります。ログデータの挿入は、対応する SYNC イベントを挿入したのと同じトランザクション内で実行されるので、このデータを受信するスレーブからは、SYNC イベント受信の際には確実にそれを見ることができます。ログデータは、ローカルノードをオリジンとして持っているセットのために生成されたログデータが削除される際に、一緒に削除されます。このプロセスについては、2.4.4 ですでに見たとおりです。

In order to be able to cascade, the log data merged and applied in 2.4.5. must also be stored in the local log data table. Since this happens in the same transaction as inserting the SYNC event the log data was resulting from, every cascading slave that receives this data will be able to see it exactly when he receives the SYNC event, provided that the SYNC event was delivered by the provider. The log data will get cleaned up together with eventually local generated log data for sets originating on this node. The process described in 2.4.4. covers this already.

2.4.8. プロバイダの変更とフェイルオーバ / Provider change and failover

ノードに保存されたログデータは、そのセットを購読する全てのノードが SYNC イベントへのコンファームを終えるまでは削除されません。これによって、プロバイダ変更とフェイルオーバが可能になっています。

To store the log data on a node so configured until all nodes that subscribe the set have confirmed the corresponding SYNC events is the basis for on-the-fly provider changes and failover.

トリガが設置済みで、イベント通信を開始してさえいれば、ログデータ・プロバイダの変更が意味するところは、いずれかの時点で、ログデータを違うノードから読み始めるということ (2.4.5 で既述) です。違うノードとは、そのデータを持っているマスター、もしくはスレーブです。

Changing the log data provider means nothing else than starting at some arbitrary point in time (of course triggered and communicated with an event, what else) to select the log data in 2.4.5. from another node that is either the master or a slave that does store the data.

フェイルオーバは、他のノードとの同期を取りながら、セットのオリジンを変更し、最終的にプロバイダを変更する手続きです。

Failover is not much more than a logical sequence of syncing with other nodes, changing the origin of sets and finally a provider change with a twist.

1. 図 4 (Figure 4) のノード A が、これからフェイルするとします。ノード A は、データセット 1 のオリジンです。これから、ノード B をマスターへ昇格させ、ノード C は、新しいマスターから引き続きレプリケーションができるようにしようと思います。

Node A in figure 4 fails. It is the current origin of the data set 1. The plan is to promote node B to the master and let node C continue to replicate against the new master.

2. この時点で、ひょっとしたらノード C はノード B よりも、レプリケーションの観点で「先行して」いるかも知れませんので、ノード B はまず、自分が持っていない全てのイベント(及び、SYNC イベントに対応するログ差分)を要求します。この動作は、ノード A からのレプリケーションと、実質的には違いはありません。

Since it is possible that node C at that time is more advanced in the replication than node B, node B first asks for every event (and the corresponding log deltas for SYNC events) that it does not have itself yet. There is no real difference in this action than replicating against node A.

3. ノード B が確実にノード C よりも先行することが確約できたならば、ようやくセットを譲り受けてオリジンとなることができます。このプロバイダ変更に際してノード C について注意しなければならない点は、ノード C が、まだノード A からの SYNC イベントを全部処理したとは限らないことです。そこで、ノード B へはもう全てが適用済みなので、ノード B は ORIGIN イベントを生成します。ORIGIN イベントに含まれるノード A のイベントは、ノード B が既に知っているものになります。つまりは、クラスタ全体に知られているものです。ノード C で ORIGIN イベントを処理する際に注意すべきことは、このイベントがコンファームされるのは、ノード C がすべてのノード A のイベントを処理してからだということです。この時点で、ノード C は、ノード B やノード D をプロバイダとして、レプリケーションを続行できる状態になっています。

At the time Node B is for sure equally or more advanced than Node C, it takes over the set (becoming the origin). The twist in the provider change that node C now has to do is, that until now it is not guaranteed that node C has replicated all SYNC events from node A, that have been known to node B. Thus, the ORIGIN event from node B will contain the last node A event known by node B at that time, which must be the last node A event known to the cluster at all. The twist in processing that ORIGIN event on node C is, that it cannot be confirmed until node C has replicated all events from node A until the one mentioned in the ORIGIN. At that time of course node C is free to either continue to replicate using node B or D as its provider.

フェイルオーバのプロセスは比較的シンプルに見えると思いますが、実際シンプルなのです。Slony-I 全体のデザインについては本稿の最初から述べてまいりましたので、これは、驚くにはあたらないでしょう。しかしながら、このシンプルさには相応の対価が必要です。それは、スレーブノードが利用不能になった時に、クラスタ内の他の全てのノードではクリーンアップを停止してしまうため、イベント情報とログデータを蓄積してしまうことです。そのため、あまりに長い間ノードが利用不能となった場合、システムに構成変更を伝えるためには、再有効化のための何らかのテクニックが必要となります。当該ノードを論理的にサスペンド(無効化)するか、あるいは構成から完全排除することで、これが可能になります。

The whole failover process looks relatively simple at this point because it is so simple. The entire Slony-I design pointed from the beginning into this direction, so it's no real surprise. However, this simplicity comes at a price. The price is, that if a (slave) node becomes unavailable, all other nodes in the cluster stop cleaning up and accumulate event information and possibly log data. So it is important that if a node becomes unavailable for a longer time, to change the configuration and let the system know that other techniques will be used to reactivate it. This can be done by suspending (deactivating) the node logically, or by removing it from the configuration completely.

一旦無効化されてしまったノードを、再度他のクラスタ内のノードに追いつかせる際に、スクラッチで一から再参入させる以外にも、望みはあります。ポイント・イン・タイム・リカバリの差分ファイルについては 2.4.7 で見てきましたが、これを利用すれば、ログのテーブルからは遥か昔に除去されてしまったデータを、適用させることができるかも知れません。ノードは、このリプレイを終え次第、再度有効化されます。そして、クラスタ内にある他の全ノードは、このノードのために、再度ログを貯め始めるようになります。再有効化されたノードは、再び差分ログファイルを再生するようになり、再有効化直前の SYNC イベントに対応するデータが現れるまで、データの適用を続けます。そして、それが終了し次第、オンラインに戻ります。

For a deactivated node there is still hope to catch up with the rest of the cluster without re-joining from scratch. The point in time recovery delta files created in 2.4.7. can be used to feed it information that has been removed from the log tables long ago. When the node is finished replaying that it is reactivated, causing everyone else in the cluster to keep new log information again for the reactivated node. The reactivated node now again replays delta log files, eventually waiting for more to appear, until the one corresponding to the last known SYNC event before its reactivation appears. It is back online now.

3. 謝辞 / Acknowledgements

Slony-I の中心となる動作原理のいくつかについては、PostgreSQL プロジェクトに寄贈された、他のレプリケーションソリューションを参考としています。連続ストリームをトランザクション境界(シリアライザブルな分離レベルの)で分割するアイデアと、ログテーブルをスイッチするアイデア、およびその方法については、PostgreSQL Inc. によって寄贈された eRServer に倣っています。

Some of the core principles of Slony-I are taken fromanother replication solution that has been contributed tothe PostgreSQL project. Namely the splitting of the continuous stream of log information at a transaction boundary compatible with the serializable isolation level and the ideato be able to switch log tables and how to do it exist verysimilar in eRServer, contributed by PostgreSQL INC.

プリンタ用画面
友達に伝える
ログイン
ユーザー名:

パスワード:



パスワード要求 | 新規登録

検索
メインメニュー
 


Copyright(C) SIOS Technology, Inc. All Rights Reserved