すぎゃーんメモ

2013-03-30

#perlcasual で再びライブコーディングさせていただいた

PerlCasual #05 : ATNDにて、@さんに声をかけていただき、再びライブコーディングをさせていただいた。

前回やらせていただいたのは2年半以上前なのか…

第6回 yokohama.pm & Perl Casual でライブコーディングに挑戦してみた - すぎゃーんメモ


前回はちょっとしたスクリプト的なものだったけど、今回はWebアプリっぽいのはどうか、ということでAmon2を使ってWebアプリを作成する、というお題にしてみた。

という目標でやってみた。


掲示板的なのは実はAmon2のtutorialにあるやつですね。

Tutorial - Create BBS site — Amon2 3.69 documentation


結局できたのは http://sleepy-tor-6236.herokuapp.com/ というところに。書いたコードは https://github.com/sugyan/perl-casual-2013-03 にそのままpushしておきました。



まぁテンパったり余計なところで時間つかってしまったりで「とりあえず掲示板っぽいものが動いた」くらいのところで30分すぎてしまいましたね。いちおう構想としては

  • 投稿時間もちゃんと保存して表示
  • sqlite使っていたところをheroku postgres使ってちゃんと永続化できるように
  • ただ投稿されるだけじゃなくて形態素解析して全裸に

とか考えてはいたのだけど、予想通り自分の能力では30分でそこまでは出来なかった。

カンペは用意してあったけど結局使わなかった。

カンペ.md


準備とかの話

herokuは個人でちょっとしたアプリを公開するのによく使っていたけど、いつもRubyでやっていて、Perlで作ったことはなかった。昨日くらいにようやくperl buildpackを試してみて、miyagawaさんのオリジナルではうまく動かないっぽいことに気付いた(要・再検証)。

最初のcarton installに時間かかるなー、というのはすぐに感じて、でもまぁherokuのセットアップも同じように時間かかるし並行して作業すれば良いかな、と。ただネットワークの不安はあったのでcarton bundlecarton install --cachedを使ったインストールを試してみたところbundleが上手く動かないことに今日になって気付いて、tokuhiromさんに相談したところすぐに直していただいたのでなんとかなった。

OAuthログインも最初は違うモジュールとか使って愚直にやろうとしていたけど事前にtokuhiromさんに相談していたらAmon2::Auth::Site::Githubの存在を教えていただいたので(使ったことあったはずなのにすっかり忘れてた)、それを使うことに。pluginでそういうのも簡単にできるアピールをしたかったけど、伝わったかしら…

あとはもう、ちょっといじってすぐに30分経ってしまいそうで そんな凝ったものは作れなそうだと感じて。Twitter Bootstrapでのフォームの書き方とかdbhの使い方とか少し復習して、ターミナル文字サイズを大きめの環境でひとりで何度か練習。

いちおうアレでも何度か練習したんですよ、、、

Emacsはもうちょっとモダンな設定とか整えてやりたかったけど、そこまで余裕なかったなぁ…


ありがとうございました

他の発表者の方々のお話もとても面白かったです。特に@さんの話!

元タワレコ店員×Perl×Webサービス (PerlCasual #05)


久しぶりに勉強会的なものへの参加&発表側でだいぶドキドキでしたが、とても楽しかったです。

声かけてくださったyusukebeさん、運営に関わってくださった方々、懇親会でお話してくださった方々、ありがとうございました!

2012-12-31

2012年を振り返る。そして2013年に向けて

2009年を振り返る。そして2010年に向けて - すぎゃーんメモ

2010年を振り返る。そして2011年に向けて - すぎゃーんメモ

2011年を振り返る。そして2012年に向けて - すぎゃーんメモ

を見返しつつ。


2012年の「すぎゃーんメモ」

TopHatenarによるとブクマ数はこんなかんじ。

f:id:sugyan:20121231173827p:image

ホッテントリとかは無し。

記事数は少なかったけど、まぁボチボチと自分なりにメモを残したりは出来ていたんじゃないかな、と…


思い返す

結局今年やったことのすべてはこのスライドにすべて詰まったかんじ。


反省

ヲタ活動に一生懸命になりすぎて勉強会などもあまり行かなかったし、技術者としてはほとんど進歩もなく停滞の一年だった。


2013年の目標

なんとか人生軌道修正していきたいと思います。徐々に。


最後に

自分自身は色々とアレでしたが、今年も多くの人に優しく接していただき楽しく過ごすことが出来ました。ありがとうございました!

来年も多くの方々と仲良く楽しく過ごせると良いな、と思います。よろしくお願いします。

2012-11-08

perlbrewを利用したプロジェクトごとのPerl環境管理

整理するためのメモ。

  • よくある問題: プロジェクトごとの依存モジュールの管理
  • 全環境共通でインストールするとモジュールのバージョンが分けられない
  • local::libcartonを使ってプロジェクト専用のインストール領域を作るのが良い
  • しかし実行するPerlのバージョンが違うと動かなかったりするし
  • だったらPerlそのものもプロジェクトごとに管理した方が
    • 同一アーキテクチャの複数サーバにデプロイするときも1箇所で環境作ってディレクトリ丸ごとrsyncで済むし
  • というわけでプロジェクト専用のPerlperlbrewインストールして使おう
    • ビルドに時間かかったりもするけどまぁ最初の一回だけだし我慢

手順

既にperlbrew自体は標準の方法でインストールしておいていて使えてる、という前提で

$ cd <PROJECT_ROOT>
$ export PERLBREW_ROOT=${PWD}/perl5
$ perlbrew init
$ source ${PERLBREW_ROOT}/etc/bashrc
$ perlbrew install 5.16.2

みたいなかんじでプロジェクトディレクトリ内にPERLBREW_ROOT環境変数をセットした状態でインストール。するとそこ以下を作業ディレクトリとしてインストールしてくれる。

その後、実際に使うperlにswitchしたいところだけど、普通にperlbrew switchをすると、$HOME/.perlbrew/initに諸々の環境変数情報が書き込まれることになり、下手すると普段使っているperlbrew用の環境を壊してしまう。PERLBREW_HOME環境変数をセットしておけばそこ以下にinitファイルを作って使うようにしてくれるので、セットしておく。

$ export PERLBREW_HOME=${PWD}/perl5
$ perlbrew switch 5.16.2

これで、あとは<PROJECT_ROOT>/bin/env.sh

#!/bin/sh

# require perlbrew
if ! which perlbrew; then
    source ${HOME}/perlbrew/etc/bashrc
fi

# set environment for PROJECT_ROOT
PROJECT_ROOT=$(dirname $(dirname $0))
PERLBREW_ROOT=${PROJECT_ROOT}/perl5
PERLBREW_HOME=${PROJECT_ROOT}/perl5

# use switched perl
source ${PERLBREW_ROOT}/etc/bashrc

exec "$@"

といったシェルスクリプトを用意しておいて、perl絡みのコマンドを常にこれを経由して使うようにすれば、プロジェクト内で完結する環境で動かすことができる。

$ perlbrew list
  perl-5.10.1
  perl-5.12.4
* perl-5.14.2
  perl-5.16.0
  perl-5.16.1
  perl-5.16.2
  perl-5.17.3
  perl-5.8.9
$ which perl
<HOME>/perl5/perlbrew/perls/perl-5.14.2/bin/perl

$ cd <PROJECT_ROOT>
$ ./bin/env.sh perlbrew list
* perl-5.16.2
$ ./bin/env.sh which perl
<PROJECT_ROOT>/perl5/perls/perl-5.16.2/bin/perl

$ curl -L cpanmin.us | ./bin/env.sh perl - App::cpanminus
$ ./bin/env.sh which cpanm
<PROJECT_ROOT>/perl5/perls/perl-5.16.2/bin/cpanm
$ ./bin/env.sh cpanm --installdeps .
$ ./bin/env.sh plackup

開発時にはProcfileに、デプロイ用にはdaemontoolsのrunスクリプトenv.sh経由で立ち上げるように書いておいて、そこ経由でしか立ち上げないようにしておけばミスも少なくなる気がする。


参照

perlbrewを使うにあたっていろいろな小細工をした件 - (ひ)メモ

http://yappo.github.com/talks/20121019-yokohamapm9-amon2/index.html#/step-3

2012-11-05

#isucon2 のお手伝いをしました

昨年は参加者として出場して楽しませていただいた、ISUCON。

#isucon で優勝したチームのメンバーとして参加してた - すぎゃーんメモ

今年は奇しくも開催側の会社に転職したということで、参加者としてではなく運営側としてお題作りなど裏側に関わっての参加になりました。

確か入社前の1月 #bphbqpstudy2012 にて飲んだときにtagomorisさんに「入社したら次のISUCON手伝いますんで」なんて酔っ払いながら言っていたような気がするけど、おかげで準備スタートのときに声をかけていただいて、微力ながら手伝わせていただくことができました。


初期アプリ

まずは何となくの「チケット販売サイト」というお題の方針が決まり、昨年のお題 を参考に叩き台アプリを作らせていただきました。

このへんのネーミングは自分ですね。DISられなくて良かったです。

https://github.com/tagomoris/isucon2/blob/4cc32693905ae6cb9fbc39c1af4e5e6f280131f0/webapp/config/database/initial_data.sql#L10


最初にPerlアプリを、まず昨年と同じくKossy を使って一通り各ページと購入処理までできるよう書きました。今回はトランザクション処理も必要だしちょっとSQL発行も多く、生DBIよりモジュール使った方がいいかな、とDBIx::Sunny も使用することにしました。

Kossyはサイドバーみたいなのを実装するときにfilterを簡単に利用することができて便利ですね。


で、ある程度のアプリ仕様が固まってきたところで他の言語も用意し始めました。自分は一応Node.js, Rubyもこれくらいなら出来そう、ということでNode.js版Ruby版 も担当させていただきました。Python版、PHP版、Java版はそれぞれ社内の別の方に書いていただいて、その後の修正などに幾らか手を入れたりさせていただきました。


調整

ある程度揃ったところで、並行してid:tagomorisさんが作っていた負荷ツールやチェッカーも出来てきて、それを使ってそれぞれの言語でちゃんと動くか確かめます。

その後も、チェッカーで使うためにDOMを変更したり、より意地悪な問題になるように仕様を変更したり、でPerl版はちょいちょい変更がかかります。それに追従して他言語でも同じSQLを発行して同じDOMを吐くようテンプレートを修正して…とかはなかなか面倒でしたね。。複数の言語で同じ動作をするアプリを作る、というのはなかなかこういうところが大変だと思いました。

静的ファイルは統一してシンボリックリンクの先を参照するようにしていたのでラクでしたが。


デプロイ準備

で、試験用にサーバを用意していただいたタイミングで各言語をappサーバの5000番ポートで動くよう設定します。

前回同様、supervisord を使って起動・終了できるように。

https://github.com/tagomoris/isucon2/blob/4cc32693905ae6cb9fbc39c1af4e5e6f280131f0/webapp/etc/supervisord/isucon.conf


Perl
  • Kossy
  • DBIx::Sunny
  • DBD::mysql
  • Starman

のみ使用。cartonとか使わずにcpanmでextlibに入れてパスを通す形で、Starman でplackup起動。


Node.js版
  • express
  • jade
  • mysql
  • async
  • moment

昨年のものに倣ってexpress を使用して作りました。だいぶバージョンも書き方も変わったとは思うけど。

MySQLに続けてクエリを投げる処理が多く、どうしても普通に書いているとネストが深くなりがちだったので、止むを得ずasyncを多用しました。series, waterfall, mapなど、それぞれ使いやすく書く分には便利ですね。それを使わずに正直に書いたらもっと早くなったりするのかな…?ちょっと分かりません。

デプロイではclusterを使って複数プロセスで動かすようにしました。

今は手元で再現できていないのですが、supervisordから起動して終了させたときに子プロセスが生き残ってしまうという現象が発生し、よく分からなかったのでsupervisordの設定で

stopsignal=QUIT

として無理矢理ころすようにしました。あとで教えていただいたのですが、下記のようにシグナルをちゃんと処理するようにするのが正解のような気がします。

supervisor配下でcluster.jsを動かすためのTips - アルパカDiary


Ruby
  • sinatra
  • slim
  • mysql2
  • unicorn

昨年のものに倣ってSinatra を使用。テンプレートエンジンは個人的な好みでhamlではなくslimに。開発中はshotgunがとても便利でした。

実際のデプロイにはUnicorn を使用。最初はThin でやっていたのだけど、Starmanみたいに複数workerで起動するようなのが出来ない(?)ようだったので、unicornを選択。

で、手元で実際にworker_processes 50で立ち上げてベンチマークを動かしてみたところ、

Mysql2::Error - Too many connections:

というエラーが多発。mysqlのconnectionはリクエスト毎に破棄され切れてくれると思うのに何故?と思ってググってみたところ、

http://unicorn.bogomips.org/Unicorn/OobGC.html

Unicornはデフォルトで5 request毎にGCが走るようになっているっぽい、ということで試しに

require 'unicorn/oob_gc'
use Unicorn::OobGC, 1, %r{\A/}

として1 request毎に走るようにしてみたところエラーが出なくなったのでコレでいいかなーと思ってこうしました。

普通にMySQL側の設定でmax_connectionsの値を大きくすれば問題ないのかも知れませんが。

(参照: #isucon2 に参加しました - @kyanny's blog)


Python版

詳しくは知らないのですが、flaskというのを使ってWSGIなアプリを作っていただいたので、gunicornというサーバで動かしてみました。



それぞれの言語について専門家の方々から「もっとこうして作ればいいのに!」とかツッコミあればご指摘いただけると嬉しいです。


初期状態スコア

そんなわけでセットアップした各言語で動かして前日夜に初期状態でスコアを取ってみたのが以下になります。

perl:   837 tickets, score:587233
ruby:   440 tickets, score:1117082
node:   102 tickets, score:4818816
python: 466 tickets, score:1054754
php:     90 tickets, score:5461316

ちょっと怪しいのでミスっていたかもしれません!あとでもう一回とりなおしてみるつもりです!


#ISUCON2 を終えて

コンテスト当日は所用があって昼過ぎで退席したので最後まで見届けることが出来なかったのですが、Twitterやブログでとてもエキサイティングに楽しんでいただけた様子を知ることができました。

ブログで作業メモや考えたこと、反省などを書いている方が多く、思考を追うことができて非常にありがたいです。ありがとうございます。

livedoor Techブログ : #isucon2 参加者・関連エントリまとめ


あれこれ考えながら相談しつつお題を調整し、作っていくのはとても楽しかったです。参加者のみなさまにも楽しんでいただけたようで何よりです。

来年どうなるか分かりませんが、また何らかの形で関わりたいな、と思います。

ありがとうございました。

2012-10-15

任意の要素数の密な配列を生成するスニペット

Perlだと、

use Data::Dumper;
my @hoge = ("fuga") x 5;
print Dumper \@hoge;

__END__
$VAR1 = [
          'fuga',
          'fuga',
          'fuga',
          'fuga',
          'fuga'
        ];

みたいな感じでx演算子を使って任意の個数の密な配列を簡単に作ることができるのだけど、JavaScriptではどうしたら良いのだろう?と。

new Array()で任意のサイズの配列は作れるけど、中身が無くて各要素に値を入れることができない。

$ node
> new Array(5)
[ , , , ,  ]
> new Array(5).map(function () { return "fuga" })
[ , , , ,  ]

ただ、Array.joinを使って一度文字列を作ってsplit、とかやれば一応できる。

$ node
> Array(5).join()
',,,,'
> Array(5).join().split(',')
[ '',
  '',
  '',
  '',
  '' ]
> Array(5).join().split(',').map(function () { return "fuga" })
[ 'fuga',
  'fuga',
  'fuga',
  'fuga',
  'fuga' ]

こんな変わったやり方しかないの…?と思っていたところ、@さんに教えていただいた。

Array.apply(null, Array(N))

という方法。

$ node
> Array.apply(null, Array(5))
[ undefined,
  undefined,
  undefined,
  undefined,
  undefined ]
> Array.apply(null, Array(5)).map(function () { return "fuga" })
[ 'fuga',
  'fuga',
  'fuga',
  'fuga',
  'fuga' ]

という具合に、Array.applyへ渡す引数で任意のサイズの配列を渡すことでundefinedが詰まった配列を生成できるらしい。

でもコレはJScriptではダメだそうで… 言語仕様として定義されている動作ではないのかな…?

ちょっとよく分かりませんが、とりあえずNode.jsとかで使う分にはこの方法で良さそうな気がします。


追記

パフォーマンスも試しに測ってみました。まぁ変なことせずに素直にfor文まわした方が効率的には良さそうですね。

http://jsperf.com/create-filled-array