株式会社クリアコード > ククログ

ククログ


変更禁止のオブジェクトの一部のメソッドだけをES6 Proxyでオーバーライドする方法

以前にMozillaのJavaScript実行エンジンの独自拡張機能である__noSuchMethod__をES6 Proxyで代替する方法をご紹介しましたが、その応用となる話です。

Proxyの制限事項

Proxyは任意のオブジェクトに対するプロパティアクセスやメソッド呼び出しなどの操作にフックを仕掛けたり動作をオーバーライドしたりするための汎用的な仕組みです。

しかし、何でも出来るわけではなく、仕様上で定義されている制限事項もあります。 具体的には、変更が禁止されたオブジェクトのプロパティアクセスに対しては、元と異なる値を返す事ができません。

var myObject = {
  hello: function() { return 'Hello!'; },
  bye: function() { return 'Bye!'; }
};
alert(myObject.hello()); // "Hello!"
alert(myObject.bye()); // "Bye!"

Object.freeze(myObject);

var proxied = new Proxy(myObject, {
  get: function(target, name, receiver) {
     if (name == 'hello')
       return function() { return 'Hi!'; };
     return target[name];
  }
});

alert(proxied.hello()); // TypeError: proxy must report the same value for a non-writable, non-configurable property

これは、"Hello!"という文字列を返すhello()メソッドをオーバーライドして、代わりに"Hi!"という文字列を返すようにしようとして失敗した例です。 メッセージを見ると分かりますが、getトラップでは、書き換え不可能・変更不可能なプロパティに対するアクセスには元と同じ値を返さなくてはならず、違う値を返すとその時点で例外が発生するという仕様になっています。 (詳しくはMDNの解説の「不変条件」の見出しを参照して下さい。)

実際に、Firefox/Thunderbird用のアドオン向けテスティングフレームワークのUxUではこの制限事項の影響がありました。 このアドオンはThunderbirdの動作に関わるテストを容易にするために、「メールを送信しようとした時に、実際にはメールを送らないで、送られるはずだったメールの内容を溜め込んでおいて後で比較できるようにする」という機能を含んでいるのですが、これはメール作成ウィンドウのgMsgComposeというオブジェクトのSendMsg()メソッドをオーバーライドする事で実現していました。 しかし、gMsgComposeが変更不可能になっているため、SendMsgプロパティの呼び出し時に違う関数を返す事で動作をオーバーライドするというようなことはProxyベースではできなくなってしまっていました。

手軽な解決策

この問題に対する最も簡単な回避策は、ダミーの変更可能なオブジェクトに対するProxyとして作成しておいて、実際には変更不可能なオブジェクトに対するProxyとして動作させるようにするというものです。 上記の例であれば、このようになります。

...
Object.freeze(myObject);

var proxied = new Proxy({}, { // <= Proxy対象のオブジェクトを、ダミーの変更可能なオブジェクトにしておく。
  get: function(target, name, receiver) {
     if (name == 'hello')
       return function() { return 'Hi!'; };
     return myObject[name].bind(myObject); // <= 実際のProxy先は本来のオブジェクトにしておく。bindによるthisの束縛も忘れないように!
  }
});

alert(proxied.hello()); // "Hi!"
alert(proxied.bye()); // "Bye!" <= 本来のオブジェクトに処理が委譲されている。

用途が限定されている場面では、これで十分実用になります。

ただ、こうして作成されたProxyはあくまで元のオブジェクトではなくダミーのオブジェクトに対するProxyなので、いくつかの場面では振る舞いが元のオブジェクトは異なってきます。 例えば、Proxyに対してinを使ってプロパティの存在を確認をしてもfalseが返るのに、実際にアクセスするとプロパティの値が返ってくる、という不思議な事が起こります。

alert('hello' in myObject); // true

alert('hello' in proxied); // false <= メソッドが無いと判定される。
alert(typeof proxied.hello); // "function" <= 実際にアクセスするとメソッドがある。
alert(proxied.hello()); // "Hi!" <= 実行もできる。

FirefoxやThunderbirdのアドオンでは、複数のバージョンのFirefoxやThunderbirdに対応するために、Firefox/Thunderbird自身が提供している機能の存在の有無を確認して、それを以てバージョン判別の代わりにするということが多いです。 しかし、上記のようにProxyでラップされたオブジェクトに対してはプロパティの存在を問い合わせても「無し」という答えが返ってきてしまうため、併用している他のアドオンが動作しなくなったり、Firefox/Thunderbird自身の機能が動作しなくなったりという事が起こり得ます。

より自然に振る舞うProxyを定義する

このような問題を避けるためには、「Proxyに何か操作が行われたら、ダミーのオブジェクトに対してではなく本来の変更不能なオブジェクトに対する操作として処理を委譲する」というトラップの指定をより広範囲に渡って行う必要があります。 以下は、仕様で定義されているすべてのトラップを定義した例です。

var proxied = new Proxy({}, {
  get: function(aTarget, aName, aReceiver) {
    if (name == 'hello')
      return function() { return 'Hi!'; };
    return myObject[aName].bind(myObject);
  },
  // ここから追加分
  getPrototypeOf: function(aTarget) {
    return Object.getPrototypeOf(myObject);
  },
  setPrototypeOf: function(aTarget, aPrototype) {
    return Object.setPrototypeOf(myObject, aPrototype);
  },
  isExtensible: function(aTarget) {
    return Object.isExtensible(myObject);
  },
  preventExtensions: function(aTarget) {
    return Object.preventExtensions(myObject);
  },
  getOwnPropertyDescriptor: function(aTarget, aProperty) {
    return Object.getOwnPropertyDescriptor(myObject, aProperty);
  },
  defineProperty: function(aTarget, aProperty, aDescriptor) {
    return Object.defineProperty(myObject, aProperty, aDescriptor);
  },
  has: function(aTarget, aProperty) {
    return aProperty in myObject;
  },
  set: function(aTarget, aName, aValue, aReceiver) {
    return myObject[aName] = aValue;
  },
  deleteProperty: function(aTarget, aProperty) {
    delete myObject[aProperty];
  },
  enumerate: function(aTarget) {
    return Reflect.enumerate(myObject);
  },
  ownKeys: function(aTarget) {
    return Object.getOwnPropertyNames(myObject);
  },
  apply: function(aTarget, aThis, aArgs) {
    return myObject.apply(aThis, aArgs);
  },
  construct: function(aTarget, aArgs) {
    return new myObject(...aArgs);
  }
  // ここまで追加分
});

こうしておけば、メソッド呼び出し以外の場面でも元のオブジェクトと同じ振る舞いをしてくれます。

alert('hello' in myObject); // true
alert('hello' in proxied); // true <= 元の変更不可能なオブジェクトと同じ結果になっている。

一般的な開発ではここまでの事をする必要はないと考えられますが、フレームワーク的な物を作る場合や、既存の仕組みの基盤にあたる部分をハックする場合には、こういった工夫が役に立つ場合があります。

Firefox/Thunderbirdのアドオン開発も、そのような特殊事例の1つと言えます。 これらのアドオンは、どのような組み合わせで使われるかが事前に予想できないため、既存の物の振る舞いを不用意に変えると、思わぬ所で互換性の問題が発生してしまいます。 「このアドオンは便利なのだが、入れたら他のアドオンが全く動かなくなってしまった。実用には使えないので、使用を諦めるしかない。」というような事態に陥りにくい、他のアドオンとの相互互換性が高いアドオンにするためには、このようにして可能な限り元の動作との互換性を確保しておく必要があるわけです。

まとめ

ES6 Proxyを使って、本来であれば変更ができないはずのオブジェクトの振る舞いを部分的に変える方法をご紹介しました。

2016-03-04

第1回 法人向けFirefox導入セミナーで発表を行いました

2016年3月16日、Mozilla Japan主催の法人向けFirefox導入セミナーでFirefoxの法人利用についての紹介を行いました。

発表資料はGitHubの発表資料のリポジトリにてPDFを公開しています。 また、PDF版ではリンクになっていない参考情報などについてはMarkdown形式の元ファイルをご参照下さい。

Firefoxの法人向けカスタマイズメニューについて

発表の中で、法人利用で頻出の設定項目を一覧にしたカスタマイズメニューをご紹介しました。

Firefoxはカスタマイズ性の高さが特長の製品ですが、「なんでもできます」と言われても普通は途方に暮れてしまいますし、実際に法人で利用する場面では、行いたいカスタマイズの内容はある程度パターン化されています。 そこでクリアコードでは、業務の効率化を兼ねて、カテゴリ別にカスタマイズ項目とその実現方法を整理したカスタマイズメニューを作成・利用しています。

こちらも発表資料と同様にGitHubで公開しており、全設定項目の一覧はOpenDocument Formatのファイルとしてダウンロードしていただけます。 また、発表では触れませんでしたが、各カスタマイズ項目の検証手順のドキュメント(※現状では未完成)や、検証用のテストケース群もあります。

設定項目の一覧のODSファイルには、以下の3つのシートが含まれています。

  • 全般的な設定項目の一覧(メタインストーラの設定を含む)
  • キーボードショートカットやメニュー項目のON/OFFに関する設定の一覧
  • その他のUI項目の非表示化に関する設定の一覧

それぞれの設定項目には、設定項目の識別用のIDと共に、簡単な説明と、各選択肢の具体的な反映方法を記してあります。 ここに書かれている内容に従ってMCD用の集中管理用設定ファイルglobalChrome.css用のスタイルシートファイルを作成すれば、それらのカスタマイズが反映された状態のFirefoxを迷い無く作る事ができます。

リポジトリにあるファイルは一般的な内容になっており、実際の業務では、ここからお客様ごとのニーズに合った選択肢を選び出して使用しています。 現在はちょうどFirefox 38ESRからFirefox 45ESRへの更新が発生している時期なので、その過程で得られた知見を随時フィードバックしつつ、各項目や対応する検証手順をアップデートしているという段階です。

検証が完了していない箇所については、内容に間違いや見落としがある可能性がありますので、その場合はもし宜しければissueでの指摘やプルリクエストを頂けましたら幸いです。

カスタマイズのデモンストレーション用メタインストーラについて

発表の中で、実際のメタインストーラを実行してカスタマイズ済みのFirefoxをインストールする様子をご紹介しました。 こちらの実際のファイルもGitHubリポジトリのリリースの一部として公開しています

ただし、Firefoxのインストーラそのものの再配布はできないため、実際には「メタインストーラ作成キット一式」の状態の物をアップロードしています。 こちらのZIP形式のファイルを展開すると「FxDemoInstaller-source」というフォルダが取り出されます。この中の「resources」フォルダにFirefox 45ESRのインストーラの実行ファイルをコピーして、「FxDemoInstaller.bat」というバッチファイルを実行する事で、最終的なインストーラの実行ファイル「FxDemoInstaller-45.0.exe」が作成されます。 この「FxDemoInstaller-45.0.exe」をWindowsクライアントにコピーして実行することで、カスタマイズ済みのFirefoxが実際にインストールされます(それ以外のファイルはコピーしなくても大丈夫です)。

このデモ用メタインストーラで行われているカスタマイズ内容は、上記のカスタマイズメニューからいくつか項目をピックアップして作成しています。 カスタマイズの適用方法の例として参考にして下さい。

カスタマイズメニューとメタインストーラのご利用上の注意

上記のカスタマイズメニューとメタインストーラは、自社内での展開用などにそのままお使いいただいて問題ありません。 ただし、内容については無保証となります。

期待通りの結果を得られないなどのトラブルへの対応、個別の環境に合わせるための調査・修正、既存の項目でカバーされていないカスタマイズのご相談などについては、クリアコードのMozillaサポートサービス(有償)のご利用をご検討下さい。

次回のセミナーについて

この法人向けセミナーは毎月第3水曜日の定期開催となっており、次回は4月20日を予定しています。 参加登録はDoorkeeperのMozillaグループから行うことができ、開催会によっては事前登録時のアンケートで、あらかじめセミナーで聞きたい内容をリクエストすることもできるようになる予定です。 Firefoxの法人利用についてピンポイントで不明な点がある場合には、その旨をお伝えいただければ幸いです。

また、次回開催時には15時から16時までと17時から18時までを個別相談の時間として会場を開放し、セミナーは16時から17時の開催とする予定です。 セミナーの質疑応答で大々的には質問できないことでお困りの場合には、個別相談の時間にご相談頂くこともご検討ください。

以上、Mozilla Japanの第1回法人向けFirefox導入セミナーの発表内容の補足と、次回セミナーのご案内でした。

タグ: Mozilla
2016-03-17

pglogicalとPGroongaを使ったレプリケーション対応の高速日本語全文検索可能なPostgreSQLクラスターの作り方

PostgreSQLは標準機能ではインデックスを使った日本語全文検索機能がありません。PostgreSQLでインデックスを使った高速な日本語全文検索を実現する拡張機能にはPGroonga(ぴーじーるんが)があります。しかし、PGroongaはPostgreSQL標準のレプリケーション機能を使えません。これは、PostgreSQLが拡張機能で追加したインデックスのレプリケーションをサポートしていないからです。

レプリケーション機能を提供する拡張機能があり、それを使うとPGroongaでもレプリケーションを実現できます。たとえば、pg_shardを使う方法があります。

ここでは、別の方法としてpglogicalを使う方法を紹介します。

なお、pglogicalの開発者はPostgreSQL 9.6へpglogicalを含めることを提案しています。どうなるかはわかりませんが、もしかしたら、将来のPostgreSQLにはpglogicalが含まれているかもしれません。

構築方法

pglogicalとPGroongaを使ったレプリケーション対応の高速日本語全文検索可能なPostgreSQLクラスターの構築方法を説明します。

ディストリビューションはCentOS 7を使い、PostgreSQLは9.5を使います。

クラスターには次のノードがあるとします。

役割 ホスト名 IPアドレス
マスターノード master 192.168.0.16
スレーブノード1 slave1 192.168.0.17
スレーブノード2 salve2 192.168.0.18

このクラスターではblogデータベースをレプリケーションします。

それでは、まずはマスターノードをセットアップし、その後スレーブノードをセットアップします。

マスターノードのセットアップ

PostgreSQLをパッケージでインストールします。

% sudo rpm -ivh http://yum.postgresql.org/9.5/redhat/rhel-$(rpm -qf --queryformat="%{VERSION}" /etc/redhat-release)-$(rpm -qf --queryformat="%{ARCH}" /etc/redhat-release)/pgdg-centos95-9.5-2.noarch.rpm
% sudo -H yum install -y postgresql95-server
% sudo -H /usr/pgsql-9.5/bin/postgresql95-setup initdb
% sudo -H systemctl enable postgresql-9.5

pglogicalをパッケージでインストールします。詳細はpglogicalのインストールドキュメント(英語)を参照してください。

% sudo -H yum install -y postgresql95-contrib
% sudo -H yum install -y http://packages.2ndquadrant.com/pglogical/yum-repo-rpms/pglogical-rhel-1.0-1.noarch.rpm
% sudo -H yum install -y postgresql95-pglogical

PGroongaをパッケージでインストールします。詳細はPGroongaのインストールドキュメントを参照してください。

% sudo -H yum install -y http://packages.groonga.org/centos/groonga-release-1.1.0-1.noarch.rpm
% sudo -H yum install -y postgresql95-pgroonga

これで必要なパッケージはすべてインストールできたので設定をします。

まずはpostgresql.confを設定します。

外部からの接続を受け付けるようにするため、listen_address*にします。

/var/lib/pgsql/9.5/data/postgresql.conf:

listen_addresses = '*'

続いてpglogical用の設定をします。詳細はpglogicalのドキュメント(英語)を参照してください。

次の項目を設定します。

項目
wal_level 'logical'
max_replication_slots 2
max_wal_senders 2
shared_preload_libraries 'pglogical'

max_replication_slotsmax_wal_senders2なのは、今回の例ではスレーブノードが2台だからです。スレーブノードの数だけこの値を増やす必要があります。

なお、max_worker_processesをレプリケーションするデータベースの数以上に設定する必要もありますが、今回はデフォルト(8)から変更しません。理由は、今回の例ではblogデータベースだけをレプリケーションするからです。1つなのでデフォルトの8で十分だからです。

具体的には次のように設定します。

/var/lib/pgsql/9.5/data/postgresql.conf:

wal_level = 'logical'

max_replication_slots = 2
max_wal_senders       = 2

shared_preload_libraries = 'pglogical'

続いてpg_hba.confを設定します。

次の2つの設定を追加します。

  • 後で作成するデータベース操作用のユーザー(blog_user)がローカルネットワークからblogデータベースへパスワード接続することを許可
  • 後で作成するレプリケーション用のユーザー(blog_replication)がスレーブノードからのレプリケーションのためにパスワード接続することを許可

/var/lib/pgsql/9.5/data/postgresql.conf:

host blog        blog_user        192.168.0.0/24 md5
host replication blog_replication 192.168.0.0/24 md5

これで起動前の設定は完了したのでPostgreSQLを起動します。

% sudo -H systemctl start postgresql-9.5

まず、データベースを使用するユーザーblog_userを作成します。パスワードはuser_passwordにしたとします。

% sudo -u postgres -H createuser blog_user --pwprompt

続いて、レプリケーション時にスレーブノードから接続するユーザーblog_replicationを作成します。パスワードはreplication_passwordにしたとします。

% sudo -u postgres -H createuser blog_replication --pwprompt --replication

blogデータベースを作成します。blog_userをオーナーにします。

% sudo -u postgres -H createdb --owner blog_user blog

blogデータベースをセットアップします。これはスーパーユーザーで実行する必要があります。

% sudo -u postgres -H psql blog

データベースにpglogicalをインストールします。詳細はpglogicalのドキュメント(英語)を参照してください。

CREATE EXTENSION pglogical;
GRANT USAGE ON SCHEMA pglogical TO blog_user;
GRANT USAGE ON SCHEMA pglogical TO blog_replication;
GRANT SELECT ON ALL TABLES IN SCHEMA pglogical TO blog_replication;

PGroongaもインストールします。詳細はPGroongaのドキュメントを参照してください。

CREATE EXTENSION pgroonga;
GRANT USAGE ON SCHEMA pgroonga TO blog_user;

スーパーユーザー権限が必要なのはここまでです。blog_userで接続しなおします。

% psql --user blog_user --host 192.168.0.16 blog

テーブルとPGroongaのインデックスを作成します。いくつかデータも投入します。PGroongaのインデックス作成方法・使い方についてはPGroongaのチュートリアルを参照してください。

CREATE TABLE posts (
  id text PRIMARY KEY,
  title text NOT NULL,
  body text NOT NULL
);
CREATE INDEX posts_full_text_index ON posts USING pgroonga (id, title, body);
INSERT INTO posts VALUES ('2016-03-20-pgroonga',
                          'はじめてのPGroonga',
                          'PGroongaを使いはじめました!');
INSERT INTO posts VALUES ('2016-03-21-pglogical',
                          'pglogicalにトライ',
                          'pglogicalを試しています。PGroongaと一緒に使えるかな?');
INSERT INTO posts VALUES ('2016-03-22-pgroonga-and-pglogical',
                          'PGroongaとpglogical',
                          'pglogicalとPGroongaを一緒に使えました!');

インデックスを使って「一緒」が含まれる投稿を全文検索してみます。

SET enable_seqscan = off;
SELECT body, pgroonga.score(posts) FROM posts WHERE body %% '一緒';
--                          body                          | score 
-- -------------------------------------------------------+-------
--  pglogicalを試しています。PGroongaと一緒に使えるかな? |     1
--  pglogicalとPGroongaを一緒に使えました!               |     1
-- (2 行)

うまく動いていますね。

それでは、blogデータベースをレプリケーション対象にしましょう。

まず、ノードとして登録します。これは1度だけやる処理です。詳細はpglogicalのドキュメント(英語)を参照してください。

SELECT pglogical.create_node(
  node_name := 'master',
  dsn := 'host=192.168.0.16 port=5432 dbname=blog'
);

現在のblogデータベース(のpublicスキーマ)内のすべてのテーブルをレプリケーション対象にします。

SELECT pglogical.replication_set_add_all_tables('default', ARRAY['public']);

新しくテーブルを追加したときはこの処理を再度実行する必要があることに注意してください。そうしないとレプリケーション対象になりません。これはハマりポイントなので最後に改めて言及します。

これでマスターのセットアップは完了です。

スレーブノードのセットアップ

スレーブノードのセットアップは基本的なセットアップはマスターノードと同じですが、省略せずに説明します。

PostgreSQLをパッケージでインストールします。

% sudo rpm -ivh http://yum.postgresql.org/9.5/redhat/rhel-$(rpm -qf --queryformat="%{VERSION}" /etc/redhat-release)-$(rpm -qf --queryformat="%{ARCH}" /etc/redhat-release)/pgdg-centos95-9.5-2.noarch.rpm
% sudo -H yum install -y postgresql95-server
% sudo -H /usr/pgsql-9.5/bin/postgresql95-setup initdb
% sudo -H systemctl enable postgresql-9.5

pglogicalをパッケージでインストールします。詳細はpglogicalのインストールドキュメント(英語)を参照してください。

% sudo -H yum install -y postgresql95-contrib
% sudo -H yum install -y http://packages.2ndquadrant.com/pglogical/yum-repo-rpms/pglogical-rhel-1.0-1.noarch.rpm
% sudo -H yum install -y postgresql95-pglogical

PGroongaをパッケージでインストールします。詳細はPGroongaのインストールドキュメントを参照してください。

% sudo -H yum install -y http://packages.groonga.org/centos/groonga-release-1.1.0-1.noarch.rpm
% sudo -H yum install -y postgresql95-pgroonga

これで必要なパッケージはすべてインストールできたので設定をします。

まずはpostgresql.confを設定します。

外部からの接続を受け付けるようにするため、listen_address*にします。

/var/lib/pgsql/9.5/data/postgresql.conf:

listen_addresses = '*'

続いてpglogical用の設定をします。詳細はpglogicalのドキュメント(英語)を参照してください。

次の項目を設定します。

項目
max_replication_slots 1
shared_preload_libraries 'pglogical'

pglogicalでは複数のマスターノードからレプリケーションすることもできます。その場合は、max_replication_slotsmax_worker_processesをマスターノード数以上に設定する必要があります。

今回はmax_worker_processesはデフォルト(8)から変更しません。理由は、今回の例ではマスターノードが1つなのでデフォルトの8で十分だからです。

具体的には次のように設定します。

/var/lib/pgsql/9.5/data/postgresql.conf:

wal_replication_slots = 1

shared_preload_libraries = 'pglogical'

続いてpg_hba.confを設定します。なお、この設定はpglogicalの設定ではなく一般的な設定です。自分の利用方法に合わせて調整してください。ただし、データベース操作用のユーザー名はマスターノードと合わせておいた方がデフォルト設定を使えるため便利です。

次の設定を追加します。

  • 後で作成するデータベース操作用のユーザー(blog_user)がローカルネットワークからblogデータベースへパスワード接続することを許可

/var/lib/pgsql/9.5/data/postgresql.conf:

host blog blog_user 192.168.0.0/24 md5

これで起動前の設定は完了したのでPostgreSQLを起動します。

% sudo -H systemctl start postgresql-9.5

データベースを使用するユーザーblog_userを作成します。

% sudo -u postgres -H createuser blog_user --pwprompt

レプリケーション初期化時のデータ同期時に必要になる(マスターノードでGRANT ... TO blog_replicationしているため)のでblog_replicationユーザーを作成します。ログインするわけではないのでログイン不可にします。

% sudo -u postgres -H createuser blog_replication --no-login

blogデータベースを作成します。blog_userをオーナーにします。

% sudo -u postgres -H createdb --owner blog_user blog

blogデータベースをセットアップします。これはスーパーユーザーで実行する必要があります。

% sudo -u postgres -H psql blog

データベースにpglogicalをインストールします。詳細はpglogicalのドキュメント(英語)を参照してください。

CREATE EXTENSION pglogical;
GRANT USAGE ON SCHEMA pglogical TO blog_user;

PGroongaもインストールします。詳細はPGroongaのドキュメントを参照してください。

CREATE EXTENSION pgroonga;
GRANT USAGE ON SCHEMA pgroonga TO blog_user;

スーパーユーザー権限が必要なのはここまでです。blog_userで接続しなおします。接続先のIPアドレスは捜査対象のスレーブノードのIPアドレスになっているか確認してください。

% psql --user blog_user --host 192.168.0.17 blog

まず、ノードとして登録します。これは1度だけやる処理です。スレーブノードごとnode_nameを変えてください。以下は詳細はpglogicalのドキュメント(英語)を参照してください。

なお、ここで設定した接続情報はレプリケーション開始時にデータベースの内容を同期するときに使われます。その際、スーパーユーザー権限が必要になるのでスーパーユーザーの接続情報を指定してください。以下の設定ではUNIXドメインソケット経由でpostgresユーザーで接続します。(pglogical.create_subscriptionのオプションで同期を無効にできます。その場合はスーパーユーザー権限は必要ありません。)

SELECT pglogical.create_node(
  node_name := 'slave1',
  dsn := 'dbname=blog'
);

レプリケーションを開始します。これも1度だけやる処理です。subscription_nameはスレーブごとに違う値にします。hostはマスターノードのIPアドレス(またはホスト名)にします。

SELECT pglogical.create_subscription(
    subscription_name := 'subscription1',
    provider_dsn := 'host=192.168.0.16 port=5432 dbname=blog user=blog_replication password=replication_password'
);

レプリケーションを開始するとデータを同期するのでpostsテーブルができてデータが入っています。

SELECT * FROM posts;
--                 id                 |        title        |                      
--    body                          
-- -----------------------------------+---------------------+----------------------
-- ---------------------------------
--  2016-03-20-pgroonga               | はじめてのPGroonga  | PGroongaを使いはじめ
-- した!
--  2016-03-21-pglogical              | pglogicalにトライ   | pglogicalを試していま
-- す。PGroongaと一緒に使えるかな?
--  2016-03-22-pgroonga-and-pglogical | PGroongaとpglogical | pglogicalとPGroongaを
-- 一緒に使えました!
-- (3 行)

もちろん、PGroongaを使った高速日本語全文検索も動きます。

SET enable_seqscan = off;
SELECT body, pgroonga.score(posts) FROM posts WHERE body %% '一緒';
--                          body                          | score 
-- -------------------------------------------------------+-------
--  pglogicalを試しています。PGroongaと一緒に使えるかな? |     1
--  pglogicalとPGroongaを一緒に使えました!               |     1
-- (2 行)

マスターノードでデータを追加するとスレーブノードから参照できます。

マスターノードで実行:

INSERT INTO posts VALUES ('2016-03-23-pgroonga-postgresql-96',
                          'PostgreSQL 9.6でPGroonga',
                          'PGroongaはPostgreSQL 9.6と一緒でも使えた!');

スレーブノードで実行:

SET enable_seqscan = off;
SELECT body, pgroonga.score(posts) FROM posts WHERE body %% '一緒';
--                          body                          | score 
-- -------------------------------------------------------+-------
--  pglogicalを試しています。PGroongaと一緒に使えるかな? |     0
--  pglogicalとPGroongaを一緒に使えました!               |     0
--  PGroongaはPostgreSQL 9.6と一緒でも使えた!            |     0
-- (3 行)

どちらのスレーブノードでも新しく追加したレコードがヒットします。

まとめ

pglogicalとPGroongaを使ったレプリケーション対応の高速日本語全文検索可能なPostgreSQLクラスターの作り方を説明しました。

pglogicalのドキュメントの「4. Limitations and Restrictions」にある通り、いくつか制限はありますがレコードの追加・更新・削除をしてもスレーブノードで検索できるという基本的なことは実現可能です。制限は自分のユースケースでクリティカルなものか確認し、pglogicalの使用を検討してください。なお、制限とは、たとえば、DDL(CREATE TABLEなど)はレプリケーションされない、PRIMARY KEYがないと更新・削除がレプリケーションされない、などです。

DDLがレプリケーションされないことが運用に与える影響について少し補足します。

DDLがレプリケーションされないということはCREATE TABLEがレプリケーションされないということです。つまり、マスターノードでテーブルを作成したらスレーブノードでも同じテーブルを定義する必要があるということです。さらに、テーブルを定義するだけではレプリケーションされないなので、テーブルを作ったらレプリケーションの設定も更新する必要があります。具体的にはマスターノードで次のSQLを実行します。(pglogicalのドキュメントではトリガーでこの作業を自動化する方法を紹介しています。)

SELECT pglogical.replication_set_add_all_tables('default', ARRAY['public']);

次の順序で操作した場合はこれだけでOKです。

  1. マスターノードでテーブル作成
  2. スレーブノードでテーブル作成
  3. マスターノードでpglogical.replication_set_add_all_tablesを実行
  4. マスターノードでデータ追加

しかし、次のようにデータを追加してからpglogical.replication_set_add_all_tablesを実行した場合はもう1つやることがあります。

  1. マスターノードでテーブル作成
  2. スレーブノードでテーブル作成
  3. マスターノードでデータ追加
  4. マスターノードでpglogical.replication_set_add_all_tablesを実行

次のようにスレーブノードでpglogical.alter_subscription_resynchronize_tableを実行します。

SELECT pglogical.alter_subscription_resynchronize_table(
    subscription_name := 'subscription1',
    relation := 'posts'
);

これでデータが同期されます。

それでは、pglogicalとPGroongaで高速日本語全文検索を実現してください。

タグ: Groonga
2016-03-22

OSS Gateワークショップ2016-03-26を開催 #oss_gate

2016年3月26日に2回目のOSS Gateワークショップを開催しました。

ワークショップの様子

前回のOSS Gateワークショップは、1回目ということで「メンターを経験すること」を重視しました。OSS Gateメンバーの中にワークショップ経験者が少なかったからです。

今回のワークショップで重視したことは次の2つです。

  • 前回のフィードバックを踏まえて前よりもうまくやれないか試すこと
  • たくさんのビギナーに対応できるか試すこと

前者関連では、アイスブレイク時の交流を増やすこと、名札を用意すること、教えすぎないことなどに取り組みました。今回のふりかえりではそれらに対する課題は前回ほどでてこなかったので、前よりもうまくできたと言えます。

後者は課題が残りました。

一時期は15名ほどのビギナー登録がありました。そのため、それらに対応できるようにメンターとして参加してくれる人を呼びかけました。その結果、17名がメンターとして登録してくれました。

前日・当日で5名ほどのビギナーがキャンセルし、実際に当日参加したのは4名でした。一方、メンターは登録していた人がほとんど来てくれたため、メンターの数の方がすごく多くなってしまいました。そのため、メンターとして来たがビギナーとして参加した人たちがいました。

なお、4名というのは前回と同じビギナー数なので、「たくさんのビギナーに対応できるか試す」という観点では今回はあまり成果はありませんでした。

しかし、図らずも「メンターを集めることはなんとかできる」ということはわかりました。前回ビギナーとして参加した人のうち75%は今回も参加してくれました。(ビギナーとして参加の人もメンターとして参加の人もいました。)他にも今回から新しくメンターとして参加してくれた人たちもいます。ワークショップを継続していればメンターとして協力してくれる人は増やしていけそうです。

こういったことを踏まえて、次回以降はメンターを集めることよりもビギナーを集めることに注力したほうがよさそうだということになりました。

次回のチャレンジはもう1つあります。2016年5月28日開催予定の次回のワークショップは、これまで進行役をしてきた須藤が参加できないことがわかっています。そのため、須藤以外の人が進行役をやってみる、ということにチャレンジする必要があります。なお、そのために今回のワークショップの進め方をメモしてあります。シナリオ資料もあるので、おそらく他の人が進行しても大丈夫でしょう。

ワークショップ中は作業ログを残しながら進めています。これを見れば、未経験者の人でも1日のワークショップでなにかしら「OSSの開発に参加する」ことができるとわかるでしょう。

ワークショップ後のアンケートでは、すべてのビギナーがこれからもOSS開発に参加していこうという気持ちになっていました。実際、ワークショップ後もOSS開発を続けている人はいます。Gitterのoss-gate/workshopチャットでOSS Gateメンバーとやりとりしています。ワークショップ後も助け合える関係ができていて素晴らしいですね。

今後も隔月最終土曜日にワークショップを開催していく予定です。これまではコデアルさんに会場提供してもらっていましたが、今後はクラウドワークスさんにも会場提供してもらえることになりました。2016年7月30日開催予定のワークショップはクラウドワークスさんで開催する予定です。

OSS Gateワークショップに興味のある方はビギナーでもメンターでもぜひご参加ください。また、このOSS Gateという取り組みをまわりの人へも伝えてください。OSS Gateの説明には公式サイトの説明を活用してください。

2016-03-30

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|