Slony-Iを停止させずにテーブルを追加する

Slony-Iを用いた非同期レプリケーションを構築している環境で、テーブル追加をする場合、やや面倒な手順を踏まないといけません。このあたりはMySQLレプリケーションに劣るところですね。

手順の概要

Slony-I環境にテーブルを追加するには、以下の手順を実施する必要があります。

  1. 対象テーブル(のDDL)をレプリケーション対象の全ノードに追加する。
  2. 追加したテーブルをレプリケーションに参加させる。
    1. 新しいテーブル情報を設定ファイルに追記する。
    2. 追記した設定をSlony-Iに反映する。


以降で、手順の詳細は順番に書いていきます。

対象テーブル(のDDL)をレプリケーション対象の全ノードに追加

Slony-Iはトリガーベースのレプリケーションのため、DDLはマスター/スレーブ間で伝播しません。そのため、作業者自身の手で、全ノードにDDLを実行して回る必要があります。


オリジンノード、及び、サブスクライバノードにログインし、以下のようなDDLを実行します。ここでは、複数のノードで実行することを考慮し、DDLスクリプトファイルとして実行しています。

/usr/local/pgsql8/bin/psql -p5432 -U demo demodb -f create_table.sql

追加したテーブルをレプリケーションに参加させる

テーブルを全ノードで追加し終わったら、オリジン*1ノード にログインし、新しいテーブルをレプリケーションに参加させる設定を行います。


まずは、slon_tools.conf に新しいテーブル情報を追加します。追加する必要がある情報*2は下記の通りです。

    "set2" => { ・・・1
        "set_id"       => 2, ・・・2
        "table_id"     => 2, ・・・3
        "origin"     => 1,   ・・・4
        "sequence_id"  => 2, ・・・5
        "pkeyedtables" => ['schema.table1', 'schema.table2', ], ・・・6
    },
  1. テーブル情報は、この「セット(set)*3」という単位で追加します。
  2. セット単位に付与されるID。稼働中のset_idと被らないようにします。
  3. この変数は、テーブルを一意に特定するための番号付けがどこから開始されるのかということを意味します。
  4. オリジンのノード番号。
  5. この変数は、一意である必要がありますが、何の情報に紐づいているか、イマイチ分かりません。。
  6. 主キーを持ったテーブル名をカンマ区切りで記述します。


少し、長くなりましたが、追加情報は上記の通りです。で、実際に追記してみると以下のようになります。「table_id」と「sequence_id」はオリジンノードのPostgreSQLに格納されているSlony-Iの管理テーブルより、最新情報を取得の上、設定します。

[postgres]$
[postgres]$ vi /usr/local/slony1/etc/slon_tools.conf
〜〜〜〜〜〜〜〜〜〜
$SLONY_SETS = {
    # 既存の設定
    "set1" => {
        "set_id" => 1,
        "table_id"    => 1,
        "origin"     => 1,
        "sequence_id" => 1,
        "pkeyedtables" => ["demo.table1, demo.table2, demo.table3"],
    },

    # 今回追加した設定
    "set2" => {
        "set_id" => 2,
        "table_id"    => 4, # select max(tab_id) from _<<クラスタ名>>.sl_table;の結果 +1
        "origin"     => 1,
        "sequence_id" => 4, # select max(seq_id) from _<<クラスタ名>>.sl_sequence;の結果 +1
        "pkeyedtables" => ["demo.table4, demo.table5"],
    },
};
〜〜〜〜〜〜〜〜〜〜


設定ファイルへの追記が終わったら、Slony-Iへ設定を反映していきます。


Slony-Iの制御は、Slony-I独自の命令を、slonikというコマンドに入力することで行います。しかし、この独自の命令は小さなプログラミング言語のため、操作するのがかなり面倒です。そこで、独自の命令(プログラム)を生成するコマンドが用意されており、以下の形式で使用します。


命令生成コマンド 引数 | slonik


要するに、命令を自動生成し、パイプ(|)でslonikに入力するということです。ここでは、以下の命令生成コマンドを利用します。

  • slonik_create_set (追加したセット番号)
    • 追加したセットをオリジンノードに反映する命令を生成
  • slonik_subscribe_set (追加したセット番号) (スレーブnode番号)
    • 追加したセットをサブスクライバノードに反映する命令を生成
  • slonik_merge_sets (マスタnode番号) (マージ先セット番号) (追加したセット番号)


では、実際にSlony-Iへ設定を反映してみましょう。今回は、追加したセット番号=2、マスタnode番号=1、スレーブnode番号=2,3とすると、以下のようになります。

# オリジンノードに設定を反映
[postgres]$ slonik_create_set 2 | slonik

# サブスクライバノードに設定を反映
[postgres]$ slonik_subscribe_set 2 2 | slonik
[postgres]$ slonik_subscribe_set 2 3 | slonik

# 設定追加のために一時的に作成したセットを既存セットにマージ
[postgres]$ slonik_merge_sets 1 1 2 | slonik


Slony-I上では、set1(元のセット)と、set2(追加用の一時的なセット)をマージしましたので、設定ファイル(slon_tools.conf)も合わせておきます。

[postgres]$ vi /usr/local/slony1/etc/slon_tools.conf
〜〜〜〜〜〜〜〜〜〜
$SLONY_SETS = {
    # マージして一つのセットに
    "set1" => {
        "set_id" => 1,
        "table_id"    => 1,
        "origin"     => 1,
        "sequence_id" => 1,
        "pkeyedtables" => ["demo.table1, demo.table2, demo.table3,demo.table4, demo.table5"],
    },

    # set2の記述は削除

};
〜〜〜〜〜〜〜〜〜〜


作業は以上です。いや〜、結構大変ですね。

補足1

  • この手順実施後に、PostgreSQL、及び、Slony-Iの再起動は不要です。
  • 手順として、「set2など追記せず、いきなり既存設定(set1)にテーブル名を追記すればいいのでは?」と思われるかもしれませんが、いきなり既存設定を修正すると、レプリケーション設定がおかしくなってしまいます。
  • 原則的に、slon_tools.conf のメンテナンス作業は、オリジンノードのみでOKですが、フェイルオーバーなどを考慮すれば、全ノードに同じslon_tools.conf を配っておくのがよいとは思います。

補足2(slon_tools.confの解説)

slon_tools.conf は、Slony-Iの基本となる設定ファイルで、レプリケーション対象のノード情報や、対象テーブルの情報を設定します。ここで少し内容を確認しておきます。

  • $CLUSTER_NAME : クラスタ名を設定。ここでは"slony_demo"。
  • $MASTERNODE : マスターノードには、マスターサーバの番号を設定。ここでは"1"を設定。
  • add_node : ノード情報を記述。
  • $SLONY_SETS : レプリケーションセット情報を記述。
    • pkeyedtables : PKのあるテーブルはここに追記。
    • keyedtables : PKはないがNOT NULL制約とユニーク制約が定義された、事実上のPKを持つテーブルはここに追記。
    • serialtables : PKとなりうるカラムのないテーブルはここに追記。*4
[postgres]$ cat /usr/local/slony1/etc/slon_tools.conf

if ($ENV{"SLONYNODES"}) {
    require $ENV{"SLONYNODES"};
} else {
    $CLUSTER_NAME = 'slony_demo';
    $LOGDIR = '/var/log/slony1';
    $MASTERNODE = 1;

    add_node(node     => 1,
             host     => '192.168.0.1',
             dbname   => 'demodb',
             port     => 5432,
             user     => 'repl',
             password => 'repl');

    add_node(node     => 2,
             host     => '192.168.0.2',
             dbname   => 'demodb',
             port     => 5432,
             user     => 'repl',
             password => 'repl');
}

$SLONY_SETS = {
    "set1" => {
        "set_id" => 1,
        "table_id"    => 1,
        "origin"     => 1,
        "sequence_id" => 1,
        "pkeyedtables" => ["t_pkey_table"],
        "keyedtables" => {"t_ukey_table" => "t_ukey_table_key",},
        "serialtables" => ["t_nokey_table"],
        "sequences" => ["t_pkey_table_key"],
    },
};

if ($ENV{"SLONYSET"}) {
    require $ENV{"SLONYSET"};
}

1;

*1:いわゆるマスターサーバ、サブスクライバとはスレーブサーバのこと。

*2:ここではPKが存在するテーブルを追加するケースを前提にしています。PKが存在しないテーブルの場合には、もう少し別の項目も併せて追記する必要があります。

*3:セットとは、Slony-Iでのレプリケーションするテーブルの組のことです。例えばセットset1はテーブルtbl_1をノード1からノード2にレプリケート、 セット2はテーブルtbl_2をノード1からノード3にレプリケートするなど、細かな設定が可能です。

*4:Slony-Iレプリケーションを行う全てのテーブルにユニークなキーが必要なため、それらがない場合は、Slony-Iが自動的にシリアルナンバーを割り振る動作をします。