YAPC::ASIA 2013

YAPC::ASIAに初めて参加してきました。

印象に残っているトークの感想などなど。

大規模Perl初心者研修を支える技術

圧巻の71人個別指導。

最初から高トラフィックを想定してデータ設計やコーディングをさせるというのは、そういうノウハウがあるからこそできることで、すごく羨ましいですね。

あとは、自身でふりかえれるようにフォローアップしていくというのは、自立的に動けるメンバーを育てるという観点からもいいやりかただなぁと勉強になりました。

ちなみに、これに関係する話が聞ける師弟登壇・新米サムライの集い 2013というイベントが来月開催されるみたいですよ。

僕の考えたFuture Perl

Perl 6、噂話程度にどういう状況なのかは聞いていましたけど、今の状況を整理して聞くことができたのはとてもよかった。

また、Perl 6 の機能を Perl 5 でも使えるような CPAN が出てきていて、その中でよいものを Perl 5 のコアに取りいれていくというのもよいのではという話は、言語のコアでそういうことをやれるとしたらおもしろいなぁ。

YAPC::Asia Tokyo 2013 特別座談会 「Rubyの良いところ語ってください 〜そんなPerlで大丈夫か?〜」

言語の差よりも、プログラマ自身の能力の差の方がずっと大きいからみんながんばれという話。

Rubyだと普通にその辺に言語のコア開発者がぞろぞろしてるけど、これはかなり特殊というか特別な状況というのを実感した。

Railsの影響でRubyには経済圏が出来たというのと、モダンな有償サービスでPerlのサポートは少なくて自分達で働きかけないといけないというのはハッとした。

本当にあったレガシーな話

どこかで聞いたことがあるような大変そうな話だった。 最後のキーノートにも繋る話で、こういうことをやった人を評価する仕組みっていうのが大切なんだなぁと。

Keynote

マネージャーという役職(?)についていろいろと考えさせられるトークだった。

運動部のマネージャーと会社のいわゆるマネージャー 社内政治のハッカー ディレクション リーダーとマネージャー

Ikebeさんみたいなマネージャーのいる会社はきっと楽しい(だけじゃないだろうけど)と思いました。

Closing

なんとなく、そろそろYAPCに参加してみようかなぁと思ったら、「現行体制での最後のYAPCです」と言われて、嬉しいやら悲しいやらという感じ。

RubyKaigi(1stシーズン)の最後にも立ち合ってしまったし、この業界自体がそういう時期にきているのかなぁと、いろいろ考えてしまいました。

まとめ

はじめて参加したYAPCでしたが、

インターネットでしか見たことない人の顔と名前がいっぱい一致した Perl ちょっとやってみようかな、という気になった 武蔵小杉の横須賀線からの乗り換え設計した人出てこい

という感じで、とても充実した二日間でした。ありがとうございました!

Sansan様でNiigata.rbの再演

昨年からお仕事でお世話になっているSansan様にてNiigata.rbの再演をしました。

この発表を作る上で今のプロジェクトに影響されているところも沢山あるので、そのあたりの裏話を混ぜたりして話してみました。

発表後の質疑応答のときに「なかなか上手にテストが書けないんだけど、いいテストと悪い(?)テストは何がどう違うのか。どうやったらいいテストが書けるようになるのか」みたいなことを言われたのですが、私の思いとしては

describeとcontextが整理されていること そのdescribeとcontextが嘘をついていないこと

で十分じゃないかなぁと思います。

1番目は、ここが整理されていると、エッジケースの漏れがないかとか、仕様のずれや漏れなどがないかとか、一目でわかるのでレビューが楽なんですよね。

2番目は、よくあるのがmockやstubを多用している(せざるをえない)ときですね。 shard_contextとかにも繋がるんですが、せっかく日本語(や英語)で状況を整理して書いているのに、実際にコード見たら「その騙し方はないだろう」みたいになるのがつらいよねという話。 で、多くの場合そういうテストコードは落ちてほしいときに落ちなくて、落ちないで欲しいときに落ちるよね、と。

他にもいろいろ質問をいただいたのですが、外と発表するのとは違ってコンテキストを共有できる状態でのディスカッションはやっぱり話が早いので、こういう機会を増やしていけるといいなぁと思ったのでした。

発表の機会をくださったSansan様ありがとうございました。

60min.第13回「Background Job Worker座談会」

ursmから、このようなものが何故必要なのか、そしてidobataで実際にどう利用しているのか、ということを話してもらった。

なぜ必要なのか

Railsのプロセスはだいたい200Mくらいはメモリを使うので、たとえばそれを32個上げるとするとそれだけで 200M * 32 でだいたい6Gくらいのメモリを使うことになる。(そして、メモリは普通有限なのだ)

たとえば1秒かかる処理があると、同時に捌けるリクエストは32個になってしまい、33個目は1秒後に処理が開始されることになって、レスポンスを返すまで2秒かかる。

これが1秒ならまだいいが、普通、処理待ちをしている間にもどんどんリクエストは来るので、雪だるま式に処理待ちのリクエストが増えていくことだろう。

なので、同期処理が必要ない部分は、積極的に非同期処理に逃がしていかないといけない。

Background Job Workerはどれを使えばいいの?

2013年では以下の4つのライブラリを抑えておけば十分で、それぞれ特徴があるからアプリの特性やインフラと相談して決めるのがいいよということ。

Resque Sidekiq queue_classic girl_friday

まず、ResqueとSidekiqについて。 仕組みはだいたい同じだが、Resqueは処理がプロセス、Sidekiqはスレッドになっている。

Ruby(MRI)はグリーンスレッドなのとMRIは1.9以降はネイティブスレッドだけどGVLがあるのと、マルチスレッドがらみのメモリリークやバグに悩まされたくないなら、とりあえずResqueという選択でもいいかもしれない。

queue_classicは、ジョブキューがPostgreSQLのPubSubを使っているので、ポーリングしている他のライブラリに比べて仕組みがクール。Redis等を入れなくてもいいので便利。

girl_fridayは、ジョブキューを自身のプロセスの中に持っている。ジョブを依頼する側と処理する側が同じプロセスになるので、ちょっと面白い(強引な)こともできたりする。ただ、Railsのプロセスが落ちるとジョブキューも無くなってしまうので、Redisバックエンドの仕組みを入れておいたほうがいいだろう。

因みに、私はとあるアプリでインフラにあまり手をいれたくなかったので、girl_fridayのオンメモリで実装したことがあります。ただ、それは運用の支援ツールみたいなやつで、「だめなら再実行すればいいからさくっと作ってほしいという話だったからなので、なるべくRedisバックエンドにしましょう。

まとめ

あまりこの辺に詳しくないなら、まずはResqueを使おう。ただ、Sidekiqでも実感できる程の処理の遅さはないと思う。

DBMSがPostgreSQLならqueue_classicは試してみる価値があるよ。

girl_fridayは、仕組みが面白いから教養として使ってみるといいかも。

ちなみに、idobataではSidekiqとgirl_fridayを使っているとのこと。(なんで2つあるのかは、忘れてしまったそうな)

mysql2を 0.3.13 に上げて苦労した話

今のプロジェクトは Jenkins に bundle update した上で Pull Request させる - @kyanny's blog を参考に10弱くらいのリポジトリを週1で bundle update してるんですが、今朝のアップデートで mysql2 があがってちょっとだけはまったのでメモ。

問題が起きたのは、SELECT SUM(column_name) AS s FROM table_nameという感じでSQL的にSUMしたときなんだけど、もしかしたらSUM以外でも影響あるのかもしれない。

0.3.11では、SUMを入れると必ずBigDecimal(ただしfloatを除く)だったのが、0.3.13だと「その数値を表現するのに必要十分な型」で返すようになったような印象。

地味にDecimalのprecision: 10, scale: 0の結果が変わってるのがちょっと疑問が残るところ…

以下に例を上げておきますので「そうじゃねーよ」ということなら教えてください>< (ちなみに、Railsは3.2.11です)

以下のようなモデル(Point)があったとして、それぞれどんなかんじになるかというと

class CreatePoints < ActiveRecord::Migration def change create_table :points do |t| t.integer :i t.float :f t.decimal :d1, precision: 5, scale: 2 t.decimal :d2, precision: 10, scale: 0 t.timestamps end end end 0.3.13の結果 irb(main):001:0> Point.create(i: 1, f: 1.1, d1: 1.2222, d2: 1) (0.0ms) BEGIN SQL (0.3ms) INSERT INTO `points` (`created_at`, `d1`, `d2`, `f`, `i`, `updated_at`) VALUES ('2013-07-22 11:30:40', 1.2222, 1, 1.1, 1, '2013-07-22 11:30:40') (0.1ms) COMMIT => #<Point id: 1, i: 1, f: 1.1, d1: #<BigDecimal:7f930db7b4a0,E'0.12222E1',18(45)>, d2: 1, created_at: "2013-07-22 11:30:40", updated_at: "2013-07-22 11:30:40"> irb(main):002:0> Point.select("SUM(i) AS s").first.s Point Load (0.2ms) SELECT SUM(i) AS s FROM `points` LIMIT 1 => 1 irb(main):003:0> Point.select("SUM(f) AS s").first.s Point Load (0.2ms) SELECT SUM(f) AS s FROM `points` LIMIT 1 => 1.100000023841858 irb(main):004:0> Point.select("SUM(d1) AS s").first.s Point Load (0.2ms) SELECT SUM(d1) AS s FROM `points` LIMIT 1 => #<BigDecimal:7f930db243d0,E'0.122E1',18(18)> irb(main):005:0> Point.select("SUM(d2) AS s").first.s Point Load (0.2ms) SELECT SUM(d2) AS s FROM `points` LIMIT 1 => 1 0.3.11の結果 irb(main):001:0> Point.create(i: 1, f: 1.1, d1: 1.2222, d2: 1) (0.0ms) BEGIN SQL (0.3ms) INSERT INTO `points` (`created_at`, `d1`, `d2`, `f`, `i`, `updated_at`) VALUES ('2013-07-22 11:30:40', 1.2222, 1, 1.1, 1, '2013-07-22 11:30:40') (0.1ms) COMMIT => #<Point id: 1, i: 1, f: 1.1, d1: #<BigDecimal:7f930db7b4a0,E'0.12222E1',18(45)>, d2: 1, created_at: "2013-07-22 11:30:40", updated_at: "2013-07-22 11:30:40"> irb(main):001:0> Point.select("SUM(i) AS s").first.s Point Load (0.1ms) SELECT SUM(i) AS s FROM `points` LIMIT 1 => #<BigDecimal:7f31daaf1730,E'0.1E1',9(18)> irb(main):002:0> Point.select("SUM(f) AS s").first.s Point Load (0.3ms) SELECT SUM(f) AS s FROM `points` LIMIT 1 => 1.100000023841858 irb(main):003:0> Point.select("SUM(d1) AS s").first.s Point Load (0.2ms) SELECT SUM(d1) AS s FROM `points` LIMIT 1 => #<BigDecimal:7f31da9b7e28,E'0.122E1',18(18)> irb(main):004:0> Point.select("SUM(d2) AS s").first.s Point Load (0.3ms) SELECT SUM(d2) AS s FROM `points` LIMIT 1 => #<BigDecimal:7f31da98b440,E'0.1E1',9(18)>