時計を壊せ

駆け出してからそこそこ経ったWebプログラマーの雑記

redis-sentinel管理のredisを手動failoverさせる

やんごとなき理由でredis-sentinel管理のredisのmasterを再起動したくなって、
手動failoverのやりかた調べてたら公式のドキュメントにちゃんと書いてあった。
公式のドキュメントちゃんと読まないとダメだ。


適当なsentinelに入って、

redis 127.0.0.1:26379> SENTINEL failover <master name>

としたら良い感じにfailoverしてくれた。
redis-sentinelべんり。

configファイルのmasterのIPも新しいものに変えておかないと次回sentinelを再起動したタイミングで狂うので忘れずに換えて置かなければならないのだけ注意したい。
というか先にconfigのIP変えてから手動failoverしたほうがいいかもしれない。

参考: http://redis.io/topics/sentinel

2013年のKPT

2013年終わったのでKPTやってみる。

Keep

新しい分野に挑戦した

  • Android
  • フロントエンド開発
    • こっちはお手伝いとお遊び程度だけど

新しい言語に挑戦した

新しいモジュールを作った

  • Parallel::Async
  • SQL::Maker::Plugin::Case
  • Hash::Compact::Lite

ISUCON3出た

  • 予選通過
  • 本戦惨敗

作曲した


ぼくの干し椎茸 by karupanerura on SoundCloud - Hear the world’s sounds

帰り道(demo) by karupanerura on SoundCloud - Hear the world’s sounds

帰り道はこのデモをもとにThe Monzllisでバンドバージョンを作った。そっちは2番もある。

ライブやった

高円寺界隈の人と仲良くなれた

  • バンドつながりで知り合いがふえた

資格試験受けた

温泉発火村やった

  • 熱海行った
  • 旅館がすばらしかった

地域PMに参加した

  • Hachioji.pm
  • Chiba.pm

エンジニアのコミュニティにより深く関われた

  • kenjiskywalkerさんとかstudio3104さんとかuzullaさんとかxtetsujiさんpython_spameggsさんとかsongmuさんとさyusukebeさんとか、挙げてくとキリが無いくらいの人と知り合えた。

ITエンジニア平成会に参加できた

  • 意外とあの人と歳近かったのか!みたいなのあってよかった。
  • 新年会やりましょう

Problem

デスマった

  • 見積もりミスこわい

引っ越しの片付けが済んでいない

  • 言い訳のしようも無い

Yomiuri完成してない

  • Text::Markdown::Hoedownのlow-level APIを使って組んでたがAPI変更があったり

ISUCON3惨敗

  • インフラ周り本格的に勉強したいしノウハウ積みたい

情報セキュリティスペシャリスト試験落ちた

  • 午前Iでマークシート写し損ねがあって減点したのが痛かった
  • 自己採点だとたしか午前Iだけ落ちてたはず。かなしい

IT芸人になりつつある


自分が作った代表的なプロダクト的なものが無いまま名前だけそこそこ知られてるって状況になりつつあってエンジニアとしての危機を感じている。

Try

新しい言語を会得する

新しい分野に挑戦する

  • サーバー運用
  • 言語処理系

新しいモジュールをつくる

  • ORM
  • 痒い所に手が届く系

遠方の地域PM/参加した事の無い地域PMに参加する

  • Hokkaido.pm

Perl/MySQL以外の勉強会に参加する

  • rbとかgo

英語を勉強する

  • YAPCで英語でトークしたい

数学を勉強する

ソロでライブやる

  • 弾き語り
  • のために曲作る

ISUCON3でまたもや惨敗したはなし

ISUCON3への参加から日が経ってしまいましたが、来年の自分へのメッセージとして書きます。

だいたいの内容

坂パスタチームの仲間の面々が書いてくれているのでそちらをご参照下さい。
id:kfly8 http://kfly8.hatenablog.com/entry/2013/11/30/200331
id:masasuz http://masasuzu.hatenablog.jp/entry/2013/11/10/isucon

事前準備

生憎、仕事もプライベートも予定がキツキツになってしまって準備に割ける時間が直前でなくなってしまって全然動けなかった記憶があります。
もっとはやく準備してればよかったなーと思いました。足回り系とか、準備が主な仕事なので役割果たせてない。
id:kfly8 先生にげんこつされる夢を見ました。うそです。
でも、げんこつされても反論できないくらい、仕事できてなかったので来年がんばる。

ぼくがやったこと

  • 緊張する
  • gitとか整える(ここまで平常運転)
  • とりあえずconvertコマンドがボトルネックになってる事がわかったのでこいつを速くするぞ!まずは置き換えてみよう!というアウトな方針を立てる
  • convertコマンドを他のcpanモジュールで置換しようと延々と試行錯誤を行い時間を無駄に潰す
    • 画像のdiffが多くてコケる
  • 慌てる
  • ImageMagicのperl bindingなら大丈夫だろ
    • 画像のdiffが多くてコケる
  • 慌てる

以上です。アウトー!!

反省

事前準備に時間を割かなさすぎた

時間がない!って分かった時点で他の人に事前準備の役割を移譲するべきだったし、
そもそもぎりぎりになって準備をしようと考えている時点で甘かった。

方針決定に時間を割かなさすぎた

ほぼ思い付きの方針みたいな感じだったし、冷静に考えればまずはリソースを使いきれるようスケールアウトさせるのが先決だった。
チューニングの順序も、本来は「処理数を減らす」「処理を高速化する」の2通りあるはずだったが、
予選でmarkdownコマンドをHoedownに変更した事が功を奏した事などから短絡的に「処理を高速化する」方向に舵を切ってしまった。
方針変更を躊躇した原因もここにあり、なぜなら他のチューニング方針を考えていないので、
方針変更は新たな方針を立てる事から始めなければならず、方針変更のコストが高くなってしまっていた。
ましてや、普段触ってるアプリとは違う性質のアプリをチューニングする上で、
僕みたいな経験の浅い若者にベストな答えが一発で出せるわけがなく、
色々なチューニング方法を検討した上で、ダメだった場合のプランBも決めて、
一気に手を動かしたほうが確実に効率は良かった。

慌てた

慌てんな。

感想記事を書くのが遅すぎた

覚えている事が少なくて反省の記録として質が悪い。

感想

毎度、反省が多く、勉強になり素晴らしいイベントだなと思います。
今回も多くの学びがありました。
技術的な部分だけではなく、短期間で成果をあげるためのアンチパターンやら、
チームで仕事をする上でのアンチパターンみたいなものにもハマるので、
本当に、参加するだけで価値のあるイベントになっているとおもいます。
特に、新卒や学生の人こそ参加してみて、プロの仕事の品質を身を持って体感してみると良いと思います。
運営の方々、本当にありがとうございました。来年も楽しみにしております。

Amon2で非同期レスポンスを使う方法と、非同期WebAppのハマりどころ

この記事はPerl Advent Calendar 2013の15日目の記事です。

Amon2とは

@tokuhirom さんが開発しているPerl製のWAF*1です。
Plackを軽くwrapしたような軽量でシンプルなWAFです。
現在、Version 6.00がリリースされていますが、Version 3.50からwebsocketのサポートが入り、
その関係でPSGIの遅延レスポンス/ストリーミングレスポンスのインターフェースに対応しています。

Amon2で非同期レスポンスを使う

Amon2::Plugin::Web::Streamingを使う事により非同期でレスポンスを返す事が出来ます。
例えば、index.txを5秒後にrenderして返す場合は以下のようになります。

use strict;
use warnings;
use utf8;
use Amon2::Lite;
use AnyEvent;

get '/' => sub {
    my $c = shift;
    return $c->streaming(sub {
       my $responder = shift;

       my $w; $w = AnyEvent->timer(after => 5, cb => sub {
           my $res = $c->render('index.tt')->finalize;
           $responder->($res);
           undef $w;
       });
   });
};

# load plugins
__PACKAGE__->load_plugin('Web::CSRFDefender' => { post_only => 1 });
__PACKAGE__->load_plugin('Web::Streaming');
__PACKAGE__->enable_session();

__PACKAGE__->to_app(handle_static => 1);
__DATA__
@@ index.tt
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Streaming</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <script type="text/javascript" src="[% uri_for('/static/js/main.js') %]"></script>
    <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
    <script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="[% uri_for('/static/css/main.css') %]">
</head>
<body>
    <div class="container">
        <header><h1>Streaming</h1></header>
        <section class="row">
            This is a Streaming
        </section>
        <footer>Powered by <a href="http://amon.64p.org/">Amon2::Lite</a></footer>
    </div>
</body>
</html>

@@ /static/js/main.js

@@ /static/css/main.css
footer {
    text-align: right;
}

このように$c->streamingにcallbackを渡すとそれがそのまんまPSGIの非同期レスポンスとして使えてあとは$responderをすきに料理すればいいというスンポーです。簡単ですね!

ハマりどころ

ブロックする処理を混ぜてはいけない

現実的に、現状は非同期レスポンスを返すアプリケーションを書く場合はAnyEventのイベントループの上で動くことになる為、イベントループを止めるような処理を書いてはいけません。
例えばネットワークIOがあるものなどはAnyEvent::Socketを使うライブラリを使い、sleepやwait(pid)?の代わりにAnyEvent->timer/AnyEvent->childを使うなど、イベントループを止めずに終了を通知させるAPIを使う必要があります。
イベントループを止めてしまうとその間他の処理が一切出来ないため、たとえばDBに1秒かかるクエリを投げた場合、1秒間リクエストの受付すら出来なくなってしまうという状態が起こりえます。

context毎に他のサーバーへのconnectionを保持するとリクエストの度にconnectionが増加して死に至る

HTTPの同時接続数と同じだけmysqlへの接続数が増えるという事になると、
非同期レスポンスが中心になるアプリケーションでは同時接続数が非常に膨らむ為、問題になります。

非同期レスポンス中ではAmon2->contextを使ってはいけない

Amon2->contextはAmon2における現在のcontextオブジェクトがどこからでも取れるという便利な代物ですが、
非同期レスポンスではこれはハマりどころになります。


なぜなら、Amon2の中でのリクエストの開始と終了というのはAmon2::Liteにおけるget '/'などに登録したcallbackが終わるまでの間*2だからです。
つまり、$c->streaming()の中ではcontextは基本的にundefになります。
また、同時に複数のリクエストを捌いているケースではタイミングによっては他のリクエストのcontextを参照してしまう為気付きにくいです。
シーケンスで表現すると以下のような感じです。*3



解決策

Object::Containerなどを使い、globalにリソースを持つ

globalにリソースを持つ事によって、それを複数リクエストで共有する事が出来ます。
もちろん、トランザクションなど、リソースに状態変化が起こる処理を行うと問題になりますが、
そのような処理を行わない場合は検討しても良いかもしれません。
例えば、トランザクションが必要な処理はGearmanなどのWorkerに任せてAnyEvent::Gearmanなどでタスクとして投げてその終了をcallbackで受けるなど、別プロセスで同期的に処理するのが良いかと思います。
もっとシンプルにやる方法もあるとベターかとは思いますが現状ではなかなか辛そうな印象です。
(あるはAnyEvent::DBIが同時に複数のトランザクションが走らないようなアーキテクチャになれば問題は解決するかもです)
他に良い方法があれば教えて下さい!

まとめ

非同期レスポンスむずい!!!!!!!!!!!
明日はid:mackee_wさんです!

*1:Web Application Framework

*2:もっと厳密に言えばAmon2::Web#handle_requestが終了するまでの間

*3:seqdiag.jsを使っています。walf443++

state変数は初期化のタイミングで例外が発生しても二度と初期化が行われる事は無い

タイトルの通り。
まあ、初期化は1度しか行われないので、あたりまえといえばあたりまえですがハマったのでメモ。


以下検証コード。undef,2,2となるかと思いきや初期化は1度しか行われないのでundef,undef,undefとなる。

static variable be undef if throw when initialize ...

#isucon 3予選を運良く通過したはなし

予選通過、かなり高い壁だと思っていたので通過出来て大変喜んでおります。


ちなみに、当日記録できた最高スコアは10846.599609375でした。workloadは8です。
これ、結果がブレた原因はなんとなくわかってるので後のほうに書きます。

今回気を付けた事

既に2回参加したisucon、これで3回目の参加となりますが、
流石に3連敗とはいかんのでせめて予選通過をというのが今回の個人的な目標でした。*1
なので、ある程度は確実に作業をすすめるために幾つかチームとして動くためのささやかな作戦を用意していました!
作戦と言えば聞こえは良いですが、チームとして動く上である程度当たり前のことだと思います。

ざっくりとしたスケジュールを決めてとりかかる

マイルストーンが無いと十分に調査が終わらないまま、チューニング作業に入ってしまい、無駄な作業をしてしまう為。
業務でチューニング作業などは殆どやる機会が無い為、勘と経験で乗り切るとかスーパーな事は出来ないので、
なんとなくでやらずに時間に区切りを作って調査を行った。


具体的には以下のような感じ。

時間/時刻 やること
10:00 ~ 12:00 初期状態/ボトルネック調査
12:00 ~ 13:00 お昼食べながら作戦会議
13:00 ~ もくもく
15:00 おやつ
17:30 再起動テスト
18:00 終了

初期調査の役割分担

前回、前々回と同じ作業をみんなでやるなど無駄な事をやりまくっていた為、
ある程度は無駄をなくしたかった為、ある程度決まりきっている作業は事前に役割分担を行い、
調査が終わり、お昼を食べながらチューニング指針を検討し、再度作業分担を行う作戦で行った。


具体的には以下のような感じ。

  • 自分
    • ssh鍵,git化,deploy,restartツール等足回り系を整える
    • ログ調査
    • 各人の突発の役割分担の指示
  • id:masasuz
    • ログ調査(主にMySQLまわり)
  • id:kfly8
    • アプリ仕様調査
    • ログ調査(主にアクセスログまわり)

同時に実行するとマズい作業はロックを取る仕組み

ベンチマークの実行やデプロイは同時に実行すると結果がぶれ、再度計測し直しになったり、タイムロスに繋がります。
今回は1人だけをベンチマークを実行する役にし、他のひとは原則的にベンチマークを実行しないという事にしました。
ただ、これは反省が多く、今回はスクリプトによるベンチマークの実行が可能だったので、スクリプトの実行をwrapしてデプロイツールと共通のファイルで排他ロックを取ってやる感じでシステムでカバー出来たら良かったなと思いました。

ぼくがやったこと

本格的なチューニングはアプリ側は id:kfly8 に、DB側は id:masasuzu におまかせしつつ、
サーバーと足回りの。

  • ツール等足回り系を整える
  • 定期的に「落ち着け」言う
  • nginx入れる
  • bin/makrdownをsystemで呼び出してたのをText::Markdown::Hoedwonに置き換える
    • これと id:kfly8 によるtotal_memosをキャッシュ/incrする変更で2000くらいから5000くらいまで一気にスコアが上がった
    • 提出AMIから起動してrevertしてみたらスコア半分になった
  • StarmanからStarletに変更しkeep-aliveを活用出来るように設定をチューニング
    • max_workersもいくつか試した末、最終的には80にした
    • 冷静に見ると誤差に踊らされた感が否めない
  • deployツールやrestartツールが動かなくなるバグを調査しつつ修正していく
  • やっとこさtoolchain系が安定したと思ったら、時間がなくなってきたのでCache::Memcached::Fastなどのインスタンスをstateに入れるようにするコソドロチューニングを行う
    • ついでにCache::Memcached::Fastのserialize_methods/compress_methodsなどをData::MessagePack/Compress::LZ4に差し替える
  • recent_memosをキャッシュする作戦
    • アプローチがあまりにもあほで失敗に終わる
  • 再起動をかけるも急にスコアが落ちて混乱する
    • supervisordを見落としてデフォルトのStarmanが立ち上がってました!!!111
    • すみませんでした!!!!!!!!!!!!1111
      • 再起動できてボーナスポイント入ってたら最高スコア更新してたはず!!11
    • 設定は直したけど再起動してる時間が無かった為これが最終スコアに
  • 時間切れ間際にボーナスポイントを見付けて対応する
  • 時間切れ


はい!ぼくほとんどなんもできてないですね!!!

今回の反省点

推測するな計測せよ

焦るなつってんだろ!!!!!!!!!111 > 俺

tool chain系は事前に整えておく

すみませんでした!!!!!!!!!!!!1111

リーダーシップを発揮する役目を誰かに

すみませんでした!!!!!!!!!!!!1111

感想

workloadオプションの調整やってなかったチームの事考えると、
ふつーに予選落ちだったんじゃないかとガクブルしています。
準備すごい大変だと思いますし、全員ぶん見るのもっと大変だと思います。
これほど大変なイベントを開催して頂きありがとうございました!!楽しかったです!!

本戦への意気込み

生きて帰れるように頑張ります!!!1111

*1:あと、ITエンジニア平成会のメンバーにスコアで勝つのも目標でしたが、id:mackee_w のチームに負けました。本戦では勝ちにいきます。

*2:推測するな計測せよ違反

#yapcasia 2013の感想とParallel::Asyncを書いたはなし

前夜祭

前夜祭では昨年のLTソンと同じくらいの熱気が会場を沸かせ、ときにはApache::LogFormat::CompileとかNorikuraとか真面目なプロダクトの紹介もありつつ、Acme系のはなしなどいろいろあっておもしろかったです。
ぼくも熱気に当てられて飛び込みLTとかやりました。ネタのつもりだったけど滑って残念な感じですみませんでした。

なお、スライドでも触れてる通り、ポチポチゲーの印象がつよいソシャゲですが、
最近はパズドラなどゲームゲームしているものも多く、遊ぶのも開発するのも面白いものが多いですので、
この機会に遊んでみては如何でしょうか。
中で触れてる奴についてはごにょごにょしてる最中ですの気になる方はで続報をお待ち頂ければと思います。

1日目

自分の発表まで資料の見直しと圧縮やらを繰り返しつつトークを聴いていました。

@rjbs さんのトークはPerl5.18あたりで入った新機能や廃止されるかもしれない機能、Perl開発の実態についての紹介でした。ガンガンいい感じの機能いれてこうぜって感じのアツいトークだった気がします。英語勉強しようと思いました。


id:songmu さんのDBまわりのはなしは、(たぶん)同業種かつ同職種である故か、知ってるところはうんうんうんとすごい勢いで同意したり、しらなかったところは id:kfly8 と「これやろうぜ!!」って盛り上がったりしました。


トークが終わった頃には次に聞こうと思っていた id:myfinder さんのRiakのはなしは既に始まっていて、
会場に着いたころには結構話がすすんでしまっていたきがします。
ぼくはRiakについて殆ど知らなかったので、あとで調べて発表のYoutube聴き直しながら勉強しようとおもいました。


お昼は @python_spameggs さんと麺場ハマトラに行きました。熱々濃厚で美味しかったです。


午後は id:yappo さんのメンテナンスしやすいコードを保つためのtool chainの工夫みたいなところで、
現状のsetupスクリプトの課題と、その解決策としてKsgkを作ったという感じの話でした。
だいぶテンションあがりました。


このあとぼくの発表でした。

緊張しててカバン背負ったまま壇上に立ち、それに気付いて頭が真っ白になって、アレな感じでした。
詳細は http://karupanerura.hateblo.jp/entry/2013/09/21/164133 をどうぞ。
Youtube見なおしたらやっぱりだいぶ酷くで申し訳なさでいっぱいであります。。。
次こそはと思っているので、来年の運営には少しでも力になれればと思っております。


そのあとは気ままにトーク聴いたりBOF行ったりぶらぶらしてました。
id:ry_mizuki がSpicaという素晴らしいモジュール書いてたのでLTでいいから発表しようぜとpushしたら発表してくれました!


Clientライブラリが無い、BtoB向けのAPIやマイナーなサービスのAPIを叩くときとかに重宝すると思います。
万能ではないけどだいたいのケースに対応出来るのでいいかんじです。
CPANにあがるのを

懇親会は去年以上にいろいろな人とおはなし出来て楽しかったし、料理も酒も美味しくて素晴らしかったです!
懇親会スポンサーの:DeNAさんありがとうございました!

懇親会のあとはHubに行ってなんか呑みながらくっちゃべってたきがしますが気が抜けすぎて酔っ払ってあんまり覚えていません。


あと家帰っていろいろ考えて反省とかしてたきがする。

2日目


こんなかんじでした

Hackathon


日曜日は用事があり公式のHackathonには行けなかったので月曜日にひとりHackathonをやりました。
最近、仕事でJavaを書く機会があり、Javaはカジュアルに並列処理を書けるので、
Perlでもカジュアルに並列処理できたらおもしろそうだなと思ってつくりました。

こんなかんじで並列処理が書けます。waitとか考えてなさそうなかんじ、カジュアルです。

use Parallel::Async;

my $task1 = async {
    my $res = ...;
    return $res;
};
my $task2 = async {
    my $res = ...;
    return $res;
};
my $task3 = async {
    my $res = ...;
    return $res;
};

my ($res1, $res2, $res3) = $task1->join($task2)->join($task3)->recv();

単純にforkすることやdaemonizeする事もメソッド1つ呼び出せば出来るようなかんじになっております。
isucon予選では即興でのworker代わりに使おうとしましたが時間がありませんでした。*1
けっこういい感じだと思いますので良かったらお使いください。

最後に

素晴らしいイベントをいままで育て、主導して運営してきた id:lestrrat さん、 id:941 さん、スタッフのみなさん、ありがとうございました。
本文中にもちらっと書いておりますが、来年の開催、運営を微力でもお手伝いできればと考えております。
コード書く事くらいしか取り柄がありませんがお力添えできれば幸いです。

*1:本当はResqueとかq4mとか使ったほうがよいはず。去年は時間切れで活きませんでしたがCluntchを使いました。