Shogo's Blog

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 とも会えて楽しかったです!

Sep 20, 2013 - 1 minute read - perl yapcasia

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

YAPCの一日目に行ってきたよ。 いまどきのカジュアルなデータベース関連開発 Songmu先生のセッション。 DBIx::Schema::DSL とか GitDDL::Migrator とかの説明や、 DBのスキーマ設計、Redisの紹介なんかがありました。 自分もMySQLやRedisを触る機会が増えて、DB周りでつらい思いをしたことが何度かあるので (外部キー制約でデッドロック起こしたり、無駄なインデックスを必死に削除したり・・・) 大いに参考に参考にさせていただきます。 スライドはこちらから→いまどきのカジュアルなデータベース関連開発 学術分野におけるPerlの活用例 Perlを使ったアンケートの結果と、PerCUDAの紹介。 GPGPUをPerlのコードで実現しようとのお話。 大規模Perl初心者研修を支える技術 :DeNAさんが行った研修の紹介です。 顔覚えられない、 研修生の状況把握が大変、 信頼関係を作るのが大変 といった問題をどうやって解決したかについてのお話がありました。 トークの中で紹介された本何冊か持っているけど、全然読んでない・・・。 というか研修生みんなこれ読んだんですか。 スライドはこちらから→大規模Perl初心者研修を支える技術 mod_perlの展望とApacheの超絶技巧 最近僕の周辺ではあまり Apache の話題を聞かなくなってしまいましたね。 しかし、その知名度の高さからか、他のオープンソースのプロダクトはダメでも、 Apache はOKという案件があるらしい。 「Apache使いました!」っていうために、mod_perl で代替品を作ろう、というお話。 おそろしい・・・。 スライドはこちらから→mod_perlの展望とApacheの超絶技巧 0から学んだポストモダンPerl ルーティングとかORMはWAFにはいらない。 blessで十分!これぞ、ポスト・モダンPerl!とのことでした。 僕もフルスタックのフレームワークより、 各機能が別になっているほうが好きですね。 (でもblessよりはクラスを扱うためのライブラリ使ったほうがよいと思う) まあ、あんまり大規模なWebアプリ作ったこと無いので、 実際に作ってみると意見が変わるかもしれませんが。 スライドはこちらから→0から学んだポストモダンPerl Dist::Zilla 英語のトークに紛れ込んでしまい、正直良くわからなかった。 英語能力全く向上していない。 モジュールを作成、テスト、アップロード等の管理をするためのプログラムらしい。 Redis::Namespace でつらい思いをしたので、 次モジュールを作りたくなったら試してみよう。 perl な web application のためのテスト情報 スライドの順番が正しいか、今使っているのは本当にマイクなのかのテストが必要ですね! 335さん自らテストの必要性を教えてくれました。 「なぜテストが必要か」言葉では語らず行動で示す335さんかっこいい。 Test::Deep は Redis::Namespace のテストでも一部使っていますが、これ便利ですね。 Test::More の is_deeply はちょっと不便だと思っていたので、今後も使っていこうと思います。

Sep 14, 2013 - 1 minute read - perl redis

Redis::NamespaceのPerl版書いた

Redis のキーにプリフィックスつけるの面倒だなー自動的につけてくれないかなーと思い、 調べてみると Ruby に Redis-Namespace というものがあるらしい。 だけども、Perl では探しても見つからなかったので書いてみた。 レポジトリはこちら→Redis::Namespace 使い方 インターフェースは Perl Redis と一緒。 コマンドのキー名に当たる部分に、自動的にプレフィックスをつけてくれる。 use Redis; use Redis::Namespace; my $redis = Redis->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'); 大体のコマンドには対応したつもり。 別のプレフィックスがついたキーには基本的にアクセスできなくなるので、 キー名の管理が少し楽になると思います。 でも、flushdb とか flushall すると全部消えるので気をつけてね!

Aug 24, 2013 - 2 minute read - perl redis

Perl の Redis ライブラリを調べた

最近Redis を使ったコードを書くようになったのですが、 キー名を毎回指定するのがだるいです。 Ruby には redis-objects というのがあって、 Redisのキーをオブジェクトとして扱うことができるようです。 きっと、Perl にも似たようなのあるだろ、って思って調べてみました。 ほしいもの 低レベルなRedisのライブラリはたいていメソッドとRedisのコマンドが一対一対応していて、 次のようなコードになると思います。 $redis->set('key-name', 'piyopiyo'); $redis->get('key_name'); でも、Redisに何か操作をしたいわけじゃなくて、 Redisのキーに対して操作をしたいので、 次のように書けるべきだと思うんです。 my $key = key($redis, 'key-name'); $key->set('piyopiyo'); $key->get(); Redis::Hash, Redis::List Redis::Hashと Redis::Listは Perlのハッシュや配列と同じ操作で Redis にアクセスできるようにするライブラリ。 use utf8; use warnings; use strict; use 5.014; use Redis::Hash; tie my %my_hash, 'Redis::Hash', 'hash_prefix', (server => 'localhost:6379'); # set hash_prefix:hogehoge piyopiyo # set hash_prefix:fugafuga fugufugu $my_hash{hogehoge} = 'piyopiyo'; $my_hash{fugafuga} = 'fugufugu'; # get hash_prefix:hogehoge piyopiyo say $my_hash{hogehoge}; # piyopiyo # keys hash_prefix:* say join ',', keys %my_hash; #fugafuga,hogehoge # keys hash_prefix:* # get hash_prefix:fugafuga # get hash_prefix:hogehoge say join ',', values %my_hash; #fugufugu,piyopiyo # del hash_prefix:hogehoge delete $my_hash{hogehoge}; tie とかよくわかない。 Perl の黒魔術を見た気がしました。

Jul 13, 2013 - 3 minute read - perl

ランダム抽出アルゴリズムについて考える

数日前に社内IRCで「スマートな非復元抽出の方法はないか」と話題になったので、 ランダムサンプリングのアルゴリズムについて調べたり考えたりしてみた。 復元抽出 非復元抽出の手法って調べてもなかなか出てこない・・・。 ひとまず、復元抽出についてまとめてみましょう。 線形検索 一番簡単な実装方法。 どの区間に入るかを線形検索して求める。 選択肢の個数nとすると計算量はO(n)。 use strict; use warnings; use List::Util qw(sum); sub linear_search_method { my $weights = shift; my $num = shift; my $sum = sum @$weights; my $length = @$weights; my @a; for (1..$num) { my $r = rand($sum); for my $i(0..$length-1) { $r -= $weights->[$i]; if($r < 0) { push @a, $i; last; } } } return \@a; } print join ', ', @{linear_search_method [1,2,3], 100}; バイナリサーチ あらかじめ累積分布表を作っておき、どの区間に入るかをバイナリサーチ。 準備にO(n)、選択に O(log n)かかる。

May 15, 2013 - 2 minute read - chrome perl

Google Cloud Messaging for Chrome を試してみた

少し前にGoogle Cloud Messaging for Chrome が発表されました。 Android向けに提供されていた Push 通信の仕組みである GCM の Chrome 版です。 ちょうど GCM for Android に触っていたところだったので、 for Chrome のほうも試してみることにしました。 拡張機能の登録 公式ページの説明にしたがって、 APIを使えるようにします。 GCMはOAuth2.0で認証を行うので、 クライアントIDを作る Refresh Token を作る という2ステップが必要。 クライアントIDを作る まず、新しい OAuth2.0 のアプリを作成。 拡張機能をアップロードする予定のGoogleアカウントで以下の作業して Client IDを作ります。 Google APIs Console にログインする ** Create… ** メニューから新しいプロジェクトを作成 “Services” を開いて ** Google Cloud Messaging for Chrome API ** を有効化 “API Access” を開いて ** Create an OAuth 2.0 cliend ID… ** というボタンをクリック branding information を適当に入力 “Application Type” という項目の “Web application” を選択 “Create client ID”!! Client ID と Client Secret が表示されるのでメモしておきましょう。

May 12, 2013 - 1 minute read - RaspberryPi

RaspberryPiからメールを送る

RaspberryPi に cron を仕込んで定期実行をやってみようと考えました。 cron の設定自体は crontab -e コマンドを実行すれば簡単にできます。 ただ、これだけだとちゃんと動いているか少し心配なので、 エラーが起きた時に何か通知して欲しい。 普通なら設定ファイルに MAILTO=hogehoge@example.com と書いておくと メールが送られるはずなのですが、 メールサーバが動いてないのでうまくいかない・・・。 そういうわけで、RaspberryPiからメールを送るための設定をしたのでメモ。 MTAをインストールする Raspberry Pi には標準でMTA(Message Transfer Agent)が入ってないようなのでインストール。 今回はPostfixを採用 sudo apt-get install postfix 最初、Sendmailも試してみたんだけど、送信者マスカレードがなぜかうまく行かなったので断念。 後述するように、この設定がないとスパムフィルタに引っかかってしまうのです。 プロバイダのSMTPにリレーしてもらう 実際にメールを送りには以下の条件を満たす必要があるようです。 送信元のドメインを引ける 固定IPからのアクセス 固定IPなんて自前で持ってないし、 cron からのメールは送信元が pi@raspberrypi になってしまいドメインを引けません。 そのためそのままではスパムメールとして扱われてしまい、メールが届きません。 そこで、プロバイダが提供しているSMTPサーバにメールをリレーしてもらいます。 /etc/postfix/main.cfに以下の行を追加します。 sender_canonical_maps = regexp:/etc/postfix/canonical relayhost = [smtp.example.com]:587 smtp_sasl_auth_enable = yes smtp_sasl_password_maps = hash:/etc/postfix/relay_password smtp_sasl_security_options = noanonymous プロバイダにリレーしてもらうには SMTP-Auth で認証する必要があるので、 ユーザ名とパスワードを設定しておきます。 smtp.example.com hogehoge:your-password postmapコマンドを使って、Postfixから扱える形式に変換します。 $ postmap hash:/etc/postfix/relay_password さらに、エンベロープのFromがプロバイダから提供されたメールアドレスでないと メールをリレーしてくれないので、 すべてのメールのFromをすべて書き換えるよう設定します。

May 12, 2013 - 1 minute read - RaspberryPi Python

RaspberryPiでhttps通信が失敗するのを何とかする

RaspberryPiをネットつないでみたので、PythonからいろんなURLを叩いて遊んでいたんだけど、 一部のhttps通信が Connection Timed Out で失敗しちゃう。 プログラムの問題かと思ったけど、curlで叩いてもやっぱりタイムアウト。 Macで全く同じ事をするとうまくいく・・・。 いろいろ調べて、何とかしてみたお話。 原因 接続先がTLSv1にしか対応していないのにSSLv3でアクセスしようとしていたことが問題だったらしい。 明示的にTLSv1を使うように指定して curl を叩いてみるとうまくいった。 $ curl --tlsv3 https://hogehoge なぜRaspberryPiではダメで Macでは成功するのか、という根本的な原因はよくわからなかった。 SSLv3に対応していないなら自動的にフォールバックしてくれてもよさそうなものだけど、 なぜうまく行かないんだろう・・・? Pythonでの対処 PythonでもTLSv3を使えばうまくいくはずなんだけど、 暗号化方式を指定するオプションは見当たらない(2.7での話)。 どうやら標準ライブラリのファイルを直接書き換えるか、 実行時に中身を入れ替えるかしないとできないみたいだ。 この問題普通のUbuntuでも起こるらしく、 そのフォーラムで置き換えコードを見つけた。 import httplib from httplib import HTTPConnection, HTTPS_PORT import ssl class HTTPSConnection(HTTPConnection): "This class allows communication via SSL." default_port = HTTPS_PORT def __init__(self, host, port=None, key_file=None, cert_file=None, strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None): HTTPConnection.__init__(self, host, port, strict, timeout, source_address) self.key_file = key_file self.cert_file = cert_file def connect(self): "Connect to a host on a given (SSL) port.