console.blog(self);

技術、読んだ本、いろいろ。

2015年の振り返りと、2016年の初心

あけましておめでとうございます。今年もよろしくお願いいたします。

去年はこんなことを書いていた。

2015年の振り返り

Goodpatchに入社して、1年が経った。

CTOになった

もともとSIerに9年勤めて、マネージメント系の仕事が多くなってきて、もっとコード書きたいなーと思って転職した。2社目で大規模なサービス開発をあれこれやって、がりごりRubyJavaScript書いた。2年くらいたったら、やっぱりマネージメント系の業務を求められることが多くなってきた。

もっとコード書きたいと思って、Goodpatchに転職した。入社してみるとチームによいエンジニアが揃っているけどマネジメントする人がいなくて、もったいないなと思ってマネジメントをするようになった。そのほうがProttというプロダクトを成長させることができると思った。

Goodpatchではコードを書いた時間より、マネジメントをしていた時間が圧倒的に多かった。そういう働き方を自分で選んだ。

その後もいいエンジニアがどんどん入ってきてくれて、自分ならこんな風に作りたいなって思ったことをチームメンバーがどんどん実現してくれた。エンジニアとしての社会人経験が12年になるけど、はじめてコードを書くよりマネージメントしたほうが貢献できると感じた。

1月に入社して、3月ごろにプロダクトのエンジニアリーダーになって、12月に執行役員CTOになった。役員になることが前提で入社したわけではなく、エンジニアリーダーとして働いているなかで声を掛けてもらった。

いろいろな解釈があるけど、僕にとって執行役員というのは社員ではなく経営者だ。「JavaからRubyへ」という選択をして、その3年半後に「エンジニアから経営者へ」になった。

最初に転職するときに「はたらきたい。」を読んで「大切にしているものは、何ですか?」ということを考えた。CTOというキャリアを選択をするときも、同じことを考えた。

キーワードは「大切にしているものは、何ですか?」

新装版 ほぼ日の就職論「はたらきたい。」

僕にとっては「変化に対応していきたい」だった。ありきたりな感じだけど、考え抜いてでてきたことだった。そしてこれが僕の行動規範になっている。

ノルウェイの森」で永沢さんは行動規範についてこんなことを言っている。

人生にはそんなもの必要ないんだ。必要なものは理想ではなく行動規範だ

ノルウェイの森

永沢さんの行動規範は「紳士であることだ」と答え、紳士の定義として「自分がやりたいことをやるのではなく、やるべきことをやるのが紳士だ」と答えている。

大切にしているものや行動規範が明確になっていると、迷いにくくなる。新しいことをやるとき、こんなことが浮かんだりする。

  • 楽しいか?
  • 大変か?
  • 成長できるか?
  • 成果を出せるか?

でも自分にとって大切なことは「変化に対応していきたい」なので「変化が大きいか?」を基準に考える。この1年は、とにかく変化の大きい方を選んできた。

Goodpatchは「ハートを揺さぶるデザインで世界を前進させる」ということをビジョンに掲げている。それに共感していて、大変なことはたくさんあるけど、この先になにか楽しいことがありそうだから頑張る。

デザインと経営とエンジニアリングを繋げて、よいチーム、よいプロダクト、よい会社を作っていく。

英語

あんまり書いてこなかったけど、いちばん大きな変化は英語を使うようになったことだと思う。学生時代は一貫して英語を無視していた。使うことがなかったから。

エンジニアになって、仕様書や英語の文献はある程度読んできたけど、日常的に英語を使うことはなかった。読むことはあっても、書くこと、聞くこと、話すことはなかった。

ちょこちょこ勉強して、ものすごく苦手な状態から、なんとかコミュニケーションをとることができる、くらいの状態になった。2015年は仕事でベルリンに3週間行ったり、個人的にシリコンバレーに1週間行ったりした。

簡単な議論できるくらいまで上達したい。また勉強する。

2016年の初心

2016年に注力することを考えて、あれこれ浮かんだけど絞ってみるとこんな感じになった。

本を読んで、必ずblogに書く。

とてもシンプルになった。

Why

これまでとは違った業務に携わることになった。知識が足りないので、早急に補う必要がある。

分野だけ上げるとこんな感じ。

  • デザイン
  • プロダクト
  • 経営
  • 組織

これらの分野とエンジニアリングを組み合わせて成果を上げていくことが自分の責任だと思っている。エンジニアリングの力で、デザインと経営と組織を支えていきたい。

How

inputとoutputを増やす。知識を得て、それを使うことで経験にしていく。

What

本を読んで、必ずblogに書く。これがいちばんよい方法なのかはわからないけど、勉強したことをblogに書くことで成長してきたので、まずはこれまでに成果のでている方法を試してみる。この方法が行き詰まるくらい本を読んだら、また違ったものが見えてくるだろうし。

まとめ

今年がよい1年になるといいな、していきたいな、と思っていたら、「神様のレシピ」を思い出した。

「神様のレシピだ」日比野が表情を変えずに言った。「未来は神様のレシピで決まる

錯覚ではあったが、カカシはうなずいたかのように見えた。「神様のレシピにはとても多くの材料が並んでいて、贅沢です」

僕はそれをとてもいい響きの言葉だ、と思った

オーデュボンの祈り

エンジニアリングと、デザインと経営と組織を繋げるって、わくわくする。もちろん難しくて大変だろうけど。デザインの力をエンジニアリングで支える組織を作って、経営がそれを持続させる。とても多くの材料が並んでいて、贅沢だ。

個人的に35歳を人生の折り返し地点だと思っていて、そこまであと半年を切った。これからも変化に対応していきたい。

1年間で取り組んだProttのパフォーマンスチューニング

f:id:sadah:20151225074123p:plain

Goodpatch CTOの id:sadah です。

これはGoodpatch Advent Calendar 2015の25日目のエントリです。

Prottというサービスではパフォーマンスチューニングのために、Railsアプリケーション/フロントエンドの改善、ミドルウェアのバージョンアップ、SPDYやHTTP/2の導入、オンプレミス環境からAWSへの移行などを行ってきました。 今回はその取り組みの一部をご紹介します。

Slides

10月にGoodpatch Engineer Meetupというイベントをやって、Prottのパフォーマンスチューニングについて話した。

イベントの内容はこちらにまとまっている。

僕のスライドはこんな感じ。

"Make the Prott Faster"ってタイトルにしたけど、theはいらないよってあとから教えてもらった。cute misstakeって言われた。感覚がわからない。

パフォーマンスチューニングで取り組んだこと

Goodpatchには2015年1月に入社して、そこからいろいろなことに取り組んできた。10月の時点で2倍くらいは速くなった。いまはさらに速くなっている。でもまだまだ改善できるので、来年もパフォーマンスチューニングに取り組んでいく。 2015年10月に計測したときはこんな感じだった。

f:id:sadah:20151225074418j:plain

f:id:sadah:20151225074437j:plain

イベントのあった10月までに、こんなことをやってきた。

  • Infrastructure
    • RubyRailsのバージョンアップ
    • Nginxのチューニング
    • SPDYの導入(HAProxyからNginxへの移行)
  • Rails Application
    • N+1問題の解消(道半ば)
    • Slow Queryの解消
    • メモ化
    • Cache(Russian Doll Caching)
  • Frontend
    • AngularJSバージョンアップ
    • $watchの削減

11月から12月にかけて大きな変更をした。

  • Infrastructure
    • オンプレミス環境からAWSへの移行(計画停止しての移行)
    • MongoDB / Nginx など各種ミドルウェアのバージョンアップ
    • HTTP/2の導入

僕は主にRails側の対応を行い、フロントエンドやインフラの対応はチームメンバーがやってくれた。SPDY導入や、$watchの削減についてはこちらの資料にまとまっている。

Prottの抱えていた課題

プロジェクト一覧の画面が遅い。

f:id:sadah:20151225074858j:plain

スクリーン一覧の画面が遅い。

f:id:sadah:20151225074920j:plain

このよく使うページを中心に改善していった。

パフォーマンスチューニングの指針

よく言われていることを、ちゃんと実践するようにする。

f:id:sadah:20151225074958j:plain

  • 推測するな、計測せよ。
  • 時期尚早な最適化は諸悪の根源だ。
  • 1度に修正するのは1箇所のみ。

このあたりに詳しく書いてある。

ただこのときは、時期尚早どころか
もっと早く対応するべきだった…。

SPDY

f:id:sadah:20151225075102j:plain

SPDYの導入にあたってはこんなことをした(いまはHTTP/2を使っているけど)。

  • spdycatで接続確認する
  • h2loadで計測する

このあたりを参考にさせていただいた。

spdycatを使うと、こんなふうに接続確認ができる。

% spdycat https://prottapp.com/ -v -n
[  0.379] NPN select next protocol: the remote server offers:
          * spdy/3.1
          * http/1.1
          NPN selected the protocol: spdy/3.1
[  0.386] Handshake complete
[  0.387] recv SETTINGS frame <version=3, flags=1, length=20>
          (niv=2)
          [4(0):100]
          [7(0):2147483647]
[  0.387] recv WINDOW_UPDATE frame <version=3, flags=0, length=8>
          (stream_id=0, delta_window_size=2147418111)
[  0.387] send SYN_STREAM frame <version=3, flags=1, length=215>
          (stream_id=1, assoc_stream_id=0, pri=3)
          :host: prottapp.com
          :method: GET
          :path: /
          :scheme: https
          :version: HTTP/1.1
          accept: */*
          accept-encoding: gzip, deflate
          user-agent: spdylay/1.3.2

h2loadで負荷を掛けられる。cでクライアント数、nで回数を指定できる。出力はこんな感じ。

% h2load -c 1 -n 1 https://prottapp.com
starting benchmark...
spawning thread #0: 1 concurrent clients, 1 total requests
Protocol: TLSv1.2
Cipher: ECDHE-RSA-AES256-GCM-SHA384
Server Temp Key: ECDH P-256 256 bits
progress: 100% done

finished in 117.42ms, 8.51665 req/s, 1.29MB/s
requests: 1 total, 1 started, 1 done, 1 succeeded, 0 failed, 0 errored, 0 timeout
status codes: 1 2xx, 0 3xx, 0 4xx, 0 5xx
traffic: 158829 bytes total, 375 bytes headers, 158106 bytes data
                     min         max         mean         sd        +/- sd
time for request:    78.29ms     78.29ms     78.29ms         0us   100.00%
time for connect:    38.74ms     38.74ms     38.74ms         0us   100.00%
time to 1st byte:    90.82ms     90.82ms     90.82ms         0us   100.00%

開発環境でApache Benchとh2loadで測定したら、レスポンスタイムが10%程度改善した。あんまり厳密は計測していなくて、遅くなったりしないことだけ確認した。

MongoDBのSlowQuery

mongoidのexplainメソッドだとほしい情報が取れなかった(executionStatsを渡した結果がほしかった)ので、monogo shellでexplainした。

> db.xxxx.find({ deleted_at: null, screen_id: ObjectId('XXXXXXXXXXXX')}).sort({_id:1}).explain('executionStats')

{ "queryPlanner" : { ...
  "winningPlan" : {
          "stage" : "FETCH", ...
  },},
"executionStats" : { ...
  "executionTimeMillis" : 111,
  "totalKeysExamined" : 55555,
  "totalDocsExamined" : 55555,
} }

winningPlanの内容を確認すると、stageがFETCHになってはいるけど、sortが効いてるだけっぽい。 検索したindexの数と検索したdocumentの数が同一になってしまっているので、indexは効いていなかった。

indexを追加することで改善した。

> db.xxxx.find({ deleted_at: null, screen_id: ObjectId('XXXXXXXXXXXX')}).sort({_id:1}).explain('executionStats')

{ "queryPlanner" : { ...
  "winningPlan" : {
          "stage" : "FETCH", ...
  },},
"executionStats" : { ...
  "executionTimeMillis" : 2,
  "totalKeysExamined" : 1,
  "totalDocsExamined" : 1,
} }

Rails Application

計測するためにつかったツールとか。

使わなかったツールとか。

stackprofとstackprof-webnav

stackprof

stackprofはRubyのプロファイラ。作者はGitHubのAman Gupta(@tmm1)さん。RubyKaigi2014 3日目の基調講演のスピーカーでセッション終了後の拍手がものすごかった。

RubyKaigi2014でのスライドはこちら。

config.ruをこんな感じにするとRAILS_ROOT/tmpにダンプができる。

require ::File.expand_path('../config/environment',  __FILE__)

use StackProf::Middleware, enabled:    true,
                           mode:       :cpu,
                           raw:        true,
                           interval:   1000,
                           save_every: 5
run Rails.application

stackprofで解析するとこんな感じ。

$ stackprof tmp/stackprof-cpu-*.dump --text --limit 1
==================================
  Mode: cpu(1000)
  Samples: 780 (5.45% miss rate)
  GC: 88 (11.28%)
==================================
     TOTAL    (pct)     SAMPLES    (pct)     FRAME
        33   (4.2%)          27   (3.5%)     Psych::ScalarScanner#tokenize

stackprof-webnav

stackprofの出力をWebで確認できる。ドリルダウンとかできて便利。

$ gem install stackprof-webnav
$ stackprof-webnav -f tmp/stackprof-cpu-53222-1444801808.dump

こんな感じで表示される。

f:id:sadah:20151225075311j:plain

こんな感じでドリルダウンできる。

f:id:sadah:20151225075355j:plain

Frontend

画像の Layout / Paint が重かった。Chrome DevTools の timeline を使って計測。画像を縮小して表示している部分が重かった。

f:id:sadah:20151225075536j:plain

DevToolsのUI、カオス。そしてまたいろいろ変わっているんだろうな。Renderingタブにいろいろ設定がある。

f:id:sadah:20151225075606j:plain

いろいろ有効にすると、LayerやFPSを確認できる。

f:id:sadah:20151225075634j:plain

改善するために、試しにtransform使ってGPU使ってみた(ちょっと試してみたかった)。速くなったけど根本的な解決じゃないではないので、Nginx(ngx_small_light)で縮小したサイズの画像を生成することにした。

画像への初回リクエスト時に指定されたサイズの画像を生成しキャッシュするようにした。適切なサイズの画像を使うことで、ブラウザがかなりスムーズに動くようになった。

あとはこんなツールを使ったり、細かいJSの改善をやり続けていった。

AWS

これらの取り組みのあと、インフラをオンプレミス環境からAWSに移行した。地理的には東京からオレゴンに移行した。Prottをグローバルに使われるサービスにしていくために必要なことだった。とても難しい決断だった。

ヨーロッパでProttが遅いという報告を以前から受けていて、10月から11月にかけてベルリンに行って調査した。アメリカにサーバを置くと、かなり改善することがわかった。もちろん日本からのアクセスのレイテンシーは大きくなる。

そのため、AWS化にともないNginx / MongoDBなど主要なミドルウェアのバージョンアップを行い、さらに使うインスタンスのスペックを上げた。

その結果、日本からのアクセスのレイテンシーは増えたけどパフォーマンスは上がり、これまでよりも速くなった。ヨーロッパやアメリカでは格段に速くなった。

事前にしっかり準備することで、大きな問題も起こさずに移行を完了できた。海外のメンバーから、Prottが速くなった!というメッセージをたくさんもらえて、とてもうれしかった。

まとめ

フロントエンドの改善は id:yoshiko_pg が、がつがつ進めてくれた。インフラ周りのほとんどの作業を id:urapico が担当してくれた。他のメンバーもいろいろな改善をやってくれた。

経営的判断として、パフォーマンスチューニングを最優先課題として取り組んだ。チーム全体でパフォーマンスチューニングに
取り組む期間を作れたことはとてもよかった。

ただ、まだまだボトルネックになっているところは多いし、もっと速くしていかなくてはならない。例えばキャッシュで速くなっても初回アクセスの重さが解消できていないし、当たり前の設定を入れた感じなので
もっと攻めたい。

Goodpatchは「ハートを揺さぶるデザインで世界を前進させる」というビジョンを掲げている。サービスが遅いと
ハートを揺さぶれない。パフォーマンスは
UXに直結する。

Prottをもっとよいサービスにするために、来年もいろいろなことに取り組んでいく。

We are hiring!!

Prottを改善していくエンジニアを募集しています!興味があれば気軽に連絡ください。

RubyKaigi2015に行って、Ruby 2.3.0-preview2をインストールした

今年もRubyKaigiに行ってきた!

去年はこんな感じだった。

初日のKeynoteの最中にRuby 2.3.0-preview2がリリースされた。

というわけでさっそくインストールしてみた。

Ruby 2.3.0-preview2のインストール

開発環境はMacで、homebrewとrbenvを利用している。homebrewのインストール方法とかは割愛。まだ rbenv や ruby-build をインストールしていない場合は、こんな感じでインストールする。

brew install --HEAD ruby-build
brew install rbenv

すでにrbenvやruby-buildがインストールされている場合は、こんな感じでアップデートする。

brew update && brew upgrade --HEAD ruby-build
rbenv install 2.3.0-preview2

ruby-buildのインストールやアップデートで --HEAD のオプションを渡す必要がある。これで最新のruby-buildを利用できる。このあたりのことは rbenv/ruby-build に書いてある。

brew upgrade --HEAD ruby-build
brew install --HEAD ruby-build

続いてRuby2.3.0-preview2をインストールする。

rbenv install 2.3.0-preview2

ちゃんとインストールできたか確認する。

rbenv versions
  system
  2.2.2
* 2.2.3 (set by /Users/sada/.ruby-version)
  2.3.0-preview2

よさそう。rbenv globalで利用するRubyのバージョンを指定できる。

rbenv global 2.3.0-preview2

RailsアプリケーションのRubyバージョン変更

Prottでは .ruby-version を利用している。RAILS_ROOTにある .ruby-version のバージョンを書き換える。

2.3.0-preview2

RAILS_ROOTのRubyのバージョンを確認する。

ruby -v
ruby 2.3.0preview2 (2015-12-11 trunk 53028) [x86_64-darwin15]

いい感じ。Rubyのバージョンが変わったので、bundlerをインストールする。

gem install bundler

rbenv rehash したあとに、bundle install で gem をインストールする。

rbenv rehash
bundle install

ProttはRSpecを使っているので、テストを流して問題ないことを確認した。CHANGELOGから問題の起きそうな変更がないことを確認した。 サーバを起動して簡単な動作確認を行ってみましたが、特に問題は起きなかった。

プロダクションに投入するときにはもうちょっとしっかり確認するけど、だいたいはこんな感じ。さくっと動いてよかった。

&.(ぼっちオペレーター)や did_you_mean をちょこちょこ試した。楽しい。クリスマスに正式版がリリースされる予定なので、積極的にバージョンアップしていきたい。

まとめ

Goodpatch は RubyKaigi 2015 のゴールドスポンサー。まだ小さい会社だけど、好きな技術のカンファレンスのスポンサーになったり、こういう活動ができてうれしい。

もちろんRubyエンジニアを募集していて

そのほかの職種も募集している。

興味があったら、気軽にぽちったり、連絡ください。

RubyKaigi 2016は京都で開催される。楽しみ。