Shogo's Blog

Feb 1, 2014 - 1 minute read - perl redis

Redis::Fast 0.06 released

こんにちは、もうすぐ17才と100ヶ月を迎えるいっちーです。 今朝、Redis::Fast 0.06をリリースしました。 主な変更点はメモリーリークの修正と、エラー発生時にSegmentation Faltで落ちる問題の修正です。 メモリーリーク Redis::Fastをサブスクライバーモードで動作させると、メモリを無限に食い続ける問題をついに!ついに!修正しました。 原因は、一言で言ってしまえば、Perlのリファレンスカウントの扱いの勉強不足です・・・。 XSの中でPerlのオブジェクトを作るとき、プログラマが手動でリファレンスカウントを制御する必要があります。 とはいうものの、全てのオブジェクトのリファレンスカウントを制御するのは大変なので、 XSには「揮発性」という考え方があります。 sv_2motralを使って変数を揮発性に設定しておけば、よしななタイミングでオブジェクトを解放してくれます。 gfx先生のブログにもあるように、 オブジェクト作成したら原則sv_2motralをつけるようにすれば、 メモリーリークはほとんどなくなるはずです。 SV * s = newSVpv("Hello World",0); // Perl の文字列オブジェクト sv_2motral(s) // 揮発性にすることで、使われなくなったら自動的に解放してくれる この「よしななタイミング」をよく理解していなかったのでリークしてました・・・。 XSからオブジェクトへアクセスできなくなったときでないとオブジェクトを解放できないので、 揮発性のオブジェクトが実際に解放されるのは「XSで書かれた関数が終了してPerlに戻るとき」です。 メッセージを待ち続けるwait_for_messages関数は (タイムアウトをしない限り)ずっと終了しないので、 揮発性のオブジェクトを解放するタイミングが一切なかったのです。 不要になったら解放されるよう、揮発性オブジェクトの有効範囲を明示的に指定しました。 sv_2motral(s); ENTER; SAVETMPS; sv_2motral(v); FREETMPS; LEAVE; // v はココで解放される // s は生き残ってる perlcallとかちゃんとドキュメントを読みましょう > 自分 Segmentation Falt 同期的にコマンドを実行してる最中にSIGNAL等で実行が中断されると、 Segmentation Faltが起こる問題を修正しました。 Redis::Fastは同期モードでコマンドを発行したときでも、 hiredisの非同期モードの機能を使って通信しています。 コマンド実行中にエラーが発生すると、 コールバック関数の呼び出しタイミングが変わってしまい、 メモリの確保・解放のタイミングが狂ってしまっていました。 このバグ、試した環境の中ではUbuntu+Perl5.14でしか再現しませんでした。 他の環境ではたまたま解放後もアクセスできてしまって、 正常に動作してしまっていたようです。 嫌なバグだ・・・。 まとめ C言語でメモリ管理するコードは書くべきでない。

Dec 21, 2013 - 2 minute read - unity csharp

Unity Test Tools を使ってみる

みなさんこんにちは、 最近つらいことばかりで元気のないいっちーです。 少しでもつらいことを解消できないかと Unity Test Tools ってので遊んでみました。 背景 最近あったつらいことのひとつに「Unityで作ってるプロジェクトで、機能拡張したときに間違えて一行消しちゃった!!!」 ってのがあります。 もちろん僕が消したわけじゃないですよ!!! 僕サーバサイドエンジニアですから、Unityはいじりません。 でも、一応修正コミットは見ていたはずなので、気がつけなかったのは残念です・・・。 どんなにコード書く人が頑張っても、レビューする人が頑張っても、 人間誰だってミスします。 じゃあ、機械にやらせよう!テストコードだ!って話なんですが、 コードカバレッジが低く、今回のつらい事例でもテストがありませんでした。 一部書いてあるテストも、担当者が代わってからなんか怪しい・・・。 あと、自分も手元でテスト動かしてみたのですが、今のテスト面倒・・・。 Unityのコンソールにドバッと流れる テストが全部通ったのか、失敗したのかよくわからない ユニットテストを1項目だけやりたいとかどうやるんだろう 「テストの実行」が「シーンの再生」なので1項目とかどうすんの? Unity Test Tools つらいので解決方法を探るべくインターネットの海をさまよっていたら Unity Test Tools なるものを発見。 Unity Test Tools Released これを書いてる時点で、3日前のリリースです! タイムリーだ!! 英語でよくわかんないけど、スクリーンショットはわかりやすくてかっこいいぞ! 遊んでみよう! 事前準備 まず、Unity Testing Tools をダウンロードしてこよう! Aseet Store に並んでるので、ダウンロードボタンを押してしばらく待ってれば Unity が勝手に使える状態にしてくれます。 簡単なユニットテストを書いてみる 以前れもんさんが書いた「#24 「Unityでコルーチンも単体テストしよう」 tech.kayac.com Advent Calendar 2012」を Unity Testing Tools でやってみました。 テストの対象はこんな感じのクラスです(短く書けそうな部分があったのでちょっと変えた)。 namespace MyProject { public class Plan { public string Title { get; private set; } public string Text { get; private set; } public Plan(string title, string text) { Title = title; Text = text; } } } れもんさんの記事ではSharpUnitを使っていましたが、 Unity Test Tools は NUnit というテストフレームワークを使うようです。 Plan のテストをNUnitを使って書きなおしてみます。

Dec 7, 2013 - 1 minute read - perl ark

Ark-View-DataTable グラフや表やCSVを簡単に表示したい

こんにちは、最近ログの解析をして遊んでいるいっちーです。 解析の結果は最終的にグラフに出すわけなのですが、 先輩方がよく使っているのもあって Google Charts を使ってます。 で、このグラフを他の人に見せると「その元データCSVでちょうだい!」と言われるんです…。 もちろんcsvを作るなんてこと簡単にできるんですが、 今のプログラムにはグラフ用のテンプレートとHTMLで表出力するためのテンプレートとCSV用のテンプレートがあって、 グラフが追加されるたびにコピペして微妙に書き直し、 という不毛な作業が発生してしまうのです。つらい。 Ark::View::DataTable 使い回しの効かないテンプレートとかなんのためのテンプレートなのか。 データだけ用意してあとはそれぞれのテンプレートに入れるだけとなるのが理想的だよねー、と思い続けて早数ヶ月。 ようやく重い腰を上げて Ark::View::DataTableってのを書きました。 使い方 Data::Google::Visualization::DataTable をレンダリングするための ArkのViewです。 use Ark::View::DataTable; use Data::Google::Visualization::DataTable; sub gvis :Local { my ($self, $c) = @_; my $datatable = Data::Google::Visualization::DataTable->new(); $datatable->add_columns( { id => 'x', label => "X", type => 'number' }, { id => 'y', label => "Y", type => 'number' }, ); # 〜〜〜〜正弦波を描きましょう〜〜〜〜 $datatable->add_rows( map { [$_, sin(2*3.1415926535*$_/500)] } 1..1000, ); $c->stash->{table} = $datatable; $c->forward( $c->view( 'DataTable' ) ); } Controllerに感じでかくと使えます。 「/gvis?

Nov 9, 2013 - 1 minute read - isucon perl

ISUCON3の本戦に参加してきた

ISUCON3の予選を何とか通過し、 本戦へと参戦してきました。 大会中の方針とか考えたこととかメモ。 お題 Tw○tter–likeな画像投稿サービス ユーザをフォローできる フォローしたユーザが画像を投稿すると、タイムラインに画像が流れる 公開範囲を全員に公開・フォロワーのみに公開・自分だけに公開から選べる タイムラインはロングポーリングを使ってリアルタイム反映 JSON-APIが用意されていて、Javascriptから叩く 使用できるサーバは5台 画像を扱うお題と聞いて、会場がざわめきました。 MySQLのクエリを見てみる 開始直後、鍵を用意したり、gitのレポジトリを立てたりなんだりした後、 一回目の計測。 topコマンドで走っているプロセスを見ていると、大量のconvertが!! プロセス名とお題から考えるに、こいつら確実にImage Magickだ・・・。 CPUのほとんどが画像の変換にくわれていたので、 まずは「どこかでキャッシュする」作戦をとることに。 キャッシュするならフロントに近いほうがいいだろうということで、 フロントのnginxでキャッシュする作戦をとることにしました (アクセス制限があるimageは難しいかもしれないけど、全部publicなiconならすぐできるだろうとこのときは思ってました)。 僕はnginxがconvertを駆逐してくれると信じて、MySQLに投げているクエリを中心にPerlのコードを見てました。 役割分担はこんな感じ。 サーバの設定とか(@mackee_wさん) nginxでキャッシュする設定(@9reさん) コード読む、主にMySQLに投げてるクエリとか(@shogo82148) 毎回、ひどいクエリが仕込まれているようなイメージがあったけど、 今回はそこまでひどくない。 クエリチューニング全然効果なさそうと判断して、次の作戦を考えることにしました。 No Image Magick, use Imager! やっぱり一番のボトルネックは画像変換。 nginxでキャッシュするとはいえ軽いほうがいいよね、ということで、 外部プロセスで実行している画像変換をImagerを使ってPerlと同じプロセスでやる作戦。 Imagerに置き換え後ベンチにかけたら、若干スコアが・・・上がった・・・ような・・・? しかし、画像が変化していると怒られて、スコアは無効。 画像エラーを修正するコストと、スコアの上がり具合を見て、Image Magickのままにすることにしました。 予選でも同じように外部プロセス起動している部分をPerlのライブラリにしたけど、 その時はあっさり動いた。 あれは外部プロセス起動をやめたらスコア上がると思い込ませるための布石だったんだ・・・。 (今回の場合、プロセスの起動より画像の変換のほうが重いので、スコアが上がらないのは当たり前) いろいろ諦めてPerl側でファイルキャッシュ Imagerはテストを通らず、nginxの設定キャッシュ設定も上手く動作しなかったので、 Perlでファイルキャッシュする方針に変更。 convertの結果にmvで適当な場所にコピーして保存。 これだけでスコアが5倍くらいに跳ね上がり、一気に上位に浮上! 最初からやっておくべきだった・・・。 もうちょっと早ければ特別賞もらえたかもしれないのに。 rsync! rsync! ファイルキャッシュの作業をやっている間に、@mackee_wさんがnfsの設定をやってくれたので、 アップロードされたファイルやキャッシュファイルの保存先をnfsに変更。 あとは物量作戦でいくしかないだろうということで、rsyncで他のサーバにコピーして調整を繰り返してた。 (並行してnginxのキャッシュ設定にも再チャレンジしてたけど、nginx力が足りなかった) 最終結果 テストFAILした!! No Score!! なんかこんなの前もあった! 反省点 画像変換をGETでやってたけど、POSTでやったほうがよかったかも nginxについて勉強しよう nfsについて勉強しよう

Oct 18, 2013 - 1 minute read - perl redis

Redis::NamespaceとRedis::Keyをリリースしました

こんばんは、最近シングルトン恐怖症になっているいっちーです。 Redis::Namespaceと Redis::Keyをリリースしました。 Redis::Namespace 「Redis::NamespaceのPerl版書いた」 で紹介したモジュールをCPANizeしました。 コマンドのキー名に当たる部分に、自動にプレフィックスをつけてくれる賢い奴です。 use Redis; use Redis::Namespace; my $redis = Redis->new; my $ns = Redis::Namespace->new(redis => $redis, namespace => 'fugu'); $ns->set('foo', 'bar'); # $redis->set('fugu:foo', 'bar'); my $foo = $ns->get('foo'); # my $foo = $redis->get('fugu:foo'); RedisにはKey-Value Storeなんてかっこいい名前が付いているけど、 結局はシステム全体で使えるグローバル変数なわけです。 グローバル変数は駆逐するべきです。 いちいちプレフィックスつけて名前の衝突を回避するなんて人間のやることとは思えません。 せめてモジュールローカルとか、クラスローカルとかある程度スコープを制限したいですよね。 Redis::Namespaceを使えば簡単に実現できます。 Redis::Key Redis::Key は Redisのキーの簡単なラッパークラスです。 毎回毎回「接続先のRedisサーバ」と「キーの名前」を指定するのは面倒です。 この2つをセットにして、一つのオブジェクトとして扱うことができます。 use Redis; use Redis::Key; my $redis = Redis->new; my $key = Redis::Key->new(redis => $redis, key => 'hoge'); $key->set('fugu'); # $redis->set('hoge', 'fuga'); $key->get; # $redis->get('hoge'); 普通に使っている限りは他のキーにアクセスすることができなくなるので、 Redis::Keyのオブジェクトを他のクラスに渡す、とかしても安心です。

Oct 13, 2013 - 1 minute read - redis perl

Redis::Fastをcpanize&アップデートしました

Redis::Fastをcpanizeしました! さらに!早速不具合が見つかったので0.01から0.02にアップデートしました! CPANに上げてから24時間も経たないうちにpull requestがやってきてCPAN怖いところです。 最初のバージョンである0.01ではタイムアウト処理をちゃんと書いていなかったので、 タイムアウト時に無限ループに陥る不具合がありました。 LinuxとMacとでコネクションを張るのに失敗したときの挙動が違うらしく、 Linuxでは問題なくテストが通るのに、Mac上でのテストでは再現するという面倒バグでした。 さらに面倒なことにRedisの起動のタイミングによって、 Macでもテストが通ったり通らなかったりするという・・・。 主に開発はLinux上でやって、Linux上でしかテスト動かしてなかったので全く気がついていませんでした。 CPANデビューのモジュールがネットワーク関連でXSで少しハードルを上げ過ぎた感じがします。 環境依存な部分が多くてつらいです。 pull requestを送ってくださったsyohexさん、実際にインストールを試みてテストが通らないことを教えてくださったみなさん、ありがとうございました。 アップデートした0.02では、タイムアウト時の処理を少し書きなおして、pull requestも取り込みました。 Mac上でも問題なくテストが通ってインストールできるはずです(きっとね)。

Oct 7, 2013 - 1 minute read - isucon perl

ISUCON3の予選に参加してきた

こんにちは、いつの間にかチームぽわわ2のメンバーになっていたいっちーです。 @9reさんと @mackee_wさんとでISUCON3の予選に参加してきました。 主にアプリの書き換えを担当していたので、やったことを残しておきます。 チーム全体の方針とか役割分担とかはまこぴー先生の#isucon 予選でとりあえず10位だったを参照。 お題 gistみたいなWebアプリ。 社内ISUCONのときと似たようなお題ですね。 違いは… スターは無い Recent Postsのサイドバーが無い代わりに、ページングしてたどっていけるページがある privateな投稿ができる Markdown形式で投稿できて、表示はHTMLでレンダリングされる 詳しくは、れもんさんの#isucon 2013年予選問題の解説などを参照。 やったこと 一言で言えば、Redisにキャッシュするようにしました。 RecentをRedisのリストで管理 Recentの表示で日付順ソートしているのが重たそうだったので、 公開メモのソート結果をあらかじめRedisのリストに入れておく作戦。 RedisのSORTコマンドが高機能で面白いなーって思ってたので使ってみました。 リストにはメモのIDだけ入れておいて、メモの実体は別のキーを参照する、なんてことができます。 このコマンド、SORTって名前なのに「ソートしない」ってオプションあるところがいいですよね! MySQLがボトルネックになっているのはこれで解消できました。 bin/markdownを使わない&レンダリング結果をキャッシュ Markdownのレンダリングを外部コマンド叩いてやっていたので、 Text::Markdown::Discountを使ってレンダリングするように変更。 qx{hoge}って記法はじめて見ました。Perlってやつはいろんな書き方があってよくわからないです。 Markdownの文法って亜種が結構あるので、レンダラをかえるのはちょっと怖かったんですが、全く問題なし。 スコアも3000くらい上がってかなり効果がありました。 さらにレンダリング結果をRedisに入れてキャッシュで+1000くらい。 Recentのレンダリング結果をキャッシュ RecentをRedisでさばくようにしたけど、そもそも100要素もあるHTMLのレンダリングそうとう重いはず。 と、いうわけでここもRedisにキャッシュするようにしました。 公開メモが投稿されたらRecent/:pageのキャッシュを全部削除。 Postのたびにキャッシュクリアされるのであんまり効果ないかなーと思っていたけど、わりと効果あったみたい? (正確なスコアよく見てなかった) Redis::Fast!! 残り時間も少なくなり時間内にできることも限られれきたので、最後の最後でRedis::Fastを投入。 これで+1000くらい上がったらしい。(正確なスコアよく見てなかった) s/Redis/Redis::Fast/ するだけの簡単なお仕事の予定が、githubからのインストールに一番手間取った。 cpanfileにgitのレポジトリを書くと(非公式だけど)インストールできるよ!ってどこかで見た気がするけどなかなかうまく行かず、 自分でgit cloneしてそのディレクトリを指定してインストール(したってまこぴー先生が言ってた)。 (hiredis.hが無い!って叫んでいたから、cartonがsubmoduleをうまく処理できていなかったと予想。 非公式の機能に頼るの良くないね。) できなかったこと my.cnf?なにそれ美味しいの? SQLクエリをいじる余裕がなかった Newer/Olderのクエリが残念なのはわかってたけど、結局いじってない Nginxでキャッシュしたい 必要なモジュールは事前にCPANにあげておこう。 まとめ 結果は13192.1点で10位でした。 特に問題がなければこのまま予選突破できるはず・・・! ところで、魔王軍が学生枠を制圧していて恐ろしいですね。 てか、僕らのチームとの差、500点程度しか無いじゃないですか。怖!!! これ以上の侵攻はなんとしてでも食い止めなければ。

Sep 28, 2013 - 4 minute read - perl redis

Redis::Fastってモジュールを書いた

hiredisをPerlから扱うためのライブラリとして Redis::hiredisってのがあるけど、 なんだか微妙だって聞いたので自分でPerlのhiredisバインディング書いてみたよ。 https://github.com/shogo82148/Redis-Fast (READMEからRedis.pmをそのまま持ってきたことがまるわかりですね。なんとかしよう。) 使い方 Redis.pmと全く同じインターフェースなので、 そのまま置換できる、はず。 use Redis::Fast; my $redis = Redis::Fast->new; ### synchronize mode $redis->set('hoge', 'piyo'); print $redis->get('hoge'); # piyo ### asynchronize mode $redis->get('hoge', sub { my ($result, $error) = @_; print $result; # piyo }); $redis->wait_all_responses; ### pubsub $redis->publish('fugu', 'fuga'); $redis->subscribe('fugu', sub { my ($message, $topic, $subscribed_topic) = @_; }); my $timeout = 10; $redis->wait_for_messages($timeout) while 1; 以前作った、Redis::Namespaceにもそのまま使えます。 use Redis::Fast; use Redis::Namespace; my $redis = Redis::Fast->new; my $ns = Redis::Namespace(redis => $redis, namespace => 'fugu'); $ns->set('foo', 'bar'); # $redis->set('fugu:foo', 'bar'); my $foo = $ns->get('foo'); # my $foo = $redis->get('fugu:foo'); ベンチマーク Redis.

Sep 24, 2013 - 1 minute read - perl yapcasia

YAPCへ行ってきた(二日目)

前回のポストにつづいてYAPC二日目。 聞いたトークの内容を簡単にメモ。 Perl で書く結合テスト 前半はSWET(Software Engineer in Test), TE(Test Engineer)といった業種の話。 後半はテスト手法の分類(誰がする?テストの対象は?方法は?目的は?)について。 スライドはこちら→[Perlで書く結合テスト(]http://ikasama.hateblo.jp/entry/2013/09/22/234521) これからのPerlプロダクトのかたち 世界一高速な処理系を目指して開発中のgperlと、 その過程でできたツールの紹介。 PerlをLLVMにコンパイルすることがで、高速動作するらしい。 恐ろしい・・・。 Perlは文脈によってトークンの意味が変わってしまうから、トークナイザーを作るのに苦労したとのこと。 (例えば、hoge * fuga とあったときに、*が掛け算なのかブロブなのかわからない) コンパイルの高速化のために文法を工夫しているKuinを見習って欲しいですね。 Emacs実践入門 Perl編 typester先生によるEmacs入門。 PerlCompletion とか helm とか便利そう。 あんまりEmacsカスタマイズできていないので、今度いろいろ入れて遊んでみよう。 Perlでレコメンデーション 登壇者はJubatusのPerlモジュールを書いたりしているらしい。 Jubatus に触ってみようと考え始めてからどれだけの月日が経っただろう・・・ そのうち触ってみます。そのうち。 中規模チャットサービスの運用事例 handlename先生のLobi運用のお話。 今日もcronのメールが迷惑メールフィルタによって闇に葬りさられる悲しいことがあったので、 cronの結果をIRCに飛ばすのとか参考にして何とかしたい。 PhantomJSによる多岐にわたる広告枠の確実な表示テスト 最近の広告はJavascriptを使った遅延読み込みをするので、 ちゃんと表示されるかを静的に判断することができない。 そこで PhantomJS を使ってテストするお話。 フルテストも50msで終わらせたい 〜 FreakOutの取り組み 〜 さすがにフルテストは50msで終わりません。 Ukigumoを使って複数台のサーバでテストを分散実行する取り組みを紹介。 スライド→http://yapcasia.org/2013/talk/show/767463b0-d8fd-11e2-971a-72936aeab6a4 LT 前日にアイデアだけLTで紹介したHTTP::Body::Builderが、別の人の手によって実現されていたのには驚いた。 YAPC恐ろしいところだ・・・。 HUB 懇親会参加しない組だったので、 @sasaplus1 さん, @kazuph さん, @aokcub とHUBで飲み会。 なぜ学内にHUBがあるんだ・・・? NDS勢やNiigata.pm勢、あと何故かスタッフになっていた @jewel_x12 とも会えて楽しかったです!