Pacific のクライアントAPI (仮) について
先週、概要を紹介させていただいた Pacific について。まだ API をフリーズしていないつもりなのですが、だいぶ整ってきた気がするので、ざっくりまとめておきたいと思います。
インストール手順
- Thrift をインストール注1
- Pacific の svn レポジトリからチェックアウト
- Perl ドライバを make (cd driver-perl && perl Makefile.PL && make all test install)
- リゾルバを make (cd resolver && make)
テーブルのセットアップ手順
テーブルのセットアップは、pschema コマンドを使って行います。
# リゾルバの裏側の MySQL は 127.0.0.1:33060 で動作
#
# プライマリテーブル「user」を作成
# ・ 分散キーの名前は「username」
# (型は varchar(255) not null charset utf8 collate utf8-bin 固定)
# ・ カラムとして realname varchar(255) not null,last_tweet_at int unsigned not null
# ・ ノード内で、セカンダリテーブルとのリレーションを表現するカラム _iid
# も生成される
#
pschema create-primary --manager=127.0.0.1:33060 --primary=user --hostport=127.0.0.1:33061 --primary-key-name=username 'realname varchar(255) not null,last_tweet_at int unsigned not null'
# セカンダリテーブル「tweet」を作成
# ・ カラムとして mtime と body (と 上記 _iid)
#
pschema create-secondary --manager=$MANAGER --primary=user --secondary=tweet --primary-key=mtime 'mtime int unsigned not null,body varchar(255) not null'
この結果、MySQL ノードには、以下のようなスキーマのプライマリテーブルが作成されます注2。ER図で書くと、こんな感じになります。
CREATE TABLE `user` (
`_iid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
`realname` varchar(255) NOT NULL,
PRIMARY KEY (`_iid`),
UNIQUE KEY `_pac_key` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `tweet` (
`_iid` int(10) unsigned NOT NULL,
`mtime` int(10) unsigned NOT NULL,
`body` varchar(255) NOT NULL,
PRIMARY KEY (`_iid`,`mtime`),
CONSTRAINT `tweet_ibfk_1` FOREIGN KEY (`_iid`) REFERENCES `user` (`_iid`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
リゾルバを起動
先に make した pacific_resolver を起動します。TCP ポート番号は現在のところ 9306 に固定です。リゾルバが使用するデータベースの情報は、環境変数を用いて渡すことができます注3。
$ PACIFIC_MGR_MYSQL_HOST=127.0.01 PACIFIC_MGR_MYSQL_PORT=33060 ./pacific_resolver
Perl クライアントからのアクセス方法
まず、Pacific のドライバオブジェクトを生成します。
use Pacific::Driver::Direct;
use Pacific::Driver::Direct::DBI::DBD::mysql;
use Pacific::Driver::Direct::Resolver;
my $pac = Pacific::Driver::Direct->new({
dbi => Pacific::Driver::Direct::DBI::DBD::mysql->new({
user => 'root',
pass => undef,
database => 'pacific',
}),
resolver => Pacific::Driver::Direct::Resolver->new({
host => '127.0.0.1',
port => 9306,
}),
});
キーによるテーブルのルックアップは、イテレータを使って行います。Pacific は、渡されたキーがどの RDBMS ノードに属するかリゾルバに問い合わせを行い、そのキーに属するデータにリードロックをかけ、順次イテレータに値を渡してきます。
my @rows;
for (my $iter = $pac->query_iter('user', [ qw/Alice Bob Eve/ ]);
$iter->next;
undef) {
my $r = $iter->dbh->selectall_arrayref(
'SELECT username,realname FROM user WHERE ' . $iter->key_expr,
{},
$iter->key_values,
) or die $iter->dbh->errstr;
push @rows, @$r;
}
レンジクエリ(範囲を指定した検索)も同様に記述することができます。範囲指定の演算子は、< <= > >= を組み合わせて使うことができます。
# Bob 以降10人を取得
my @rows;
for (my $iter = $pac->query_iter('user', { '>=' => 'Bob' });
$iter->next;
undef) {
my $r = $iter->dbh->selectall_arrayref(
'SELECT username,realname FROM user WHERE ' . $iter->key_expr
. 'LIMIT ?',
{},
$iter->key_values,
10 - @rows,
) or die $iter->dbh->errstr;
push @rows, @$r;
last if @rows >= 10;
}
書き込みにあたっては、query_iter の代わりに modify_iter を使用します注4。
for (my $iter = $pac->modify_iter('user', qw[ /Alice Bob/ ]);
$iter->next;
undef) {
$iter->dbh->do(
'UPDATE user SET hitpoint=hitpoint+10 WHERE ' . $iter->key_expr,
{},
$iter->key_values,
) or die $iter->dbh->errstr;
}
ノード内でトランザクションを組むこともできます (下の例のように、単一のキーにアクセスする場合も、イテレータを使います)。
# tweet テーブルに発言を追加し、user テーブルの最終発言時刻を更新
for (my $iter = $pac->modify_iter(
'user', [ qw/Alice/ ], { transactional => 1 },
);
$iter->next;
undef) {
$iter->dbh->do(
'INSERT INTO tweet (_iid,mtime,body) VALUES'
. ' ((SELECT _iid FROM user WHERE username=?),?,G)',
{},
'Alice', $tweet_at, $tweet,
) or die $iter->dbh->errstr;
$iter->dbh->do(
'UPDATE user SET last_tweet_at=? WHERE user=?',
{},
$tweet_at, 'Alice',
) or die $iter->dbh->errstr;
}
また、より高レベルな ORM っぽいインターフェイスを提供する Pacific::Driver::Direct::Accessor モジュールもありますが、自分は元来 ORM 不要派で経験値が低いので、あまり深入りしたくない (深入りしたところでいいものができないと思ってる) 気持ちです。
それではひとまずこのあたりで。ノードの分割/再配置に使う prelocate コマンドについても、また書きたいと思います。
注2. ノード内部でリレーションを表現するために _iid という値を別途使用するのは、空間効率を高める一方で、データの分断につながります。ですので、今後、_iid を使わず、セカンダリテーブルにも直接分散キーを書き込むモデルをサポートすることも考えています
注3. 使用可能な環境変数については、MySQLDriver.cpp を参考にしてください
注4. 範囲を指定した更新については、現時点で未対応です
Comments