Postgres-XC gtmの多重化(改訂3版:2011.02.25)

Postgres-XCの単一障害点であるgtmを多重化するmgtmを作ったので公表する。 これはAPPIA版の改良でJGroupsを使っている。
2011年2月現在、最新版はver0.9.3〜0.9.4だが、mgtmはver0.9.2で動作する。

(2016.2.11) 多重化gtmが動くVagrant boxを作成して公開: [Vagrant box| GitHub] したので、興味があればどうぞ。

動機

詳細にはいる前に、簡単に作成した動機を述べる。

開発の目的

これを作った動機は2つある。

  • ディペンダブルなミドルウエアをつくること
  • フォーマルメソッド、モデル検証がどこまで適用できるか確認すること

ディペンダブルなミドルウエアをつくる

そもそもの発端は数年前に 「memcachedとPostgreSQLによるユーザ認証システムの検討」と称してあるシステムを構築したことにある。

これを現代的にリプレースするため、根本から見直して作りはじめたのがきっかけである。

基本的なアーキテクチャは下図。

アーキテクチャ概要

gtmもmemcachedもメッセージを受け取って結果を返す状態遷移マシンとして扱うので、基本的にはどんなものにでも対応できる(はず)。 要するに図のサーバ部分はgtmでもmemcahedでも別のものでも簡単に組込みが可能なのである。

ついでに、今回の耐障害性の仕組み。これも汎用的な仕組みとしてプログラムしている。

gtmクラッシュ時の動作は以下。

クラッシュ時の動作

フォーマルメソッド、モデル検証がどこまで適用できるか確認する

「3重化しました。しかしコンポーネントの信頼性が低いので、全体の信頼性は単体のときよりも下がりました」では意味がない。
また、いくら「きちんとつくりました。テストもしました」といっても、それは説得であって証明ではない。

ディペンダブルシステムについて考えていると、自然と「フォーマルメソッド」や「モデル検証」に目がいく。 批判も多いとは認識しているが、現時点での私のスタンスはここに書いてある。

とにもかくにも、やってみなければわからない。組込み分野やエンタープライズ分野では実際に使われているが、 信頼性を問われるミドルウエア分野での適用例は聞いたことがない。
個人的な意見だが、ミドルウエア分野はOSやRDB以外、規模もそれほど大きくなく、信頼性が重要視され、かつ状態遷移マシンで記述できる程度の複雑さを相手にするので、まさに「フォーマルメソッド+モデル検証」を使うにベストな分野と思っている。

準備と動作確認

前置きはこれくらいにして、設定方法と動作確認方法を述べる。

Postgres-XC ver0.9.2の準備

まず、こちらを参考に、Postgres-XC ver0.9.2をセットアップする。

各種jarファイルのダウンロード

起動方法

mgtmはサーバgtm,coordinator1,coordinator2の3台で起動させる。 mgtm_proxyはサーバcoordinator1,coordinator2の2台で起動させる。

以下のファイル"demo.xml"を用意し、全サーバにコピーする。アイテム名がいまいちだが気にしないでほしい。

<?xml version="1.0" encoding="UTF-8"?>
<conf>
  <gtms>
    <gtm>
      <gid>1</gid>
      <host>gtm</host>
      <port>5555</port>
      <port_ic>12345</port_ic>
    </gtm>

    <gtm>
      <gid>2</gid>
      <host>coordinator1</host>
      <port>5555</port>
      <port_ic>12345</port_ic>
    </gtm>

    <gtm>
      <gid>3</gid>
      <host>coordinator2</host>
      <port>5555</port>
      <port_ic>12345</port_ic>
    </gtm>
  </gtms>

  <proxies>
    <proxy>
      <pid>7</pid>
      <host>coordinator1</host>
      <port>6666</port>
    </proxy>

    <proxy>
      <pid>8</pid>
      <host>coordinator2</host>
      <port>6666</port>
    </proxy>
  </proxies>
</conf>

mgtm起動用スクリプトmgtm.shをサーバgtm,coordinator1,coordinator2に配置する。

#!/bin/bash
export CLASSPATH=./commons-logging-1.1.1.jar:./jgroups-2.5.0-GA.jar:./mgtm-0.4.0a.jar
java mgtm -f demo.xml  -D . -g $1 -x $2

mgtm_proxy起動用スクリプトmgtm_proxy.shをサーバcoordinator1,coordinator2に配置する。

#!/bin/bash
export CLASSPATH=./jgroups-2.5.0-GA.jar:./commons-logging-1.1.1.jar:./mgtm-0.4.0a.jar
java  mgtm_proxy -D . -f ./demo.xml -g $1

mgtmの起動

サーバgtmで、以下のコマンドを実行する。

gtm> ./mgtm.sh 1 1000

ここで第一引数"1"はgtmのidで、demo.xmlファイルでgid=1のサーバ起動を意味する。 第二引数"1000"はグローバルトランザクションIDの初期値で適当に選んだ値である。

次いでサーバcoordinator1で、以下のコマンド:

coordinator1> ./mgtm.sh 2 1000
サーバcoordinator2で、以下のコマンド:
coordinator2> ./mgtm.sh 3 1000
を実行する。

第二引数のグローバルトランザクションIDはすべてのmgtmで同じ値を設定すること。 設定ファイルをつくろうとも考えたが面倒なので辞めた。

3台とも立ち上がると、gidが一番若いgtmサーバのコンソールに以下のメッセージが表示される。


        This is a trial piece for feasibility stady.
        (1)This program works with Postgres-XC version 0.9.2 only.
        (2)You can execute pgbench, and simple queries.

Ok.
I'm running...
Configuration File:
        [GTM]
        gid     host            port    port_ic
        -----------------------------------------------
        1       gtm     5555    12345
	2       coordinator1    5555    12345
	3       coordinator2    5555    12345
	[GTM_Proxy]
        pid     host            port
        ----------------------------
        7       coordinator1    6666
        8       coordinator2    6666

GTM instance created.
2011/02/25 16:35:43 org.jgroups.JChannel init

.... 略 .....

[Address]=/192.168.122.121[Port]=12345
        [Address]=/192.168.122.121[Port]=12345  gid = 2
Leader = 1
I'm LEADER!!!!

mgtm_proxyの起動

"I'm LEADER!!!!"メッセージが出てリーダが選出されたら、mgtm_proxyを起動する。

サーバcoordinator1で以下のコマンド:

coordinator1> mgtm_proxy 7
サーバcoordinator2で以下のコマンド:
coordinator2> mgtm_proxy 8
をそれぞれ実行する。

以上で準備は終わりである。通常の手順でPostgres-XCのcoordinatorとdatanodeを起動し、適当にpgbenchなど実行する。
実行中にmgtmをshutdownさせても新たにリーダが選出されて、処理を続行するはずである。

なお、mgtm_proxyの終了処理が汚いので、なかなか終了しない場合がある。これも直すのに時間がかかるので、そのまま公開することにした。 killでjavaVMごと落とせばよい。

まとめ

前述したが、UIが悪い、終了処理がまずい、などなど欠点も多い。 しかしgtmの多重化が目標ではないので(ケリをつける意味で)、詰めが甘いのを承知で公開する。

動作速度であるがほどほど速いと思う。 mgtm間通信はJGroupsのUDPユニキャストを使っている。 UDPマルチキャストをつかえはもっと速くなるのは明らかだが、マルチキャストを使えない環境もありえるため、 今回はユニキャスト版を公開した。

mgtm_proxyは送信したメッセージを、mgtmは結果を、それぞれ保存しており、 各メッセージにはIDが割り振られているので、 mgtmのどれかが生きていれば処理を続けることができる。
mgtm_proxyは停止しないと仮定している。これはmgtm_proxyに接続するcoordinatorやdatanodeの挙動を制御できないため。別のシステムを載せた場合は、*_proxyに接続するクライアント側に切り替え接続の機能を持たせることで対応できる。

このシステムはmgtmのクラッシュ障害に対しては完全な耐性があり、Omission障害にもほぼ耐性がある(JGroupsに依存)。 間欠障害については特に今回の場合はdatanode-mgtm_proxyやcoordinator-mgtm_proxyがランデブー通信なので、 timeout値の設定にも依存するが、それを修復不可能な障害と判断すれば縮退運転に移動するし、復活したならJGroupsの力でmgtm間のデータ一貫性は保てる。詳細な分析は後で公開できればと思っている。

ソースの公開については、かなり先になる。理由は「フォーマルメソッドやモデル検証の手法を部分的に試しつつあり、現状は公開できるレベルに到達していないため」 まだまだ試行錯誤の状態である。

「ケリをつける」については行間を読んでほしい。


Last-modified: 2012-10-28