今日は MySQL::Sandbox を利用した MySQL を使ったデータベース処理のテストについて紹介します。
MySQL の実行環境を手軽に作れる CLI ツール群です。
これは簡単。
$ sudo cpan -i MySQL::Sandbox
その OS での MySQL を $HOME/opt/mysql にダウンロードしてから、MySQL::Sandbox に入っている make_sandbox コマンドで、
$ make_sandbox ~/opt/mysql/mysql-{version}-{dist}.tar.gz
を実行すると、Sandbox が作られて、さらに展開済みのバイナリが ~/opt/mysql 以下にバージョン番号ごとに置かれます。
使いたいバージョンの mysql を一通り揃えておくといいです。 ちなみに $HOME/opt/mysql である必要性は無いのですが、デフォルトの探索パスがここなので、このディレクトリ以下に作るのが吉です。
現在、自分の環境には、
がインストールされています。
make_sandbox コマンドの usage は以下のような感じです。
usage: ./make_sandbox version [options] {VERSION|tarball_full_name}.
ここのオプションは --help では --export-binaries しか記載されていませんが、実はもっと色々な使い方が出来ます。
例えば --sandbox-directory オプションを指定すると、好きな名前を付けて同一バージョンの Sandbox を複数作る事が出来ます。
$ make_sandbox 5.4.1 --sandbox_directory yapcasia2009 --verbose
こうすると、通常は $HOME/sandboxes/yapcasia2009 と言うディレクトリ以下に mysqld の起動に必要な各種ファイルや、便利なスクリプトが展開されます。
覚えておくべきオプションとしては、
辺りを覚えておけば make_sandbox では困る事は無いです。
$ ls ~/sandboxes/yapcasia2009/ USING change_ports data load_grants my.sandbox.cnf restart start use change_paths clear grants.mysql my proxy_start send_kill stop
my.sandbox.cnf がいわゆる my.cnf で、
[mysql]
prompt='mysql [\h] {\u} (\d) > '
#
[client]
user = msandbox
password = msandbox
port = 5410
socket = /tmp/mysql_sandbox5410.sock
[mysqld]
user = zigorou
port = 5410
socket = /tmp/mysql_sandbox5410.sock
basedir = /Users/zigorou/opt/mysql/5.4.1
datadir = /Users/zigorou/sandboxes/yapcasia2009/data
pid-file = /Users/zigorou/sandboxes/yapcasia2009/data/mysql_sandbox5410.pid
#log-slow-queries = /Users/zigorou/sandboxes/yapcasia2009/data/msandbox-slow.log
#log = /Users/zigorou/sandboxes/yapcasia2009/data/msandbox.log
#
# additional options passed through 'my_clause'
#
log-error=msandbox.err
と言う設定ファイルになっています。
とりあえずは MySQL のコマンドラインでこのインスタンスに接続したい場合は、
~/sandboxes/yapcasia2009/use
を実行すると、今しがた作った Sandbox に対して接続する事が出来ます。
と言うコマンドも含まれていますが、その名前の通りです。作成した Sandbox の mysqld を start/stop/restart します。
ところでこの Sandbox を奇麗さっぱり削除したい場合は次のようにします。
$ sbtool -o delete -s ~/sandboxes/yapcasia2009
sbtool は MySQL::Sandbox の管理ツールで、delete 以外にも様々な機能があります。
make_sandbox を使ったデモ
$ make_sandbox 5.1.36 --sandbox_directory yapcasia2009 --no_confirm --force --verbose
$ ~/sandboxes/yapcasia2009/use
$ grep port ~/sandboxes/yapcasia2009/my.sandbox.cnf
でポートを確かめてから、
$ env DBI_DSN='dbi:mysql:host=localhost;port=5136;' DBI_USER=msandbox perl -MData::Dumper -MDBI -e 'my $dbh = DBI->connect; print Dumper($dbh->selectall_arrayref("show databases;"));'
$ sbtool -o delete -s ~/sandboxes/yapcasia2009
実はレプリケーション環境やら、マルチマスターなどと言った環境を好きに作る事が出来ます。
コマンドを使うとそういう事が出来ます。今回はこの説明は割愛しますが、この辺りの話は既にブログで紹介したのでそちらを見て下さい。
お手軽に使える MySQL::Sandbox ですが、実は問題点も結構あります。
と言う訳でテストしたかったので、MySQL::Sandbox を操る為のモジュールを作ってみました、と言うのが今日の本題です
とても簡単に使う事が出来ます。
Sandbox を作成してデータベース一覧を列挙するだけのプログラムは以下のようになります。
use strict;
use Test::More;
use MySQL::Sandbox::Frontend;
my $sb = MySQL::Sandbox::Frontend->create('single', 'test', undef, +{ debug => 0, });
ok($sb, 'create sandbox');
my $dbh = $sb->dbh;
isa_ok($dbh, 'DBI::db');
my $rs = $dbh->selectall_arrayref('SHOW DATABASES;');
note(explain($rs));
is_deeply($rs, [ ['information_schema'], ['mysql'], ['test'] ], 'databases');
ok($sb->delete, 'delete sandbox');
done_testing;
基本的な使い方としては、1インスタンスだけを立ち上げたい時は、
MySQL::Sandbox::Frontend->create(
'single', # 1インスタンスのみ
'yapcasia2009', # --sandbox_directory と同じ
'5.1.36', # バージョン
+{ # その他のオプション
debug => 1,
},
);
と言う感じです。簡単ですね。
ちなみにバージョンを省略すると、探索パス上にあるバージョンで最も新しい物を自動的に選択してくれます。
またテーブル定義を行い、SHOW TABLES するサンプルはこちら。
use strict;
use Test::More;
use MySQL::Sandbox::Frontend;
my $sb = MySQL::Sandbox::Frontend->create('single', 'test', undef, +{ debug => 0, });
ok($sb, 'create sandbox');
my $dbh = $sb->dbh;
isa_ok($dbh, 'DBI::db');
diag('via hide-k blog http://blog.hide-k.net/archives/2008/06/kazuhodbic.php');
$dbh->do('create database miniblog;');
$dbh->do('use miniblog;');
$dbh->do(<< 'SCHEMA');
CREATE TABLE user (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
screen_name varchar(255) NOT NULL,
PRIMARY KEY (id)
) ENGINE=innodb;
SCHEMA
$dbh->do(<< 'SCHEMA');
CREATE TABLE message (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
user_id int (10) unsigned NOT NULL,
body varchar(255) NOT NULL,
PRIMARY KEY (id),
KEY userid_id_id(user_id, id)
) ENGINE=innodb;
SCHEMA
$dbh->do(<< 'SCHEMA');
CREATE TABLE follower (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
user_id int (10) unsigned NOT NULL,
follower_id int (10) unsigned NOT NULL,
PRIMARY KEY (id),
UNIQUE (user_id, follower_id),
KEY user_id_follower_id (user_id, follower_id),
KEY follower_id(follower_id)
) ENGINE=innodb;
SCHEMA
my $tables = $dbh->selectall_arrayref('SHOW TABLES;');
note(explain($tables));
is_deeply($tables, [ ['follower'], ['message'], ['user'] ], 'created tables');
k($sb->delete, 'delete sandbox');
done_testing;
最後にレプリケーション環境を作るサンプルはこんな感じです。
use strict;
use Test::More;
use MySQL::Sandbox::Frontend;
my $slaves = 2;
my $sb = MySQL::Sandbox::Frontend->create('replication', 'yokohama_pm', undef, +{ how_many_slaves => $slaves, debug => 0 });
is( @{$sb->nodes}, 3, 'How many nodes' );
my $dbh_master = $sb->master->dbh;
my $master_st = $dbh_master->selectrow_hashref('SHOW MASTER STATUS');
$dbh_master->do('USE test');
$dbh_master->do('CREATE TABLE IF NOT EXISTS hidek( id int primary key auto_increment, name varchar(32) ) Type=InnoDB');
note('master position: ' . $master_st->{Position});
for my $idx ( 1..$slaves ) {
my $dbh = $sb->nodes->[$idx]->dbh;
my $slave_st = $dbh->selectrow_hashref('SHOW SLAVE STATUS;');
note("slave ($idx) position: " . $slave_st->{Exec_Master_Log_Pos});
ok( $master_st->{Position} >= $slave_st->{Exec_Master_Log_Pos}, 'slave binlog position less than master' );
$dbh->do('USE test');
note(explain($dbh->selectall_arrayref('SHOW TABLES')));
}
$dbh_master->do('DROP TABLE hidek');
$sb->delete;
done_testing;
START/STOP SLAVE などを用いれば一意的に slave ノードの一部を切り離したりも出来ます。
などなど、引き続きのんびり開発して行こうと思います。
ご清聴ありがとうございました。