Alligator Swamp

技術メモ

Chiba.pm #5 に行ってきた #chibapm

Chiba.pm #5に参加し、LTもして来ました。
初めてこういった場でLTをさせて頂いたので、とても緊張しましたが、良い経験になりました。

(reveal.jsで作った資料をPDFエクスポートしたらレイアウト崩れてしまいました)

内容は、最近使っているPath::Tinyというモジュールについての紹介とハマったことです。

感想

メイントークが「MySQLの歴史」と「職業訓練」でPerlが一切出てこなくて驚きましたが、どちらもとても興味深く面白い内容でした!
特にMySQLの歴史についてのトークは、業務でMySQLのバージョンアップ対応をやっていたこともあり、それについての知見が得られたのは嬉しい誤算でした。

主催者の@kaztrさん、@8GRTさん、ありがとうございました&お疲れ様でした!

ISUCON4の予選に参加してきた #isucon

ISUCON4の予選にkyokomiyasuunとチームを組んで参加してきました。
1日目に参加し、最終送信結果は15107、benchmark v2では17124と予選通過には程遠いスコアでした。
しかも、AMI審査方法の通りに実行してみたら見事にレギュレーション違反!
来年リベンジするためにも記録を残しておこうと思います。

予選当日までにやったこと

ISUCON初参加だったので、感覚をつかむために前回予選のAMIを使って3人で練習。
正直、この練習をやってなければもっとひどい結果になっただろうと思えるくらい、問題点や準備しなければならないことが山ほど出てきて泣きそうでした。

アクセスログMySQLのスローログの分析方法を事前に調べて手順化しておき、当日に慌てないように準備することにしました。

使う言語もこの時に決定。得意言語はバラバラで、Perl2人、Go1人だったんですが、作業分担的にGoにすることに。

リポジトリGitHubのプライベートリポジトリ、テキストでのやりとりはSlackを使いました。ISUCON用のチャンネルを作って、そこにGitHubにpushした内容などを通知させるように設定。

予選当日 午前

まずは当日のマニュアルに目を通し、起動したインスタンスのIPをSlackに貼る。
鍵は練習で使用したものを流用。.ssh/configにIPと鍵の場所を書いて簡単にログイン出来るようにした。

ログインしたらまず/home/isucon配下でgit initして remote add & push。
最初は修正は行わずに、アプリケーションの内容やミドルウェアなどの情報を調べることに。
アプリにログインしたあと何も出来なかったので、この機能が動くように修正するのか!?と勘違いしましたが、すぐにログインゲーだと気づく。

次にnginxのログとMySQLのスローログ(long_query_time=0)を出力する設定にしてベンチ初回実行。
access.logはApache::Log::Parserを使って集計、MySQLのスローログはmysqldumpslowを使って集計。

bench.shを作成。内容は、今あるnginxのログとMySQLのスローログをrenameして空にし、nginx、MySQL、supervisordの再起動して、ベンチを実行という感じ。

ひと通り見たので修正方針の話し合い。
login_logテーブルで禁止IPの判断、禁止Userの判断、前回ログイン情報の取得と色々やってたので、これを分割することにする。
禁止IPカウントテーブル、禁止Userカウントテーブルを作り、それをメモリに載せることにした。役割分担は以下。

  • waniji: 初期データ投入スクリプト作成
  • kyokomi: メモリに載せるようにアプリ修正
  • yasuun: nginxの設定周りを修正

午後

禁止IPカウントテーブルと禁止Userカウントテーブルの初期データ投入をPerlで作成。
Perlのアプリ実装を見てみたらlocal配下にDBIx::Sunnyがいたので、perl -I でそこを指定して初期データ投入スクリプトで使った。

禁止IP、禁止Userのカウント情報をメモリに持つ修正が出来てきたので計測したらスコアが12304。
更にnginxで静的ファイルをさばく設定がきて16002。
この時にcannot assign requested addressがでたけど、ローカルポートを食いつぶしてるとはわからず……。

次に、前回今回ログイン情報を持つテーブルを作ってlogin_logを廃止するとこに。
しかし、初期データ投入スクリプトが1分を超えてしまってウンウン悩んである間に時間がなくなってあえなく中断……。

ユーザーテーブルの情報をメモリに持つようにしたが、スコアが下がったので戻し。

この辺りで時間がなくなったのでworkload数を調整して、再起動して問題ないか確認をしてたらタイムアップ。

反省点

  • レギュレーション違反

データをメモリ上にしか持っていなかったので、再起動後のreport内容が不一致となってOUTでした。問題外なので次はこういったことがないように気をつけたい。

  • login_logにインデックスを貼らなかった

スローログを見て遅いのは分かっていたのに、どうせ無くすテーブルだからとインデックスを貼らなかったのが間違いだった。他チームの方の記事を見てみると、インデックスを追加するだけでクエリのチューニングが不要になるくらい早くなるとの事だったので、これは完全に判断ミスでした。

  • ローカルポートの食いつぶしに気が付かなかった

完全に知識と経験不足でした。予選終了後にいろいろ試したところ、ポートの設定を弄らないと20000弱で頭打ちになったので、これを対処できない人は足切りされる感じだった。

まとめ

予選は惨敗だったけど、チーム全員が準備も含めて楽しんでやってたし、色々と良い経験が詰めました!
来年も開催されたら絶対リベンジしたい!

YAPC::Asia 2014に参加してきた #yapcasia

去年初めて参加したYAPC::Asia。2回目の参加です。
ちゃんと感想を書いて、今年のYAPCを終えようと思います。

印象に残ったセッション

Releasing perl

ドキュメントの充実や開発プロセスの簡略化にかなり力を入れていて、 長く続けていくにはこういった地道な努力が必要なんだと思わされました。

お待たせしました。Perl で BDD を簡単に実践する最高にクールなフレームワークができました

Test::More 2.0が開発中止になっていたとは...。Test::Kantan使ってみます!

Perl::Lint - Yet Another Perl Source Code Linter

トークンとはどういうものなのか、どういった処理でチェックを行っているかの説明が理解しやすかったです。プロジェクトごとのポリシーも作れる(作りやすい)とのことなので(複雑でなければ)、業務に活かせそうで楽しみです!

WHERE狙いのキー、ORDER BY狙いのキー

何故クエリが遅くなるのか早くなるのかをPerlのコードを用いて説明されていて、Perl使いにはありがたいです。Indexはソート済みなのでそれを狙ってクエリの高速化を行うなど、具体的なやり方を説明しているのがとても良かった。
理解する前に進んでしまったところが一部あったので、スライドを見直そうと思います!

オープンソースの開発現場 - Perl 5.20 のSubroutine Signaturesが来るまでの奮闘の軌跡

Peter Martini氏がいい男過ぎてもう......。最終的に全部書きなおされたのに、入った事自体がいい事なんだ、なんて俺は言えないだろうな......。
海外のこういった事例が聞けるのは本当に貴重でありがたいセッションでした。

半端なPHPDisでPHPerに陰で笑われないためのPerl Monger向け最新PHP事情(5.6対応)

トークが抜群に上手く、聴衆が一気に引きこまれてました...!ネタ(闇)も交えてPHPの魅力が語られていて、やってみようかなという気にさせてくれるとても素晴らしい内容でした。
ベストトーク賞1位おめでとうございます!

Perlあるある

憧れのハッカーの方々についての、普段聞けないようなことが聞ける貴重なトークでしたw 時間の都合で答えられていなかった質問の回答がとても気になります!

YAPC::Europe 2014 に行ってきました

ビール天国に行きたい!そしてOAuth認証のLTが面白いw
Google翻訳でも何とかなるというお話は、躊躇している人にとってはとても良い情報だと思いました。

Perlの静的解析入門とPerlリファクタリングツールApp::PRTのご紹介

独特なテンポのトークがとても面白かった!
しかも内容はかなり実用的で、これは実践せねばと思いました!

キーノート

「一度の人生楽しまなきゃ損!」
最高でした。

設備など

コーヒー、ジュース、お菓子、レッドブル、かき氷が無料で提供され、懇親会は無料、HUBは貸し切りで1000杯までフリードリンクという至れり尽くせりで、正直このチケット価格で大丈夫かな...と心配になります。

イベントホールでは書籍の販売やイベントトラックがあったり、とても賑やかな感じでお祭りのようでした。

会場のネットワークは快適過ぎて最高でした。

その他

懇親会で出身地が同じ方と偶然出会い、地元トークや、色々共通点がある過去の(ヤバイ)プロジェクト話で大いに盛り上がり、とても楽しい時間を過ごすことが出来ました。こういった出会いもあるYAPCは本当に素晴らしいですね。

まとめ

運営の方々、本当にお疲れ様でした!最高です!
次回は登壇する側に立ってみたいです!

Vimでヤンクした内容を複数行にペーストする

参考にさせていただいた記事

検索・置換に便利なヤンク(レジスタ)【Vimコマンド】にさん追加7/3 - YKMbPP

やりかた

Ctrl+rを押した後"を入力すると、入力モードやコマンドラインモードでもヤンクした内容をペースト出来るようなので、以下の手順で複数行ペーストが可能です。

  1. Ctrl+vで矩形選択モードにする
  2. 範囲選択したあとにCtrl+Iで入力モードにする
  3. Ctrl+rを押して"入力でヤンクした内容をペースト
  4. ESCで抜けて複数行に反映

文字だと分かりづらいのでGIFアニメ

f:id:waniji:20140827181543g:plain

HerokuにHubotをデプロイしてSlackと連携&Travis CIで自動デプロイ

やること

  • Heroku上でHubotを動かす
  • SlackとHubotを連携する
  • Hubotリポジトリに変更があればTravis CIに検知させて自動デプロイ
  • デプロイ完了したらSlackに通知

以上を無料枠で

前提

  • Mac OSX
    • 開発環境として使用
  • 以下のアカウントは作成済み
  • HerokuのBillingは設定済み
    • Add-onsを使用するために必要
  • GitHub上にHubot用のリポジトリを作成済み

Hubotを作成

必要なアプリケーションをインストール

brew install node redis
npm install -g hubot coffee-script

Hubotの雛形を作成 (名前はお好みで)

hubot --create foobar-hubot
cd foobar-hubot

Slack用のアダプターを追加

npm install hubot-slack --save

Slack用のアダプターを使うようにProcfileを書き換え

vim Procfile
web: bin/hubot --adapter slack

GitHubにpush

git init
git add .
git commit -m "create"
git remote add origin (リポジトリURL)
git push -u origin master

Herokuにデプロイ

HerokuのCLIツールをインストール

brew install heroku

CLIツールの初期設定

heroku login

Herokuアプリの作成

heroku create --stack cedar (アプリ名)

ワーカプロセス数の設定とアドオンの追加

heroku ps:scale web=1
heroku addons:add redistogo:nano

HerokuアプリのURLを設定

heroku config:add HEROKU_URL=*****

SlackのIntegrationsでHubotを追加

f:id:waniji:20140826003419p:plain:w150

Setup Instructionsに表示された設定をHerokuに追加

heroku config:add HUBOT_SLACK_TOKEN=*****
heroku config:add HUBOT_SLACK_TEAM=*****
heroku config:add HUBOT_SLACK_BOTNAME=(ここは任意)

HerokuにHubotをデプロイ

git push heroku master

SlackにHubotのURLを追加して完了

f:id:waniji:20140826003808p:plain

Travis CIを使って自動デプロイ

Travis CIのProfileでHubotのリポジトリを有効化

f:id:waniji:20140826003927p:plain

Travis CIのCLIツールをインストール

gem install travis

リポジトリにHeroku用の設定

travis setup heroku

.travis.ymlの先頭にnode.js用の設定を追記

vim .travis.yml
language: node_js
node_js:
    - '0.10'

.travis.ymlをGitHubにpush

git add .
git commit -m "add .travis.yml"
git push origin master 

HubotがデプロイされたらSlackに通知

SlackのIntegrationsでHerokuを追加

f:id:waniji:20140826004052p:plain:w150

通知させるチャンネルを指定

f:id:waniji:20140826004120p:plain

Setup Instructionsに表示されているコマンドを実行する

heroku addons:add deployhooks:http --url=*****

参考にさせていただいた記事

Capture::Tinyを使ってSTDERRに何も出力されていないことをテストする

経緯

  1. 数千万行あるファイルに対して1行ごとに処理するようなバッチを書く
  2. utf8フラグ付きの文字列をencode_utf8せずに処理してしまう
  3. 大量のWide character in ...という警告がログに出力される
  4. 気付かずに本番リリースされ、それが原因で障害発生
  5. 悲しみに閉ざされる

対策

あれ以来、テストでSTDERRのチェックを行うようにしている。
あと、テストデータにマルチバイト文字列を忘れずに入れる。

my ($stdout, $stderr, $exit) = capture { MyApp->new->run(@argv) };
is $stderr, '', '標準エラー出力には何も出力されていない';

Path::ClassからPath::Tinyに移行した時に書き換えた処理

バージョン

  • Path::Class 0.33
  • Path::Tiny 0.056

オブジェクト生成

Path::Classで生成したオブジェクトは、ファイルはPath::Class::File、ディレクトリはPath::Class::Dirとなる。Path::TinyはどちらもPath::Tinyとなる。

# Path::Class
$file = file($path);
$dir  = dir($path);

# Path::Tiny
$file = path($path);
$dir  = path($path);

ディレクトリ配下のファイルオブジェクト生成

# Path::Class
$file = $dir->file($name);

# Path::Tiny
$file = $dir->child($name);

ディレクトリ配下のディレクトリオブジェクト生成

# Path::Class
$dir = $dir->subdir($name);

# Path::Tiny
$dir = $dir->child($name);

ディレクトリ削除(配下ファイルごと)

# Path::Class
$dir->rmtree;

# Path::Tiny
$dir->remove_tree;

ファイルオープン

# Path::Class
$read = $file->openr;
$read = $file->open('<:encoding(cp932)') or die "Can't read $file: $!";
$read = $file->open('<:utf8') or die "Can't read $file: $!";

# Path::Tiny
$read = $file->openr;
$read = $file->openr(':encoding(cp932)');
$read = $file->openr_utf8;

ファイルの内容を配列に読み込み

# Path::Class
@lines = $file->slurp( chomp => 1 );

# Path::Tiny
@lines = $file->lines( { chomp => 1 } );

ファイルにまとめて書き込み

# Path::Class
$file->spew( iomode => ':utf8', $data );

# Path::Tiny
$file->spew( { binmode => ':utf8' }, $data );

親ディレクトリ名でファイルを作成

# Path::Class
file($file->parent)->touch;

# Path::Tiny
$file->parent->touch;

ファイルをコピー

# Path::Class
$file->copy_to($path);

# Path::Tiny
$file->copy($path);

ファイルをリネーム

# Path::Class
$file->move_to($file_path);

# Path::Tiny
$file->move($file_path);

ファイルを指定ディレクトリに移動

Path::Tinyのmoveはrenameと同等の機能なので注意が必要。

# Path::Class
$file->move_to($dir_path);

# Path::Tiny
use File::Copy 'move';
move $file, $dir_path;

ディレクトリ配下のファイルを処理

# Path::Class
$dir->recurse( callback => sub {
    my $file = shift;
    return unless -f $file;
    print $file, "\n";
});

# Path::Tiny
my $iterator = $dir->iterator( { recurse => 1 } );
while( my $file = $iterator->() ) {
    next unless $file->is_file;
    print $file, "\n";
}