体験を伝える―『ガジェット通信』の考え方

面白いものを探しにいこう 本物を体験し体感しよう 会いたい人に会いに行こう 見たことのないものを見に行こう そしてそれをやわらかくみんなに伝えよう [→ガジェ通についてもっと詳しく] [→ガジェット通信フロアについて]

はてな粕谷氏が語る、安全なPlay Frameworkのバージョンアップのコツとは─by Scala福岡2017

なぜ、バージョンアップが必要なのか

ご存知の方も多いと思うが、サーバ管理・監視ツール「Mackerel」を直訳すると、「サバ」という意味。それをもじって名付けた。現在、粕谷氏は同ツール開発チームディレクターとして、開発メンバーをマネジメントしている。

株式会社はてな Mackerel開発チームディレクター 粕谷 大輔氏

現在、MackerelはPlay2.5.12で動作しているが、リリース時点ではバージョン2.3.1を採用していた。その後、2.3.7までマイナーアップデートは大きな問題もなく追随してきた。

しかし2.4のバージョンアップでは、成功したものの課題が残った。初期のバージョンアップや開発の様子について知りたい人は、ぜひ、ScalaMasturi2014で辻川貴哉氏が発表したスライド「Scala useCases at Hatena」を見てほしい。ちなみに辻川氏は、Mackerelのコードの1行目を書いた人物である。

なぜ、Pray Frameworkを2.4にする必要性があったのか。例えば粕谷氏が前職で担当していたソーシャルゲームなどであれば、長期間運用される予定がないソフトウェアに関しては、バージョンを固定化して安定運用を図ればよいと考える。

しかしMackerelはBtoBビジネス向けのサービス。10年後も当たり前のように動いていなければならない。2027年にMackerelが10年前の技術で動いていては、パッチが当たらなくなることで脆弱になったりするなど、サーバーサービスとして継続するリスクもある。

さらにPray2.3のWS(HTTPクライアント)に入っているデフォルト認証局情報が古くなり、Webhook通知の処理などに支障が出始めていた。WSだけを新しくするという方法もあるが、Mackerelはフルスタックなフレームワーク。一部のライブラリだけを載せ替えるより、全体をアップデートした方がよいと考えた。

Slickのアップデートに苦戦

これらの理由により、Play2.3から2.4へのアップデートをすることになったが、非常に大変だったという。まずコネクションプールをBooneCPからHikariCPに置き換えるには、細かい設定などのパラメータチューニングをやり直す必要があるからだ。

次に、Slick2.x→Silick3.xへのアップデート。DBIOという非同期ベースなクエリになる。特に大変だったのがSlick3.xへの移行である。Slick3.xは前述したように非同期がベース。MackerelはSlick2.xでブロッキングなクエリ処理をしていた。

それを非同期なものに置き換えていく必要がある。MackerelのSQLは数えたくないほどの数。それを普段の機能開発をしながら、すべて非同期の処理に置き換えていくのは難しい

なぜならそれは、Mackerelのサービスの提供ポリシーにかかわってくるからだ。Mackerelは毎週新機能をリリースすることにこだわっている。

定期リリースは火曜日と木曜日。年末年始とゴールデンウィーク、夏季休暇期間を除いて、156週連続リリース継続している。つまりこの毎週リリースを継続しながらフレームワークを更新することがミッションとなる。

まずは、Slickのアップデートに取り組むことになった。毎週リリースしながら、DBIOの置き換えができるのか。毎週の機能開発とコンフリクトしないようにするのは、どう考えても無理なのではないか。これがネックとなり、たびたびPlay2.4化にチャレンジするが、撤退を繰り返していたのである。

そんなところに救世主が登場した。それがビズリーチの竹添直樹氏である。同氏が運営する「たけぞう瀕死ブログ」で、「blocking-slick」というライブラリを公開していたのである。

これを使うと、Slick3.xでもSlick2.xでもBlockingAPIが使える。implicitでBlockingAPIを生やせば、既存のクエリ処理は無傷でそのまま使える。コンフリクトが起きないのではと希望が持てた。

一旦これでPlay2.4にしてしまい、少しずつDBIOに置き換えていくという方法を採用しようと考えた。

blocking-slickは最初、完全に要件を満たせなかった。しかしblocking-slickにプルリクエストを出すなどで、Mackerelへの適用を成功させた(いざとなるとfolkしてでも適用させるぞと強い意思を持っていたのだそう)。

今は違う会社で働いている当時のメンバーが、プルリクエストに出して、blocking-slickに対応してもらったのである。それによって一番ネックだったSlickの問題はなんとか解決できたというわけだ。

2.4への移行はカナリアリリースが裏目に

次は大本命のPlay2.4への移行である。フレームワークの書き換えは非常に大きな作業となる。どのような作戦で行ったのか。まずは2.3のまま進められる移行は、火・木の定期リリースで少しずつ入れていくことにした。

そして、とうとう小分けできない大規模なプルリクエストのマージするときがきた。レビューはとにかくエンジニア全員(6人)で歯を食いしばって行うこととなった。通常の定期リリースに入れるプルリクエストは、誰か一人が問題なしという判断でマージすることにしている。

一方、Play2.4のプルリクエストはエンジニア全員が問題なしという判断でマージをした。しかしコンパイル言語は、コンパイルとCIが通っていればだいたい動くという安心感がある。外部APIを叩くところなどはモックなので、そういった環境依存な処理は丁寧に動作確認を行った。

レビューしてマージしてもよさそうだということになり、リリースを翌火曜日の定期リリース日に実施することに決めた。そこで木曜日の定期リリース後にマージし、木、金、土、日、月の5日間、ステージング環境で動作させてその期間のメトリックをチェックした。

問題点を切り替えるために、通常定期リリース向けのプルリクエストはその日にはマージせず、Play2.4のプルリクエストだけリリースすることにした。

Play2.4への移行はカナリアリリースで実施。というのもMackerelが動いている複数台のサーバのうち、一部分だけPlay2.4にして、混在した状態で様子を見るためだ。

元々Mackerelはローリングデプロイで毎回リリースしているため、新旧が混在しても動くことを想定して設計されている。ダメだったら先行してリリースしたサーバ群をロールバックすればよいと考えていたからだ。

リリースしたところ、何かがおかしいことに気付いた。どうやら暗号化されたデータを復号している処理で確率的に失敗しているように見えるのだ。ロールバックした方がいいかという判断をしようとしていたとき、Play2.4のマイグレーションガイドに次の様なことが書かれていることを発見したのである。

「Play 2.4 uses a new encryption format, but it can read data encrypted by earlier versions of Play. However,earlier versions of Play will not be able to read data encrypted by Play 2.4」

「Play 2.4は新しい暗号化形式を使用しますが、以前のバージョンのPlayで暗号化されたデータを読み取ることができます。ただし、以前のバージョンのPlayでは、Play2.4で暗号化されたデータを読み取ることはできません」

つまり、カナリアリリースが完全に裏目に出たのである。ロールバックしたらさらにおかしくなるので、やりきるしかない。

検証環境で検証もしており、パラメータチューニングもしていたので、ほかに怪しい状況はなかった。全台反映を決断し、こうして無事Play2.4への移行を完了した。

2.4への移行で学んだことを糧に、2.5へはスムーズに移行

2.4への移行は成功はしたものの、課題が残る結果になった。どんな課題が残ったのか、移行振り返り会の議事録の一部を紹介する。

わかったこと

厚いフレームワークのマイグレーションが大変
ライブラリ同士が依存してロックインがつらい。またバージョンアップで別ライブラリも上げないとならないとかあって大変だった
それぞれマイグレーションガイド読む感じだと、見落としが発生しそう。
マイグレーションガイドをさらっと読むだけではなく、チェックリストを作ったり読み合わせをすればよかった。
コンパイル言語だったからこそ、マイグレーションできたところがある。

これらを踏まえて、Play2.5移行時にやることもその時の議事録に記載されている。
マイグレーションガイドを複数人同時に読み合わせをする。
マイグレーションガイドを元にチェックリストをつくり、全員レビューはそれを見ながら実施する
ステージング環境で、カナリアリリースとロールバックのテストをする(混在環境での動作、およびロールバックしても問題なく動くかをチェックする)

そしていよいよPlay2.5への移行のときがやってきた。

今回はSlickのバージョンアップはなかったので、ファイルチェンジは64個。とにかく同じ轍を踏まないように、マイグレーションガイドを入念にチェック。そして移行作戦会議や手順を共有をした。そして検証環境できちんと試すなど入念に準備をしたので、問題なく移行が完了した。

2.5にバージョンアップしたことで恩恵をうけたことがある。まずはコンパイルが速くなったことだ。次にPlay自体のパフォーマンスが改善したことで、アプリケーションサーバのCPU使用率が15%ほど削減できたことだ。

今後の展望としては粕谷氏は、「2.5でdeprecatedになったものを潰して、2.6への移行準備を進めていきたい。重厚なフレームワークへの依存度を下げ、ライブラリ単位で気軽にバージョンアップできるようにするのも1つの方法かもしれない」と語った。

「Scala福岡2017」レポート特集

Scala/Spark/Mahoutでレコメンドエンジンを作る─by Scala福岡2017
Direct Manipulationとプログラミング環境をScalaで書いてみる

CodeIQ MAGAZINEの記事一覧をみる
  • 誤字を発見した方はこちらからご連絡ください。
  • ガジェット通信編集部への情報提供はこちらから
  • 記事内の筆者見解は明示のない限りガジェット通信を代表するものではありません。