Home > 仕事のこと > DBIx::Class::Schema::Slave について

DBIx::Class::Schema::Slave について

先月末から DBIx::Class::Schema::Slave というモジュールを書いています。
DBIx::Class でプリケーション環境でマスタとスレーブに接続するためのライブラリはすでに CPAN にあがっていて

DBIx::Class::Storage::DBI::Replicated

http://search.cpan.org/~jrobinson/DBIx-Class-0.08099_01/lib/DBIx/Class/Storage/DBI/Replicated.pm

ただ、これは nitsuji さんの記事にあるように

DBIx::Classでスレーブに接続する - nitsujiの日記

http://d.hatena.ne.jp/nitsuji/20080418/1208446612

メソッドチェーンが変ることがなくインターフェイスはそのままなので使う側は特に何も気にすることなく使えるのがいいのだけれど、参照系のクエリは問答無用でスレーブに接続されてしまうのがちょっと難点。
インターフェースはそのままで、かつマスタとスレーブどちらも参照できるものをと思って作ったのが DBIx::Class::Schema::Slave。
ネームスペースの通り、DBIx::Class::Schema のレイヤで実装してあります。
POD に書いてありますが一応使い方を簡単に。
モデルとしてブログを挙げてみます。
MyApp::Schema, MyApp::Schema::Blog, MyApp::Schema::Article があるとします。
まずは、MyApp::Schema から。

package MyApp::Schema;

use strict;
use warnings;
use base qw/ DBIx::Class::Schema /;

__PACKAGE__->load_components( qw/ Schema::Slave / );
__PACKAGE__->slave_moniker('::Slave');
__PACKAGE__->slave_connect_info( [ 
    [ 'dbi:mysql:database:hostname=192.168.1.2', 'user', 'password', { ... } ],
    [ 'dbi:mysql:database:hostname=192.168.1.3', 'user', 'password', { ... } ],
    [ 'dbi:mysql:database:hostname=192.168.1.4', 'user', 'password', { ... } ],
 ] );
__PACKAGE__->load_classes;

まずは、load_components でコンポーネントとして DBIx::Class::Schema::Slave をロードします。
続いて、slave_moniker に '::Slave' なんかをセットします。
そして、slave_connect_info にスレーブの connect_info をセットします。
最後に、load_classes。

次は各テーブルのクラス。

package MyApp::Schema::Blog

use strict;
use warnings;
use base qw/ DBIx::Class /;

__PACKAGE__->load_components( qw/
    ResultSetManager
    ...
    ...
    Row::Slave
    Core
/ );
__PACKAGE__->has_many(
    articles => 'MyApp::Schema::Article',
    'blog_id',
);
# 以下通常の設定やメソッドを定義

各テーブルのクラスでは DBIx::Class::Schema::Slave に同梱されている DBIx::Class::Row::Slave を load_components するだけです。
DBIx::Class::Row::Slave はスレーブに対しての insert, update, delete で throw_exception します。
なので、create, update_or_create, update_all, delete_all なんかも問答無用で throw_excepotion されます。
find_or_new は throw_exception しません。find_or_create は find の場合は大丈夫ですが、create の場合は throw_exception されます。

使い方はこんな感じになります。

my $schema = MyApp::Schema->connect( @master_connect_info );
my $master_blog = $schema->resultset('Blog')->find( $id );
my $slave_blog  = $schema->resultset('Blog::Slave')->find( $id );

# マスタに対しての update, delete は正常に終了
$master_blog->title( $title );
$master_blog->update;
$master_blog->articles->delete;

# スレーブに対しての update, delete はエラー
$slave_blog->title( $title );
$slave_blog->update;
$slave_blog->articles->delete;

my $itr_slave_article = $schema->resultset('Blog::Slave')->search_related( 'articles', {}, {} );
# これもエラー
$itr_slave_article->delete;

スレーブにつなぎたいときは result_set に 'Blog::Slave' ('::Slave' は slave_moniker にセットした文字列) を渡せばいいだけです。
$schema->resultset('Blog::Slave')->articles で返ってくる Article はスレーブのものなので update や delete はできません。
逆に、$slave_article->blog で返ってくる Blog もスレーブのものになっているはずです。(これはテストしてないですけど。今度、テストに加えておきます)

MyApp::Schema に slave_connect_info でスレーブの接続情報を ARRAYREF of ARRAYREF でセットしていますが、どのスレーブにつなぎにいくかはデフォルトではランダムです。
もし、任意に接続先を設定したい場合は MyApp::Schema に sub select_connect_info() を定義してください。

package MyApp::Schema;

use strict;
use warnings;
use base qw/ DBIx::Class::Schema /;

__PACKAGE__->load_components( qw/ Schema::Slave / );
__PACKAGE__->slave_moniker('::Slave');
__PACKAGE__->slave_connect_info( [ 
    [ 'dbi:mysql:database:hostname=192.168.1.2', 'user', 'password', { ... } ],
    [ 'dbi:mysql:database:hostname=192.168.1.3', 'user', 'password', { ... } ],
    [ 'dbi:mysql:database:hostname=192.168.1.4', 'user', 'password', { ... } ],
 ] );
__PACKAGE__->load_classes;

sub select_connect_info {
    my $self = shift;

    my @connect_info = @{$self->slave_connect_info};
    my $connect_info = undef;

    # @connect_info から好きなものを選んでね

    return $connect_info;
}

先ほどバージョン 0.02200 を CPAN にあげたのでよかったら試してみてください。
0.02000 ~ 0.02101 はすっごいバグがあったので CPAN から削除しました。
手元にあったら使わないようにしてくださいね。

あぁ、そうそう。
DBIx::Class::Schema のレイヤでやっているので Catalyst で使う場合でも何も意識しなくても大丈夫です。

$c->model('Blog::Slave')->find( $id );
とか
$c->model('Article::Slave')->find( $id )->blog;

ただ、Catalyst から Model を切り離そうとゴリゴリしちゃってる場合はどうかわかりませんが。
まだまだ EXPERIMENTAL なのでおかしなところがあればフィードバックもらえたらうれしいです。
うれションします。

Comments:0

Comment Form

Trackbacks:0

TrackBack URL for this entry
http://hibinokoto.jp/mt/mt-tb.cgi/268
Listed below are links to weblogs that reference
DBIx::Class::Schema::Slave について from 日々のこと

Home > 仕事のこと > DBIx::Class::Schema::Slave について

Search
Feeds

Return to page top