テストをコケたままにしない理由
はじめに
現在担当しているモバイルアプリゲームではマスターデータのテストを行っています。
- リレーション先のデータが存在しているか
- 入力されているデータが想定しているデータか
- シソーラスのチェック
などヒューマンエラーがおきやすい所、おきた所を防ぐためにマスターデータに対してもテストを行っています。
本題
表題だけみると当たり前のことですが、運用をしていると施策のタイミング変更などやんごとない理由で上記であげたテストがコケる不十分な状態のマスターデータをデプロイする必要が出てきます。 CIサーバーではテストを回すと常にそのテストがコケた状態になります。
それがチーム内で共有されているとしてもテストがコケた状態だと それ今はコケるテストだから という状態が続く可能性があります。
その状態が続くと同一テスト内で他のテストがコケた場合に気づきにくくなります。
じゃあどうする
なので自分の場合は、そのようなケースが出てきた時はテストコードを修正し該当のデータだけテストをスキップさせる という方法を取っています
まとめ
テストこけっぱなしにするの(・へ・)ヨクナイ!
本番のマスターのDBでhistoryを残さないために
本番のマスターのDBで直接SQLを叩くことはほとんどありません。 が、どうしてもやんごとない事情でSQLを直接叩かないといけない時がでてきます。
SQLの実行自体はいいのですが、他の人が誤ってhistoryから実行したら怖いですね。
そうならないように現在のプロジェクトでは master_mysql
のようなコマンドを用意し
どうしても直接本番のマスターDBでSQLを実行しないと行けない場合はそれ経由で実行しています。
master_mysql の中身は
#!/bin/sh MYSQL_HISTFILE=/dev/null mysql -uuser_name -hhostname -ppassword db_name "$@"
こんな感じになってます。 これで他の人がhistory経由でミスすることが多少は防げると思います。
あわせて読みたい
夏のドキッとしたmigrateの話
はじめに
現在担当しているプロジェクトではmigrateにGitDDLを使っております。 先日本番環境でそれなりにデータが入ったテーブルに対してALTERをかけました。 その際に起こったちょっとドキッとする話を今後の自分のためにも書いてこうと思います。
GitDDLについての記事はこちら: #5「GitDDLまじイノベーティブ」 tech.kayac.com Advent Calendar 2012
起こったこと
下記の用な変更を本番のDBに対して適用することになりました
CREATE TABLE atarashi_table ( id integer(10) unsigned NOT NULL auto_increment, nanka text NULL, iroiro text NULL, PRIMARY KEY (id), ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4; ALTER TABLE oki_table ADD COLUMN tsuika_column integer(10) unsigned NOT NULL DEFAULT 0;
migrate自体はこの様なコマンドを用意して実行しています
./env-exec perl script/db/migrate
それなりに大きいテーブルだったので、stgでの確認も十分行い大丈夫だろうと実行
しかし現実は甘くはなかった
Connection Time Out…
とmigrate中にmysqlとのconnectionが切断された。 dbの設定で
mysql_connect_timeout=15 mysql_write_timeout=15 mysql_read_timeout=10
と設定していたので実行中にその時間を経過してしまったようである。 非常に焦る。
恐る恐る show tables をしてみると atarashi_table
はできている
ただ、この段階では oki_table
には まだ tuika_column
が追加されていない。
とりあえず落ち着いて現状を他のPGに共有。 すると先輩エンジニアから mysql のプロセスを見てみる。
mysql> SHOW PROCESSLIST;
するとcolumnを追加していると思われるプロセスが。
少し待つと oki_table
に tsuika_column
が追加されている事を確認。
とりあえずは一安心。
ただ migrate コマンド自体は途中で失敗したため git_ddl_version
が更新されず
./env-exec perl script/db/migrate
を再度行おうとすると
CREATE TABLE atarashi_table ( id integer(10) unsigned NOT NULL auto_increment, nanka text NULL, iroiro text NULL, PRIMARY KEY (id), ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4; ALTER TABLE oki_table ADD COLUMN tsuika_column integer(10) unsigned NOT NULL DEFAULT 0;
の差分がでて実行すると
atarashi_table already exists
と言われ もちろんだけれど実行できない。
なので無事migrateが終わっているstaging環境のgit_ddl_version
のversionでupdateすることに
UPDATE git_ddl_version SET version = ‘hogehogeversion’
なんとか問題なく終了
今後の対策
migrateの実行ファイル内で connection time を長めに設定し直すか、migrateを実行する環境の設定ファイルで長めにするかのどちらかかと思っています。
まとめ
- migrationの実行はいつもドキドキする
- 何かあっても大丈夫なようにメンテナンスは余裕のある見積もりを。
- それでも何か合った時はまず共有しましょう
- やばいと思ったら深呼吸
- mysql> SHOW PROCESSLIST;
今回はconnectionが切られたけどプロセスは生きていて大事には至らなくて本当によかった事案。 今後も起こりえる可能性があるので、未来の自分のためのエントリになります。
branchの絞り込みをpecoを使うようにしたら捗る
alias br='git checkout $(git branch | peco)'
を .bashrc
に記述するとbranchをいい感じに絞り込めるので非常に良い。
雑にindexの効果を調べた
メンターをしている後輩くんのソースをレビューしててindexが足りてなかった。 それがなんでダメなのか雑に調べた。
- player_hoge
- id
- player_id
- index_id
- created_at
- updated_at
みたいなテーブルがある。 使われ方はplayer_idとindex_idの2つでselectする機会が多い。
そのテーブルにplayer_id のみindexが貼ってあった。 こういう場合は player_id と index_id の2つにindexを貼ったほうが早くなる。 はず。
なので調べるように
- index 貼ってない
- player_id いindexを貼った場合
- player_id, index_id に index を貼った場合
sqlはこんな感じ
DROP TABLE IF EXISTS `no_index`; CREATE TABLE `no_index` ( `id` INTEGER unsigned NOT NULL auto_increment, `player_id` INTEGER unsigned NOT NULL DEFAULT 0, `index_id` INTEGER unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4; DROP TABLE IF EXISTS `one_index`; CREATE TABLE `one_index` ( `id` INTEGER unsigned NOT NULL auto_increment, `player_id` INTEGER unsigned NOT NULL DEFAULT 0, `index_id` INTEGER unsigned NOT NULL DEFAULT 0, INDEX `player_id_idx` (`player_id`), PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4; DROP TABLE IF EXISTS `both_index`; CREATE TABLE `both_index` ( `id` INTEGER unsigned NOT NULL auto_increment, `player_id` INTEGER unsigned NOT NULL DEFAULT 0, `index_id` INTEGER unsigned NOT NULL DEFAULT 0, INDEX `player_id_index_id_idx` (`player_id`, `index_id`), PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4;
使ったデータはこんな感じで作りました。 で調査ようのscriptはこんな感じです
use DBI; use DBD::mysql; use Benchmark qw/timethese cmpthese/; my $d = 'DBI:mysql:index_research'; my $u = 'root'; my $p = ''; my $dbh = DBI->connect($d, $u, $p); my $result = timethese(10, { 'player_id index_id use no index' => sub { for my $id (1..100) { my $player_id = $id * 20; my $sql = "SELECT * FROM no_index WHERE player_id = $player_id AND index_id = $id"; my $sth = $dbh->prepare($sql); $sth->execute; $sth->finish; } }, 'player_id index_id use player_id index' => sub { for my $id (1..100) { my $player_id = $id * 20; my $sql = "SELECT * FROM one_index WHERE player_id = $player_id AND index_id = $id"; my $sth = $dbh->prepare($sql); $sth->execute; $sth->finish; } }, 'player_id index_id use player_id index_id index' => sub { for my $id (1..100) { my $player_id = $id * 20; my $sql = "SELECT * FROM both_index WHERE player_id = $player_id AND index_id = $id"; my $sth = $dbh->prepare($sql); $sth->execute; $sth->finish; } }, }); cmpthese $result;
で結果が以下
player_id index_id use no index 100.0/s -- -30% -70% player_id index_id use player_id index 143/s 43% -- -57% player_id index_id use player_id index_id index 333/s 233% 133% --
やっぱりplayer_id と index_id 両方にindex貼ったほうが早かった。
じゃあ、 player_id だけ select して取る場合はどちらが早いのかと思ったのでそれも調べた
my $result_only_player_id = timethese(10, { 'player_id use no index' => sub { for my $id (1..100) { my $player_id = $id * 20; my $sql = "SELECT * FROM no_index WHERE player_id = $player_id"; my $sth = $dbh->prepare($sql); $sth->execute; $sth->finish; } }, 'player_id use player_id index' => sub { for my $id (1..100) { my $player_id = $id * 20; my $sql = "SELECT * FROM one_index WHERE player_id = $player_id"; my $sth = $dbh->prepare($sql); $sth->execute; $sth->finish; } }, 'player_id use player_id index_id index' => sub { for my $id (1..100) { my $player_id = $id * 20; my $sql = "SELECT * FROM both_index WHERE player_id = $player_id"; my $sth = $dbh->prepare($sql); $sth->execute; $sth->finish; } }, }); cmpthese $result_only_player_id;
結果がこちら
player_id use no index 90.9/s -- -55% -64% player_id use player_id index 200/s 120% -- -20% player_id use player_id index_id index 250/s 175% 25% --
player_id だけの方が早いかと思ったけどそうでもなかった。 データ量がおこまで多くないと違うのかもしれない。
まとめ
index貼るときはどう使うかも考えて貼ったほうがいいですね。