Google I/O 2017 ショートレポート

昨年よりHRをやってますtachikeiです。

ちまたのI/O熱が冷めた頃合いを狙ってHR観点でショートレポートを投下します。

次回 DeNA TechCon のネタ探しも含めて Google I/O 2017へ行きました。
プロダクトや技術的なトピはたくさんの方が書かれているので、それ以外で。

IMG_3881.jpg

持ち帰ったのは以下2点。
・Google I/Oはトッププライオリティのアジェンダ
・おもしろい技術でおもしろいモノを作る、以上

1点目は
・トップが語る
・IOにリリースをあわせる
・ムーブメントを起こす
エンジニアが好きにやってるカンファレンスじゃなくて全体がそこに向かっている印象です。

2点目は
物作りの会社。おもしろい技術でおもしろいものを作ろうと。テクノロジーとプロダクト。
エンジニアが本来やりたいのは、これだと思います。

当然利益は出さなければならないと思いますが、
ここのシンプルな点の追求こそが競争力のベースになっているように感じました。

それ以外の点としては
・物作りに集中できる環境作り
・AIの応用スピード早い
・オフィスアワーのAIニーズ高い
・ライブ配信、メディア対応手厚い

以上です。
必要なものを組織にジワジワとインストールしたいと思います。
早速、ラズパイ用のマイクロフォンを買いに行きましたとさ w

ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

golang.tokyo #6 を開催しました

こんにちは、オープンプラットフォーム事業本部の@pospomeです。
普段は GAE/Go の環境でサーバサイドエンジニアとして働いています。
(´・ω・`)

DeNA といえば、 Perl の印象が強いかもしれませんが、
AndApp というPCゲームプラットフォームの開発に GAE/Go を採用したこともあり、
AndApp以外の新規プロジェクトでも Go を採用するケースが増えてきました。

そういった経緯もあり、golang.tokyo #6 が 6/1 に DeNA にて開催されました。

当日の様子

DSCN2043.JPG DSCN2044.JPG DSCN2041.JPG

当日の資料

こちらにまとまっているので是非覗いてみて下さい。

発表に対する感想

ここからは pospome が当日の発表に対する感想をつらつらと書いていこうかと思います。

Gopher Fest 2017 参加レポート

Alias Declarations は面白い機能ですね。
「sync.Map」「環境変数の上書き」「go test の vendoring 無視」も嬉しいですね。

初めてGolangで大規模Microservicesを作り得た教訓

「非対称暗号が遅いから別サービスとして PHP で実装した」というのは
Microservices ならではの特徴ではないでしょうか。

regex, reflection が遅いというのは皆さんご存知かもしれませんが、
多用すると影響が出るくらい遅くなるケースがあるんですね。
ちなみに、PHP で実装したのは GAE/SE で利用可能で、サクっと
実装できそうだったからです。

ゲーム開発には欠かせない?!あれをシュッと見る

CSVはよく使いますよね。
こーゆーCLIツールをサラッと作れるのは Go の魅力の1つではないでしょうか。

Go Review Commentを翻訳した話

以下の記事を書いた方だったんですね。
「いいね」が100を超えたみたいです。
http://qiita.com/knsh14/items/8b73b31822c109d4c497

僕も読みましたが、Indent Error FlowInitialisms みたいな
個人の好みによってバラつきが出るものは
このようにルール化されると統一できていーですよね。

ScalaからGo

Sacla と副作用のお話でしたが、最終的に Scala の方が好きっていうw
と言いつつ、僕は個人的に DDD, クラス設計 とか好きなので、
実は Scala, Swift のように色々できる多機能な言語に魅力を感じたりします・・・。
もちろん、Go も好きですよ。

Crypto in Go

LTでセキュリティ系の話は珍しいですね。
勉強になりました。

まとめ

ということで、参加者の皆様、勉強会お疲れ様でした。
予想以上の来場者に、我々も改めて golang の人気の高さを感じました。
今回の勉強会で得られた知見を仕事や趣味で活かしていけるといいですね。

参加者並びに運営の皆様、弊社へお越しいただき、ありがとうございました。
また機会があれば弊社で開催させていただければと思っております。

ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

Selenium Conf2017 Austin に参加してきました

システム本部SWETグループのグループリーダの沖田(@okitan)です。SWET(スウェットと読む)はE2Eテストの自動化やCI/CDの整備等を通じてDeNAの事業の開発生産性と品質の向上をミッションとするチームです。

SWETチームはその前身となるMobageのオープンプラットフォームのテスト自動化とCIをミッションとしていた時代からSeleniumによるEnd to Endテストに取り組んできていました。今回、そのSeleniumの国際カンファレンスであるSelenium Conf 2017 Austinに参加してきたので、Seleniumを取り巻く最新の動向を紹介したいと思います。

Selenium Conf Austin 2017

Selenium Conf 2017 Austinは4月3日〜4月5日までの3日間開催され、約500人の参加者があったようです。初日はワークショップが開催され、2日目と3日目がカンファレンスでした。私は、日程の都合上、2日目と3日目のカンファレンスだけの参加となりました。

2017-05-23 10.01.11.png

日本からの参加者は私だけだったようなので、昼食だとかレセプションだとかで積極的にいろいろな人に話しかけたのですが、カンファレンス参加者の所属は大手企業からスタートアップまでバラエティーに富んでいて、その中でSeleniumやAppiumを使った自動テストを元にどういうようなことをやっているかというのを色んな立場の人から聞けたのはすごくいい経験になりました。

日本でも最近ではかなりDevOpsが当たり前になってきていますが、今回個人的に話を聞いた人たちの多くも当然のようにDevOpsでの開発サイクルを回していて、その中でいかに継続的かつサイクルのいろんなところにQAを分解再構築したものをいれていき、それにより品質と生産性をあげていくかといった、DevOpsQA的な取り組みを模索している最中といった感じでした。

SWETでもDeNAの事業のサイクルに寄り添い、その中でいかに品質と生産性をあげていくかに日々取り組んでいて、非常に参考になりました。

今回カンファレンスで心に残った発表も、そういったDevOpsQA的なものを目指し、CIの中でいかにSelenium特有のflaky(安定しない)なテストにチームとして立ち向かっていくかといった発表が多かったです。

Transformative Culture - The Shift From QA To Engineering Productivity

そのような発表の中で、個人的に一番よかったのは、Ashley HunsbergerさんによるTransformative Cultureでした。

ストーリーとしては、400件のテストケースのうち370件がflakyでかつ全部の実行に2時間かかるような悲惨な状況から、チームや全社的なカルチャーをいかに変えていったかといったよくある系ではあるのですが、その際の具体的な目標設定だとか、施策が非常に参考になりました。特に、作成していくテストスイートに対して、その「Goal(目的)」、「Trigger(実行タイミング)」、「Gate(失敗したときに何が起きるか)」、「Requirements(テスト実行に関わる要求)」を定義していくということがきちんと整理されていて、今携わっているプロジェクトにおいてはどうなっているだろうかとかいろいろ考えさせられました。Seleniumに関してのテクニカルなことはほぼ触れられていませんでしたが、このような自動テストを作りメンテしていく上で必要なことが一杯つまった発表だったと思います。

詳しくは、動画スライドがあがっているので、是非そちらを参照してください。

Zalenium: Use A Disposable And Flexible Selenium Grid Infrastructure

一方、Seleniumの技術的なトピックで印象に残ったのは、Diego MolinaさんとLeo Gallucciさんによる、"Zalenium: Use A Disposable And Flexible Selenium Grid Infrastructure"という発表です。

Zalenium(発表者はサレニウムと発音していました)は、Selenium Grid(CI環境でよく使われるSeleniumの複数ブラウザを並列的に動作させるための仕組み)を拡張していろいろな機能を足したもので、実はこのカンファレンス参加前からSWETグループではZaleniumの利用の検討をしていました。もっといえば、Zaleniumとほとんど同じような機能をSWET内独自に実装していて、当初はその取り組みを発表しようとSelenium ConfにCFPを送っていました(CFP送った当時はZaleniumが本当に出たてのことで全くその存在を知らなくて、CFPの時点でこれと競合していたのであればrejectもある意味納得です)。

Zaleniumがもつ機能でSWETで行いたかった取り組みは、テスト実行状況の可視化とその録画でした。というのも、特にJenkinsのようなCI環境で大量に分散したテスト実行状況をリアルタイムに確認可能にし、またその動作状況を録画しておくことにより、何かあったときのフィードバックを高速化したり、何かあったときのデバッグを非常に簡単にできます。

そのうえ、Zaleniumはリンク先の図のような仕組みにより、Selenium Gridにおけるnode(実際のブラウザが動く環境)のdockerコンテナがオートスケールします。SWETでやっているSeleniumの自動テストは、1つのテストケースに複数のブラウザを動作させることが必要となるテストが多く、そのためテスト実行時に必要なブラウザセッションの数が不定でした。nodeがオートスケールするような仕組みと我々のSeleniumの自動テストはかなり相性が良いと感じました。

なお、Zaleniumが利用しているSeleniumのdockerイメージは公式のものではなくdocker-seleniumを利用しています。このdocker-seleniumは公式のdockerイメージのgood alternativesという目的で開発されていて、例えば録画機能はこのdocker-seleniumの機能によるものです。このdocker-seleniumはつい最近まで、日本語や中国語だといったフォントが表示ができない問題があったのですが、我々が最近送ったPRにより、表示できるようになりました。以前Zaleniumを試してこの問題にあたった方も再度試していただけるといいなと思います。

Zaleniumに関しても、動画スライドがあがっているので、是非そちらもご参照ください。

まとめ

発表は基本的に、YouTubeですべて動画でみられるのですが、何よりも中の人達といろいろな話ができたのが収穫であり、刺激になりました。

例えば、Zaleniumの中の人たちは、DeNAがZaleniumを使い始めようとしていることを知っていたり、今度PRを送るねーとかやりとりをしたりしました。

また、Seleniumコミッタ陣とも、今年の7月1日に日本で行われる予定のSelenium Comitter Day 2017に関する話をしたりだとか、共著で書いたSelenium実践入門プレゼントしたりだとかのやりとりをしました。

Selenium Committer Day 2017はSeleniumのコアメンバー3人がそれぞれトークをしてくれ、同時通訳までついてます。絶賛参加募集中ですので、ぜひぜひご参加ください!DeNAもこのイベントのスポンサーをしておりますし、SWETメンバーもトークする予定となっています。

ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

実写のVRのストーリーテリングに関する調査

こんにちは。

アイドルやスポーツのライブストリーミングを行なっているSHOWROOMで、番組のVR化を進めております、エンジニアの小倉と申します。DeNAの技術開発という、メンバー個々人が研究開発をした技術をそれぞれに適合しそうなサービスに持ち込む、ちょっと変わった部署に所属しています。

今回は社内の国際学会カンファレンス支援制度という、有望な若者を世界に送り出す制度(を、おっさんが使ってしまった罪悪感を噛み締めながら)で、SXSW2017に行った報告をお届けします。

SXSW出席の経緯:

SXSW2017に出席した3/10の時点のSHOWROOM VRは単眼360度(=立体ではない360度)のライブストリーミング配信でした。単眼360度の映像では、VRグラスで見るよりもスマートフォンでみる方が快適で、ぐるっと後ろを見るとスタジオのおっさん達が映ったりして、「コレジャナイ感」「いや絶対コレジャナイよ感」がそこはかとなく漂う状況にありました。

風景主体のコンテンツは360度で配信した方が臨場感が出ますが、演者さん主体のコンテンツは注視点の方向が決まってるいわけですから、360度である必要はありません。アプリケーションのUIには番組参加のユーザーの注視点ヒートマップがあり、皮肉な事に360度である必要がないことを示しています。(※補足有り)

視野角を180度に割切る代わりに、左右の目の位置からみた映像をライブストリーミングして、スマートフォンとCardbordでリアルタイムにアイドルを立体で見られる方が、体験としてはインパクトがあります。 結果的に、この方が通信帯域を有効活用する事にもなります。

普段の生活では得られない視点から人や物を見られる事自体が、新しい体験となります。そこに4K 3D配信が入り、更に鮮明な体験が作れるようになりました。これは、「SHOWROOM VRのコンテンツをもっと進化させられないか?」、「旅行やスポーツなどへの利用を進められないか?」という問題意識を、より強く持つようになりました。これが今回SXSWに出席した理由です。

※補足※ 360度必要ないという判断は、FLAT(360度との対比で普通の四角い映像を指す言葉)の撮影を目的とした現在のスタジオの構造から来る結論であり、今後はそこも含めて変わる可能性は十分あります。実際、AKB48のオールナイトニッポン(毎週水曜25:00開催)は机の中央に360度カメラを置き、コンテンツとして360度が活かされてます。

SXSWとは:

米国テキサス州オースティンで毎年開催されるアートとテクノロジーのイベントで、1987年に音楽祭として始まって以来30年間続いています。取り扱う題材の範囲が広く、議論の切り口も斬新です。トップページの動画を見ても色々詰まり過ぎていて何のイベントかさっぱり理解できません。

2017-05-29 9.56.35.png Austin Convention Center(SXSWの中心的な場所)

2017-05-29 9.56.48.png 図:SXSWスケジュール表

スケジュール表にある通り、4つの分野「Music」「Film」「Interactive」「Comedy」が更に細分化されているのが分かると思います。VR/ARのカンファレンスは、表中央の「Convergence」欄なので、「Music」「Film」「Interactive」の合わせ技となります。それは、この期間に集中的に議論されることを意味しています。この期間だけというわけではなく、VR/ARのタグがついたカンファレンスは開始早々の3/11から「Interactive」「Film」の中にも埋もれています。

開催期間中はオースティンの街全体が会場ムードです。街のあちこちでカンファレンス、展示、デモ、パーティーが行われていて、VR/ARもその一つです。 Uberが使えないオースティンの街で往来を終日×1週間続けるのは、体力勝負の情報収集となります。また、カンファレンスには当たり外れも多いです。20分歩いて辿り着いたのに「キャパオーバーで入れない」とか、「期待外れで落胆する」ということが続いても、折れない心が必要です。

AR/VRの議論の前提「AR/VR=メディア」:

まず、「AR/VR=メディア」という捉え方は、あまり日本では意識されていないと思います。理由としては、日本のメディアは率先してAR/VRを取り上げていないことや(※NHK技研を除く)、CGを使ったVRゲームのコンテンツ開発が主流だからだと考えられます。

AR/VRがメディアとして意識されていない状況(日本におけるAR/VRの状況)を、あえて極端に図示してみました(下図参照)。この状況では、AR/VRというのは新しいもの好きな人々ための何かで、多くの人が関係するものとしては認知されにくいと考えられます。

また、海外の様にNPO・NGOでVRコンテンツ製作が活躍しているといった話を聞きません。日本のVRのオピニオンリーダーはインディーゲーム開発者とその投資家で、孤軍奮闘しているという状況です。

05-29 9.57.24.png

一方で、米国ではSXSWに限らずAR/VRはメディアとして捉えられています(下図参照)。ゲームクリエイターはもちろん、スターウォーズのようなメジャー映画のコンテンツ製作をするクリエイターや、医師、ジャーナリスト、NASAのマーケティングもオピニオンリーダーとして参加しています。AR/VRは将来的に多くの人が関わることになるものとして、大真面目な議論が行われています。

05-29 9.57.36.png

まず、この違いを認識していると、議論の範囲や、規模感を理解しやすいと思います。日本でAR/VR関連のプロジェクトの稟議が通りにくい、根本的な理由はここにあると思えます。勘が良い方は、この状況が放置されていることに危機感すら覚えると思います。というのも、未来の世代の教育レベルや国力、あるいはメディア戦に影響を与えかねない話だからです。

カンファレンスについて:

カンファレンスはパネリストによる、2人~4人のパネルディスカッションの形式が定番です。スライド資料はほとんどありません。ひたすら続く会話から重要と思われるキーワードを拾っていきます。一例としてNASAのパネルディスカッションを紹介します。

Space 360: Experience NASA Missions in VR/AR/Video

p7-05-29 9.57.54.png NASAのパネルディスカッションに参加した際のメモ

実写のVR、360度動画に関してはSXSW2014から活発に議論が行われています。当時は「この100年間で確立されたカメラワークや映像の編集方法が360度では通用しないため、模索するところから始めましょう」という論調でした。

SXSW2017においてもこの延長線上で議論がなされており、今も模索は続いています。「エクストリームな環境(例えばスキューバダイビングでサメに囲まれるものや、スカイダイビングの様な360度動画)を観ると、人は驚く」ということは確認されました。そこから長時間楽しめるコンテンツにする為に「ストーリーテリングはどうすればいいのか?」といったところに論点が来ています。

まとめ:

AR/VRに関するパネルは期間中に11セッション出ました。このうちVRのメディアとしての強みや、ストーリーテリングについてだいたいまとめると以下のようになります。

17-05-29 9.58.03.png

実写のVRの場合、「どこで情報を集めればいいのだろう?」「その場合のストーリーテリングは?」という疑問はずっと昔からありました。カメラ、ツールに関する情報はたくさんありますが、コンテンツ製作に関する情報はなかなかありません。というのも、まだ模索の段階だからです。

SXSWはそうした議論を幅広く行なっている場であることから、自分も2015年から行きたいことを表明しておりましたが、ようやく2017年に稟議が通りまして参加できた次第です。

一方、かつてのビデオ、インターネットが参考となるように、コンテンツ市場の拡大を予測する一般的な目安として、アダルト市場の動向があります。実写のVRコンテンツでも先行しているアダルト市場では、2017年から大手が次々と参入する段階に入っています(制作関係者筋談)。

どんな理由にせよ、一般層にCardbord、Gear VRなどの360度動画向きのHMDが広まることで、この後は実写のVRのコンテンツ全般に様々なチャンスが出てくると考えます。同時にコンテンツの粗製乱造のためにユーザーが離れたり、実写に少しアノテーション情報を重ねただけでAR、MRと表明するものなどが出てくるため、ARもVRもMRも名称が混乱する状況になると考えます。

TechBlogという場でTechとはあまり関係ない話をしてしまいましたが、本業はエンジニアで、GPUのコードを書いてストリーミング用の360度カメラや3Dカメラを製作したり、スマートフォン・アプリケーション側でVRの画質を向上するような仕事をしております。 機会があれば、こうした技術ノウハウも共有して参りたいと思います。

お付き合いただき、ありがとうございました。

告知:(放送は終了しました)

5/29 22:00より「ミスFLASH 2017」の4K 3Dライブストリーミング放送を行います。

mifla2017.png

必要なものはSHOWROOMアプリ(iOS版Android版)、あとはCardboard かそれに準ずるものがあれば良いです。

実際、リアルタイムで立体でアイドルを見るというのは、動画とも、また単眼の360度動画とも、体験として全然違うものになります。 番組開始時間にアプリから番組に入っていただくか、ここを踏んでいただければ始まります。まだまだ発展途上中のサービスですので、御意見をいただければ幸いです。

ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

SVVR報告会

こんにちは。根岸(@CST_negi)です。
現在新卒2年目で、VRアプリ開発の業務を行っております。
業務の傍ら、趣味でもVRアプリを開発しております。Unity/C#が好きです。

今回は、出張で3/29-3/31にサンノゼで行われたSVVR EXPOに参加してきたのでその話をします。現地のVR事情を視察しにいっただけではなく、自作のVRアプリをSVVR EXPOで展示してきましたのでその知見を含めてお話します。
渡米は初めてで英語を人に話すのも初めてな状況でしたが、様々な成果を得ることができました。

SVVR EXPOとは

SVVR(Silicon Valley Virtual Reality)というコミュニティが開催するVRイベントです。VRに関連したテーマについて講演が行われるほか、各企業や個人が制作したアプリを展示する場も設けられています。有料イベントなので、参加者が全員真剣だったのが印象的でした。
テーマに関しては、ゲームだけでなくソーシャルVRや医療系VR、Vスポーツ(VR+eSports)やWebVRなど、VRに関連したものなら幅広く取り扱っていました。

201705170001.png

展示したもの

VR本屋という仮想空間内に実際の本屋のような空間を再現しながらマンガを読めるアプリを作っていて、そのプロトタイプを展示しました。
実はDeNATechConでも同様にこのアプリを展示しまして、その時からアプリを英語対応させ、リファクタリングした上で展示に臨みました。

05-17 17.12.13.png

VR本屋は海外でもウケは良かった

体験者は期間の割に多くはないですが、「ポテンシャルはある!」「とてもいいプロダクトだね!」などの意見をいただきました。サービス化したら教えてねという話やその他ありがたい申し出などもありました。
印象としてはアジア圏(現地でお会いしたのは中国や韓国やタイ出身の方)の方からの反応が特に良かったと思います。マンガの文化が浸透しているからかもしれません。
体験後のアンケートも取ったので、それで得た意見を参考にしつつ製作を進めて行こうと思います。

海外での展示を経て知見など

2点あります。
まずは英語の話。展示というのはある程度言うことが決まっているので、話すことに関してはアピールポイントを説明できる英文をあらかじめ頭にいれておくと効果的です。一方でリスニングは聞き取れないこともあったので、自分の場合は本当にわからなかった時はノートPCでGoogle翻訳を出して「これを使ってくれませんか?」というお願いをするなどして対処しました。ちなみに、これを断る人は全くいなかったので、恥ずかしくても聞きたいという姿勢は崩さないのが良いと思います。

次に展示の話。これは日本とあまり変わらないことですが、何をやっているブースなのかちゃんと分かるようにしましょう。私の場合はデモ動画を用意して、現地の方からお借りした50インチの大画面で展示をアピールしたのが効果的でした。
大画面で「こんな事をやってるよ」というのをアピールして、それを見て立ち止まった人に「Try this?」と声をかけて、どんどん展示に引き込んでいきました。

現地のVR市場について

今回は特にソーシャルVRの領域について視察してきました。VRにおけるソーシャルプラットフォームはまだ大きなものは確立されておらず、それを獲りに行く動きが活発で多様な動きがあったのが印象的です。
Facebook社も最近ではソーシャルVRのアプリをリリースしましたが、それ以外の企業ではゲームに特化したものや、イベントに特化したものなど、それぞれ尖りが明確なサービスをリリースしています。国外のみならずソーシャルVR分野は国内でもいくつかサービスがロンチされていますので、競争は世界的に激しくなりそうです。

社内報告会での報告

帰国後、SXSWに行った小倉さんと共に社内での報告会を行いました。(SXSWの記事は後日公開されます。)
先に記した知見や、出来事などを共有しました。自分と同じように英語に不慣れな人でも、割となんとかなるということは伝えられたかなと思います。

2017-05-17 17.14.37.png

AR/VRの市場規模は2021年には現在の20倍以上に拡大すると市場予測がされており、この背景からも海外の企業では積極的に投資が行われていることを肌で感じました。また、そうした企業の目に見えた成果をSVVR EXPOで確認することができ、VR市場の拡大については私も期待を持つことが出来ました。

今回の展示では海外に飛び込んでいったからこそ新たに見えたものがありました。ここで得た知見と経験を活かしつつ、更なる発展への努力と今後のVR市場の拡大に個人としても備えていこうと思います。
ありがとうございました。

ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

ICST 2017参加報告

システム本部SWETグループの薦田(こもだ、と読む)です。SWET(スウェット、と読む)はE2Eテストの自動化を中心にDeNAの事業の開発生産性と品質の向上をミッションとするチームです。

SWETでは社外における新しいテスティング技術をウォッチし発信していくこともそのミッションの一つとなっています。そのような活動の一貫として、3月13日から17日にソフトウェアテストに関する国際会議ICST 2017に参加してきました。

ICST 2017での発表内容は大学での研究が中心でしたが、Googleやトヨタ自動車など産業界のテスト・エンジニアの参加も多かった印象です。会議期間中は発表セッションだけでなく、休憩時間や会場の通路などで、産業界からの参加者とアカデミックからの参加者が入りまじり、プログラミング教育におけるテストの位置づけの話から泥臭いテスト実装のケーススタディまで幅広いトピックについて、熱い議論が行われていました。

ICST 2017全体のスコープはソフトウェアテスト全般です。必ずしもモバイルアプリケーションやウェブアプリケーションだけがターゲットというわけではなく、車載向けシステムの検証の話やファクトリ・オートメーションの話などもあり、普段聞けないような話を聞くことができるのは、大きな魅力と感じました。

さて今回はそうしたICST 2017の発表の中でも、SWETの業務と特に関連が深いウェブアプリケーションのUI自動テストに関する「Using Semantic Similarity in Crawling-based Web Application Testing」というタイトルの、カリフォルニア大学アーバイン校Jun-Wei Lin氏の発表について紹介させていただきます。

フォーム入力自動化における実装上の課題

論文の内容について紹介する前に、弊社内でのこれまでのUI自動テスト開発の経験の中で上がっていた課題について説明させていただきます。

ウェブアプリケーションを対象にE2EのUI自動テストを書くとして、例えばログインページにメールアドレスとパスワードを入力する、という操作を自動化することを考えます。 この処理の実装としてよくあるのが、例えば以下のような実装です。

# テスト対象のログインページに移動
visit "https://test-target.com/login"
# nameが"emailAddress"であるフォームにemailを埋める
# この値は、テスト対象ページの実装依存
fill_in "emailAddress",  with: "hogehoge@fuga.com"

# nameが"passwd"であるフォームにパスワードを埋める
# この値は、テスト対象ページの実装依存
fill_in "passwd", with: "password"

# idが"btnNext"であるボタンを見つけてきて、クリックする
find('#btnNext').click

テスト対象ページのHTMLタグのidやnameの値をハードコードして、そこに特定の値を入力するという実装です。

このような実装では、テスト対象画面の入力フォームのidやnameの値をテストコード側で管理しなくてはなりません。テスト実行に必要なテスト対象固有のデータのことをテストアセットと呼びますが、テストの規模が大きくなってくるとこのテストアセットの管理が複雑になるという問題があります。

さらに悪いことに、HTML内のidやname属性の値は、テスト対象のHTMLの変更によって容易に変わってしまうものです。実際に弊社内で運用しているUI自動テストでも、HTMLの変更によってユーザから見れば全く問題がないにも関わらず、リグレッションテストが失敗することがしばしば起こります。リグレッションテストを利用するエンジニアはテストの実装者と異なる場合も多く、このようなUIテスト実行失敗の原因をシューティングすることは時間がかかり面倒な作業となっているのが現状です。

今回紹介する論文「Using Semantic Similarity for Input Topic Identification in Crawling-based Web Application Testing」では、このようなフォームの自動入力処理の実装に自然言語処理の手法を適用することで、テスト実装とテスト対象システムの実装を疎結合化し、テスト対象システム内の特定のHTML属性値に依存しないロバストなフォーム入力の自動化を実現する、という内容です。

フォーム入力自動化への自然言語処理の適用

この論文の中心となるアイディアはフォーム入力自動化を、HTMLタグをその意味ごとに分類するという機械学習の分類問題として取り扱ってみようというものです。機械学習によってテスト対象ページにあるフォームの意味が分類できれば、idやname属性の具体的な値をテスト側で知らずとも、 フォームを埋めることができます。例えば、ログインページであればページ内に存在する入力フォームを、1.ログインID、2.パスワード、3.ログインとは関係のないフォーム、の3種類に分類できれば良いといった具合です。

さてこの論文中では、フォームの実体であるHTMLを機械学習、特に自然言語処理の枠組みで扱うために少し工夫をしています。具体的には、各フォームに対応するHTMLタグを以下のように変換してしまいます。

変換前のHTMLタグ

<input type="email" id="subject-id" name="subject_id" autocomplete="on" placeholder="メールアドレス" class="txtfield w-max" value="">

変換後の単語列

["email", "subject", "id", "subject", "id", "メールアドレス", "txtfield", "w-max"]

この変換は単純に<input>タグの属性値を抜き出して単語ごとに区切っただけですが、論文中ではフォーム周辺の文字列を単語列に含めるなど、もう少し賢い変換を行っていますが本質は同じです。このような変換をかませることで、既存の自然言語処理の文書分類手法を、そっくりそのままHTMLに対して使うことができるでしょう、という点がこの論文の2つ目のアイディアです。

実験

この手法について、簡単な再現実験も行ってみましたのでその結果も報告させていただきます。

具体的には、DeNAの提供するいくつかのサービスのログインページに対して、出現する<input>タグを分類して、ログインIDを入力するフォーム、パスワードを入力するフォームを判別できるかどうかを試してみました。

詳細

グーグルで「ログイン」で検索して出てくる上位の41のサイトのログインページのHTMLから、

<input>タグを抽出し
・これらの<input>タグがログインIDなのか、パスワードなのか、ログインと関係ないタグなのかを手動でラベル付けした

ものを学習データとして利用しています。 学習データは合計218個のフォームで、ラベルの分布は

hyu01.png

のようになっています。

学習データ(mysqlのダンプ形式)

学習ログインページURLリスト

・文書のベクトル表現にはBag of Wordsを用いている

・文書ベクトルはLSI(潜在意味解析)による次元圧縮を行ったのち、ロジスティック回帰を用いてラベル推定を行っている

アルゴリズム実装にはPythonの機会学習ライブラリgensimを用いました。

学習・推定スクリプト

また、モデルの精度を評価するためのテストデータはいくつかの弊社サービスのログインページ内の10のフォームに対して検証を行っています。

hyu02.png

実験結果

検証実験の結果は以下のようになります。ログインIDに対するPrecision、Recall、パスワードに対するPrecision、Recallおよび全体のAccuracyを評価しています。

hyu03.png

データ数が小さいので確定的なことを述べるのは難しいですが、なんとなくうまくいっていそうです。再実行が簡単にできるUI自動テストというユースケースを考えると、Precisionが低いことはある程度許容できること、また本当は入力対象のフォームだったが推定時に取りこぼしてしまったケースがなかったこと(Recall 100%)を考えると実用的に利用できそうな気もしてくる結果です。

さて、誤判定をしている2つのケースですがこれはどちらも、本当はログインIDではないフォームを、ログインIDフォームとして誤判定しています。具体的にには、例えば以下のようなものでした。

<input type="email" id="register-subject-id" name="subject_id" autocomplete="on" placeholder="メールアドレス" class="txtfield w-max" value="">

このフォームタグがあるログインページは

image18392389.png

のような画面です。

このログインページには「メールアドレスでログイン」と「メールアドレスで会員登録」の2つのメールアドレス入力欄がありますが、今回の実験ではこの2つのフォームをどちらもログインID用の入力フォームと判定しています。本物のログインIDフォームの属性値と会員登録用フォームの属性値はほとんど同じであり、このケースは<input>タグの属性値のみを用いて判別するのは難しかった例と言えるかと思います。

今回の実験では簡単のため、原論文の実装とは異なり<input>タグ内部の属性直のみを用いていました。これを原論文と同じように各フォームの周辺のHTMLタグの値(ラベルの値など)を学習・推定に用いれば、このような誤判定も解決できるかもしれません。

まとめ

今回はICST 2017で発表された「Using Semantic Similarity in Crawling-based Web Application Testing」という論文について紹介させていただきました。再現実験では、とても単純な例とはいえ、UI自動テストへの機械学習適用の可能性を感じさせる結果を得ることができました。

本当にこのような手法がうまくいくのであれば、フォーム入力の自動化だけでなく自動テスト実装の様々な場面で利用することができると考えられ、より実践的なユースケースでの実験を引き続き進めていく予定です。

ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

mithril.js v1.0 の変更点

こんにちは。DeNA Games Osaka 技術編成部のさい(@sairoutine)です。
DeNA Games OsakaはDeNAの大阪拠点です。今後ともよろしくおねがいします。

2017年01月31日に、mithril.jsのv1.0がリリースされました。 (2017年5月現在、v1.1.1までリリースされています)

軽い/高速/低学習コストというmithril.js本来の特徴はそのままに、これまでのバージョンでは制約となっていた機能が大幅に変更されています。

本記事では、v1.0のリリースに当たって、大きな変更となる箇所をご紹介したいと思います。
なお、mithril.js自体の紹介については、下記の記事をご参照ください。

最速フレームワーク Mithril 入門
http://developers.mobage.jp/blog/mithril-introduction

JSX が推奨に

仮想DOMのHTML like な独自拡張構文として React には JSXがありました。同様に、mithril.js にも MSX というのがありましたが、MSX は 1.0 から非推奨になり、公式のドキュメントでも、babel と transform-react-jsx が推奨となりました。

m.deferred が廃止され、Promise が使用される

v0.2.5 までは m.deferred という Promise like な非同期処理のための関数があり、m.request 等の一部の関数は m.deferred を使用していました。これが v1.0 からはブラウザネイティブな Promise を使用するようになりました。Promise 非対応ブラウザではpolyfill を使用してくれるので、引き続き IE9 までの古いブラウザでも、mithril.js が使用できることに変わりはありません。

m.prop が廃止され stream に

v0.2.5 までは、(主に Model)クラスのプロパティの getter/setter を作成するために m.prop という関数がありました。 v1.0 からはこれが廃止され、stream という命名で別モジュールに切り出されました。

stream では今までの getter/setter 機能に加えて、stream から新しい stream を生成して、元の stream の内容の変更を新しい stream に伝播させたり、あるいは stream 同士の合体をすることができるようになりました。

// stream から新しい stream の生成
var value = stream(1)

var doubled = value.map(function(value) {
    return value * 2
})

console.log(doubled()) // 2

// stream の合体
var firstName = stream("John")
var lastName = stream("Doe")
var fullName = stream.merge([firstName, lastName]).map(function(values) {
    return values.join(" ")
})

console.log(fullName()) // "John Doe"

firstName("Mary")

console.log(fullName()) // "Mary Doe"

streamモジュールは他にも色々と出来ることがあるので、詳しくは公式ドキュメントの stream の項を参照頂ければと思います。

vnode の概念の追加

v1.0 から vnode (Virtual DOM nodes)という概念が追加されました。vnode とは仮想DOMツリーを表すオブジェクトです。コンポーネントの view 関数や、あるいは後述するライフサイクルイベントに定義された関数が mithril から呼ばれる際に、引数として渡されます。

例えば、コンポーネントに状態を持たせて、状態を参照したり変更したりしたい場合は、vnode.state に状態を追加/変更します。

var Component = {
    oninit : function(vnode) {
        vnode.state.fooga = 1
    },
    view : function(vnode) {
        return m("p", vnode.state.fooga)
    }
}

vnode オブジェクトは他にも色々なプロパティを持つので、詳しくは公式のドキュメントの vnode の項目を参照頂ければと思います。

ライフサイクルイベント

v0.2.5 までは、仮想DOMに対する config 属性で一部のライフサイクルイベント(oninit, onupdate 等)に対する処理を実装していました。v1.0 からは config が廃止され、コンポーネントに対して、以下のライフサイクルイベントで処理される関数を定義することができるようになりました。

oninit
コンポーネントが初期化される際に呼びだされるフックです。実DOMが追加されるより前に呼び出されます。

oncreate
oninit と異なり、oncreate はコンポーネントが初期化されて、実DOMが作成した後に呼び出されます。実DOMが作成した後に呼ばれるため、vnode.dom 経由で実DOMを取得して操作を行うことが可能です。

onupdate
mithril.js による再描画によって、一度生成された DOMに更新があると呼び出されます。onupdate が呼び出された際には、既に更新された実DOMが生成されているので、vnode.dom 経由で更新後の実DOMを取得したり、操作することが可能です。

onbeforeupdate
onupdate と同様に、一度生成されたDOMに更新があると呼び出されます。onupdate が、更新された実DOMが生成された後に呼ばれるのに対して、onbeforeupdate では更新された実DOMが生成される前の、仮想DOMの差分比較のタイミングで呼び出されます。この時、onbeforeupdate で定義した関数でfalse を返すことで、差分検知をスキップすることができます。

onbeforeremove
DOMが削除される前に呼ばれます。このタイミングでは、削除される実DOMはまだ削除されていないので、vnode.dom で実DOMにアクセスすることが可能です。また、onbeforeremove で定義した関数がPromise オブジェクトを返すと、mithril.js はそのPromise が完了するまで、実DOMの削除を遅延します。

onremove
DOMが削除される際に呼ばれます。onbeforeremove に関数が定義されていると、onremove は onbeforeremove が完了した後に呼び出されます。

controller の廃止

controller という概念がなくなり、今まで controller のコンストラクタで行っていたことは、コンポーネントの oninit で行うことが推奨されました。またコントローラに紐づく関数は、コンポーネントの関数として記述することが推奨となりました。

最後に

v1.0 アップデートに当たっての大きな変更点をご紹介させていただきました。その他にも細かい変更がありますので、詳細は公式の change log を参照頂ければと思います。

コンポーネントに対するライフサイクルイベントの追加や、あるいは controller の廃止により、所感としてMVC フレームワークというより、コンポーネント指向なフレームワークに近くなった印象です。

一方で、軽い/高速/低学習コストという mithril.js 本来の特徴は失われていません。 SPAを構築する上で充分かつ必要最小限なAPIに加えて、他のライブラリやビルドツールに対して低依存であることから、JSフレームワークにおけるスイスアーミーナイフのような存在です。

v1.0 にアップデートされた mithril.js にぜひ一度皆様も触れてみてください。

ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

Google Cloud Next'17 で GCP の中の人達と話してきた

こんにちは。村田 (yuichi1004) です。スマホアプリが PC でプレイできるプラットフォーム AndApp の開発に携わっています。App Engine + Go が好きです。

この度、3/8-10 にサンフランシスコで行われた Google Cloud Next'17 (Next) に参加してきました。その中で、『Next は GCP の中の人と話をする絶好の機会である』ことが分かったので、その話をしようと思います。

Nextとは

各地で開催される Google Cloud Platform (GCP) の技術発表会です。昨年までは GCP 中心のイベントでしたが、今年からは GSuite を中心としてエンタープライズ系の技術発表も兼ねたイベントとなりました。その関係もあってか、昨年の 2,000 人を大きく上回る 10,000 人規模で開催されました。

IMG_3016.JPG

我々 DeNA は、積極的に GCP を使っています(弊社のGCP導入にあたってはGoogle Cloud Platform Japan Blog の紹介記事をご確認ください)。今年は特に本腰を入れて、総勢 12 名でイベントに押しかけ、情報収集とディスカッションにあたりました。

Next は中の人と話す絶好の機会

Next の情報はすでに各種メディアで公開されていますし、各セッションの動画 は無料で公開されています。ですから、話を聞きに行く・情報を仕入れるだけならばわざわざカリフォルニアまで出向く必要はありません。Next の真髄は GCP の中の人と話せる絶好の機会 であるということです。

GCP の各セッションで登壇する人は各プロダクトを担当するプロダクトマネジャーやエンジニアになります。セッション以外にも、プロダクトの紹介ブースや、Meet The Expert というコーナーもあります。普段自分たちが使っている製品や興味のある製品を開発している人たちと直に話せるということです。

「Cloud Spanner のインスタンスって何なの?」と質問をぶつけてみました

自分は Cloud Spanner に特に興味をもっているのですが、どうしても Cloud Spanner の「インスタンス」や「ノード」が何を指しているのか分かりませんでした。そこで Cloud Spanner のブースに行ってプロダクトマネジャーとセールスエンジニアにこの疑問をぶつけてみました!

cloud-spanner-instance.JPG

担当者がその場で図を描いて事細かに説明してくれました。Google の Spanner の論文 と見比べてみると、ノードとは論文で言うところの Spanserver であり、インスタンスはノードの集合のようです。

ここで大事なことは、Cloud Spanner 上でノード数を 1 と指定すると、3 つのゾーンにそれぞれノードと書かれた箱が描かれる点です。つまり、 特定の物理マシン上で稼働するソフトウェアではなく、3 つのゾーンに分散して動作するソフトウェアスタックをノードと呼ぶ ということのようです。そのため Cloud Spanner は仮にノード数 1 で動作させたとしても 3 つのゾーンに分散して可用性を確保してくれます。

こうしたディープな話をダイレクトに聞くことができることこそが Next 最大の魅力だなと思いました。

英語が苦手、わざわざカリフォルニアまで行けないという方も多いと思います。そういう場合は Google Cloud Platform Community Slack をチェックすることをおすすめします。こちらの Slack にも、プロダクトマネジャーや開発者が目を通しています。質問すると丁寧に返事が返ってきます。

社内報告会も行いました

帰国後、DeNA 社内で Next 参加報告会を開催しました。各自現地で仕入れてきた情報や、ディスカッションを経て得られた情報を共有しました。

internal-report.jpg

GCP の注目度が上がる中、DeNA 社内でも GCP を活用したプロダクトの開発が注目されつつあります。現地のプロダクトマネジャーやエンジニアから得た情報を活かしつつ、今後のより魅力的な製品開発に活かしていきたいです。

ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

JSONデータ圧縮方式をSnappyからzstdに切り替えた事例紹介

JSONデータ圧縮方式をzstdに切り替えデータ量を38.3%削減した事例、及びマイクロサービスの無停止アップデート事例について紹介したいと思います。

はじめに

JPRゲーム事業本部開発基盤部の池田周平です。先日Rails5対応についてDeNA techブログに投稿した@namusyakaと同じチームで働いています。

JSON文字列をRDBに格納する際の圧縮フォーマットをSnappyからzstdに切り替え、データ量を削減した事例を紹介したいと思います。本対応を実施した目的はDB負荷対策です。DBで扱うデータをより小さくすることで、DBサーバのDiskI/O負荷とMaster-Slave間のレプリケーション遅延対策を目的としています。

「Sakasho」は、DeNAが持つモバイルゲームのためのプラットフォームです。複数タイトルのゲームを取り扱っており、一部データはゲーム毎の仕様差を吸収し柔軟に取り扱うため、あえてスキーマレスなJSONを採用しています。JSON文字列に圧縮を掛けRDBに保存しているのです。RDBにJSONを格納している狙いはデータ不整合を避けるためで、トランザクションを張りデータ操作しています。

Snappy vs zstd

サンプル数はそれぞれ500万件くらいで、平均6Kbyte 最大2MbyteのJSONデータに対する圧縮時のログを集計して計算しました。結果データ圧縮率はzstdが12.4062% Snappyが20.1272%でした。Snappyからzstdに切り替えた結果データ量が38.3%削減されました。またAPPサーバのCPU使用率は反映前後で変化がなくサーバ追加等の対応は発生しませんでした。

あくまでDeNAで運用しているサービスの1事例です。正確な情報はZstandard公式ページをご覧ください。

Snappyとzstdについて

zstd正式名称Zstandardは、Facebookが2016年に公開したBSDライセンスのリアルタイム圧縮アルゴリズムです。リアルタイム圧縮とはデータを高速に圧縮と解凍することに主眼を置いたアルゴリズムであることを意味しています。公式ドキュメントによるとzlibと比較して圧縮率は変わらず、圧縮速度3.9倍、解凍速度2.8倍の性能です。またトレーニング機能を有し、これはデータ毎に固有辞書を生成する機能で、より効率的なデータ圧縮を実現します。

※ 圧縮解凍速度は、Zstandard公式ページに公開されているベンチマークデータから計算しました。またSnappyはGoogleが2011年に公開した圧縮アルゴリズムです。

Sakashoとは

Sakashoは、DeNAが持つモバイルゲームのためのプラットフォームです。 モバイルゲームを開発するために必要な機能を一通り提供し、ゲームの開発の効率化を図るための共通基盤として開発・運用されています。マイクロサービス構成となっており、役割ごとに10の独立したサービスと、管理ツールによって構成されています。

sakasho.jpg

マイクロサービスの無停止アップデート

データ圧縮方式をSnappyからzstdに切り替えるにあたり、無停止でアップデートを実施するためにdeploy方法を工夫しました。

仮にマイクロサービス群に対して順にdeployを行っていくと、後半にdeployするサービスで障害が発生してしまいます。 zstd_deploy_1.png

無停止でアップデートするために、下図のようにdeployを2段階に分け、1段目でSnappyとzstdどちらでもデータをreadできる対応をdeployし、2段目でデータ圧縮方式を切り替える対応をdeployすることで複数のマイクロサービスにまたがる修正を本番反映しました。

zstd_deploy_2.png zstd_deploy_3.png

zstdライブラリの選定

各サービスは主にRubyで開発しています。zstdもRubyから扱う場合がほとんどです。gemに登録されている複数のzstdライブラリのうち、どれを選ぶべきか悩んでいたらruby expertな先輩からnative extentionでビルドしているため、メモリリークの観点で調査するべきだとアドバイスもらいました。

検証結果とコードはこちらです。


# memory_usage_zstd.rb
require 'zstd'

def current_process_memory_usage
  `ps ax -o pid,rss | grep -E "^[[:space:]]*#{Process.pid}"`.strip.split.map(&:to_i)[1]
end

JSONFILE_PATH = 'data_139k.json'
json_data = open(JSONFILE_PATH){ |io| io.to_s }
compressed_data = Zstd.new.compress(json_data)

1_000_000_000.times do |n|
  Zstd.new.decompress(compressed_data)
  puts "loop:#{n} memory usage: #{current_process_memory_usage} Kbytes" if n % 1_000_000 == 0
end

# memory_usage_zstd_ruby.rb
require 'zstd-ruby'
.....
compressed_data = Zstd.compress(json_data)

1_000_000_000.times do |n|
  Zstd.decompress(compressed_data)
  puts "loop:#{n} memory usage: #{current_process_memory_usage} Kbytes" if n % 1_000_000 == 0
end

# gem: zstd (残念ながらメモリリークした)
# https://rubygems.org/gems/zstd/

$ ruby memory_usage_zstd.rb
loop:0 memory usage: 10884 Kbytes
loop:1000000 memory usage: 43200 Kbytes
loop:2000000 memory usage: 74776 Kbytes
loop:3000000 memory usage: 106692 Kbytes
loop:4000000 memory usage: 138616 Kbytes
loop:5000000 memory usage: 169960 Kbytes
loop:6000000 memory usage: 201592 Kbytes
loop:7000000 memory usage: 233584 Kbytes
loop:8000000 memory usage: 265236 Kbytes
loop:9000000 memory usage: 297132 Kbytes
loop:10000000 memory usage: 329136 Kbytes

# gem: zstd-ruby 
# https://rubygems.org/gems/zstd-ruby

$ ruby memory_usage_zstd_ruby.rb
loop:0 memory usage: 10836 bytes
loop:1000000 memory usage: 13060 Kbytes
loop:2000000 memory usage: 13808 Kbytes
loop:3000000 memory usage: 13816 Kbytes
loop:4000000 memory usage: 13944 Kbytes
loop:5000000 memory usage: 13944 Kbytes
loop:6000000 memory usage: 13944 Kbytes
loop:7000000 memory usage: 14176 Kbytes
loop:8000000 memory usage: 14176 Kbytes
loop:9000000 memory usage: 14184 Kbytes
loop:10000000 memory usage: 14184 Kbytes

まとめ

JSONをzstd圧縮してDBに格納したら、Snappyと比較してデータ量が38.3%削減。JSON文字列と比較して87.6%データ削減できました。 Rubyでzstdフォーマットを扱う場合はzstd-rubyがオススメです。

ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る

「Sakasho」のRubyを2.4に、Railsを5にアップグレードしました

はじめに

JPRゲーム事業本部開発基盤部の@namusyakaです。

業務ではDeNAのゲームプラットフォームであるSakashoのバックエンドやインフラ周りの開発・運用をしています。

そして最近アイコンを8~9年ぶりくらいに変えました。よろしくお願いいたします。

さて本題ですが、Sakashoでは今年の2月に管理アプリケーションのRuby・Railsのバージョンの大幅なアップグレードを実施しました。この記事ではそのアップグレード対応について、一つの事例として紹介させていただければと思います。

概略

冒頭でも触れましたが、アップグレードしたのはDeNAのモバイルゲームプラットフォームであるSakashoの機能を制御するための管理アプリケーションになります。

Sakashoとは

Sakashoは、DeNAが持つモバイルゲームのためのプラットフォームです。 モバイルゲームを開発するために必要な機能を一通り提供し、ゲームの開発の効率化を図るための共通基盤として開発・運用されています。

Sakasho全体のアーキテクチャは次のようなイメージです。

sakasho architecture

今回のアップグレードは、この図の右端に位置する管理アプリケーションが対象となります。 ところで、余談ではありますが、SakashoのAPIは原則としてRailsではなくSinatraをベースに実装されています。 機会があれば、そのアプリケーションアーキテクチャについても、今後紹介できればと思います。

管理アプリケーションの特性

主に次のような特性を持ちます。

  • ruby-2.1.4/rails-4.1.1で動作している
  • ゲームプラットフォームが持つ全機能に対する制御系統を持つ。
    • ゆえに、コードベースがかなり大きい。
  • 複数DBを前提とした作りになっている。
    • 一部シャーディングもしている。
  • テストはある程度揃っている

実際の規模感をお伝えするために、rake statsの実行結果を貼っておきます。

+----------------------+--------+--------+---------+---------+-----+-------+
| Name                 |  Lines |    LOC | Classes | Methods | M/C | LOC/M |
+----------------------+--------+--------+---------+---------+-----+-------+
| Controllers          |  19075 |  15518 |     247 |    2224 |   9 |     4 |
| Helpers              |   1879 |   1657 |       0 |     225 |   0 |     5 |
| Models               |  21974 |  16928 |     581 |    1442 |   2 |     9 |
| Mailers              |      0 |      0 |       0 |       0 |   0 |     0 |
| Javascripts          |   3000 |   2763 |       0 |     620 |   0 |     2 |
| Libraries            |  10018 |   7358 |      96 |     395 |   4 |    16 |
| Tasks                |   4416 |   3132 |       2 |      15 |   7 |   206 |
| Config specs         |     23 |     20 |       0 |       0 |   0 |     0 |
| Controller specs     |  45014 |  40239 |      28 |       4 |   0 | 10057 |
| Helper specs         |    850 |    727 |       0 |       1 |   0 |   725 |
| Lib specs            |   9852 |   8384 |       1 |      10 |  10 |   836 |
| Model specs          |  17982 |  15974 |       0 |       3 |   0 |  5322 |
| Support specs        |   1038 |    943 |       2 |      13 |   6 |    70 |
| Validator specs      |    396 |    328 |       2 |       7 |   3 |    44 |
+----------------------+--------+--------+---------+---------+-----+-------+
| Total                | 135517 | 113971 |     959 |    4959 |   5 |    20 |
+----------------------+--------+--------+---------+---------+-----+-------+
  Code LOC: 47356     Test LOC: 66615     Code to Test Ratio: 1:1.4

簡単に見方を説明すると、LOCというのはLines Of Codeの略で、コードの行数を指します。 その他にも、例えばモデルと分類されるクラスが何個あるか、といった情報を読み取ることができるため、参考にしていただければ幸いです。

改修期間

対応のために掛けた期間は着手から本番リリースまで凡そ一ヶ月弱です。 大きく「開発」・「テスト」の2つのフェーズに分けることができ、それぞれにかかった期間としては次のようになります。

  • ruby-2.4.0・rails-5.0.1にアップグレードし、全てのテストケースがpassすること (一週間)
  • QAによるテスト (二週間)

開発期間中のアップグレード作業は私一人で進め、変更点のレビューなどはチームメンバー全体に依頼する形で進めました。 今思うと大分駆け足だった感はありますが、現時点で大きな障害もなく稼働しています。

なお、一応補足しますが、この記事は「テスト」ではなく「開発」のフェーズについての解説となります。

アップグレード戦略

前提となりますが、この管理アプリケーションはruby-2.1.4/rails-4.1.1で動作していました。

それらを目的のruby-2.4.0/rails-5.0.1にアップグレードするために参考にしたのが、Railsが公式に提供しているA Guide for Upgrading Ruby on Railsです。

このアップグレードガイドによると、rails-5にアップグレードするには、対象のアプリケーションはrails-4.2でなければならないようです。 したがって、目標のアップグレードを実施するには段階を踏んでアップグレードする必要があり、まずはrails-4.1.1をrails-4.2.7.1にアップグレードしなければなりません。

実際のアップグレードの流れは次のとおりです。

  • rails-4.1.1をrails-4.2.7.1までアップグレード
  • ruby-2.1.4をruby-2.4.0までアップグレード
    • ruby-2.4にバージョンをあげたらrails-4.2.7.1では動作しなかったので、railsの4-2-stableにこの時点でスイッチしました。
  • rails-5.0.1までアップグレード

一つのフェーズごとにテストが全てpassすることをゴールと設定しています。

そして、各バージョンにアップグレードする前には、可能な限りdeprecation warningsを潰すようにしました。 これは、将来的に削除される機能などを事前に回避しておくことでトラブルを防ぐ意味合いがあります。

また、この戦略を進めるにあたり、リリースブランチとは別にアップグレード専用のブランチを用意するほか、ruby-2.4でテストするための専用のjenkins jobを用意して、通常のリリースフローとは独立する形で作業を進めました。

変更点

この手の作業はbundle updateを通す作業から始まります。 幸いにして5.0.1は去年の12月リリースだったこともあって、ある程度依存しているgemは対応済みで、gemに対してパッチを当てたりforkしたり、といったことは特にする必要はありませんでした。 新しいバージョンが出たばかりのHotな時期にアップグレードするのもいいですが、数年間アップグレードされてこなかった規模の大きいアプリケーションを対象とする場合は、数ヶ月程度の時間を置いてから進めた方がハマりどころが少なくて助かるかもしれません。

次に実際の変更点について紹介します。 しかしながらあまりに数が多すぎるため、特に影響範囲が大きかった変更と、deprecation warningsなどの中から、アップグレードガイドに記載されていないものを中心にいくつか取り上げようと思います。

rails-4.2.7.1対応

元がrails-4.1.1なので、まずはUpgrading from Rails 4.1 to Rails 4.2に従って粛々と対応しました。 Upgrade guideに書いてあることは割愛するとして、主要な変更点を挙げます。

deprecations & minor changes

Primary keyでないidcolumnを持つテーブルにおいて、取得したレコードをinspectした結果、idがnilと表記される

idというカラムは存在するが、primary keyではないケースで発生する不具合です。 不具合といってもinspectの結果がおかしい程度ではあるのですが、次のようなコードで正常にidが出力されない問題がありました。

Book.find_by(id: 1).inspect #=> "#<Book id: nil, ...>"

これについてはRails側を修正することで対応しました。たまたま見つけた不具合という感じです。

Pull Request: Fix inspection behavior when the :id column is not primary key by namusyaka · Pull Request #27935 · rails/rails · GitHub

t.timestampsのデフォルトがNULLを許可からNOT NULLに変更

次のように指定することで解決しました。

create_table :books do |t|
  t.timestamps null: false
end

従来どおりの挙動にしたければnull: trueとします。 動的にtableを生成するケースなどがもしあれば、要注意といえるでしょう。

JSON.loadnilを渡さないように修正

4.2.7.1では例外が出るようになっていました。 nullが返されることを期待しているわけでもなかったので渡さないように変更しました。

関連: Fixed JSON coder when loading NULL from DB by chancancode · Pull Request #16162 · rails/rails · GitHub

MySQL-5.6以上では時刻のミリ秒を保存するようになった

MySQLにミリ秒を持たせるのは別の意味でしんどくなりそうだったので、モンキーパッチで回避しました。

参考

ActiveRecord/ActiveModelが範囲外の値に対して例外を吐くようになった

例えば、次のようなコードについて考えます。

Book.where(id: -1).first

この処理はrails-4.1と4.2で挙動が異なり、4.1ではnilが返り、4.2以降はActiveModel::RangeError: -1 is out of range for ActiveModel::Type::UnsignedInteger with limit Xといった例外が発生するようになっています。

ActiveModelにtype castingの機構が実装されたお陰だと思います。 ロジックに問題のあるコードがこれで顕在化されることになるため、これを機に既存の実装を見直しました。

ところで、この機構は明示的にlimitが指定されていない場合に、signed int(4bytes)を自動的にリミットとして定めてしまうという問題があります。

参考: Avoid RangeError without explicit limit by kamipo · Pull Request #26302 · rails/rails · GitHub

Adequate Recordの影響で動的にテーブル名をセットしているところが壊れた件

Adequate Recordはactive_record/core.rbに定義されている.find.find_by.find_by_xxx系のメソッドに対して、ActiveRecord::StatementCacheを用いてキャッシュを有効化する機能を指します。 Arelを経由したSQLへの変換を行わなくて良くなるので二倍ほど高速化が見込めるという話のようです。

この機能自体は非常に素晴らしいのですが、これを実現するために内部的に使用されているキャッシュキーはprimary keyをベースにしたものとなっており、テーブル名については一切考慮されていませんでした。 したがって、テーブル名が動的に変わった場合もprimary keyが一致すればキャッシュが効いてしまうので、誤ったSQLを発行してしまうリスクが存在しています。

無論そんな使い方をするなという話もあるかもしれませんが、動きそうなところが動かないのは困るということで、Railsを修正しました。 直し方としてはtable_name=が実行されたらキャッシュをリセットするように修正する、というものです。

Pull Request: Make table_name= reset current statement cache by namusyaka · Pull Request #27953 · rails/rails · GitHub

ちなみにこれを回避するには、Adequate Recordが効かないQueryMethodsを使ってやるだけで良いので、次のように書き換えるのが簡単です。

Book.find_by(id: 1) # before

Book.where(id: 1).first # after

ruby-2.4.0対応

rails-4.2.7.1の対応を終えた後に、ruby-2.1.4からruby-2.4.0にアップグレードしたところ全く動きませんでした。 どうやら4.2.7.1はruby-2.4.0に対応していないようで、ruby-2.4にアップグレードする前に一旦4-2-stableを使うように変更しました。

なお、私が対応していた時点では4.2系のバージョンの中では4.2.7.1が最新版でしたが、つい先日4.2.8がリリースされ、公式にruby-2.4がサポートされています。今後アップグレードする際には4.2.8を使用することをオススメします。

さて、この項ではruby-2.1.4からruby-2.4.0にアップグレードしたことで発生した主要な問題点を挙げてみます。といっても、Railsほど苦労はなかった印象です。

FixnumIntegerに統合

軽く書いてはいますが、native extension系のgemは要注意です。 以下は具体例です。

アップグレードする対象のアプリケーションが依存するgemのruby-2.4 対応状況については事前に洗い出しておいた方が良さそうです。 必要であればパッチを送り、新バージョンのリリースをねだりましょう。

サブクラスの抽出に自前のObjectSpace#each_objectではなくActiveSupportのコア拡張であるClass#subclassesを使うように変更

ruby-2.3.0からObjectSpace#each_objectがシングルトンクラスを含むようになりました。 SakashoではClass#subclassesの自前実装をObjectSpace#each_objectをベースに持っていて、そちらを修正しようとも考えたのですが、ActiveSupportのコア拡張でほぼ同じことをやっていたので、そちらを使うようにして修正しました。

参考

openssl系の標準添付ライブラリにて、keyやivの文字数がvalidでないと例外が発生するようになった

もともとの実装では、文字数がオーバーしている場合は丸められていたようです。 したがって、事前に文字列を適切にsliceするように変更して修正しました。

参考: openssl: make Cipher#key= and #iv= reject too long values · ruby/ruby@ce63526 · GitHub

rails-5.0.1対応

rails-5.0.1にアップグレードする際にも、まずはUpgrade guideに沿って進めました。

5へのアップグレードには設定ファイルの追加やinitializerの追加などを含むため、そのあたりを中心に対応を自動化するための機構として、app:updateというRakeタスクが提供されています。 対話的に大きな差分を自プロダクトに反映していくことになりますが、ある程度既存のコードにも影響のある部分なので、しっかり差分を注視しながら進めた方が良さそうです。

このフェーズについても、Upgrade guideの内容は割愛し、主要な変更点を挙げていこうと思います。

deprecations & minor changes

ActionController::ParametersがHashを継承しなくなった

多くの場所で取り上げられている変更点ですが、例に漏れず対応が困難だったもののうちの一つです。 Hashが持つメソッドや振る舞いに依存しているコードの多くが正常に動作しなくなるため、アップグレードする際には事前にその辺を洗い出しておくと楽かもしれません。

参考: Make AC::Parameters not inherited from Hash by sikachu · Pull Request #20868 · rails/rails · GitHub

datetime_selectに入力された値をDateTimeとして取得する際に、受け取った値をそのままhidden_fieldtext_fieldに渡すとRubyのHashをinspectした値がvalueに埋め込まれてしまう件

例えば次のような要素があるとします。

= f.datetime_select :opened_at, use_month_numbers: true, start_year: default_start_year

このヘルパーは、日付にかかる情報を扱うために複数のselect要素をレンダリングします。 そして、opened_at(1i)opened_at(2i)...といった複数のキーからなるopened_atにかかる情報をサーバに送信し、それらのパラメータをActiveRecord::Baseインスタンスに渡してやることで、よしなにopened_atに時刻情報が代入されるという作りになっています。

これはもとからRailsが持つ機能ではありますが、Sakashoでは、更にこのsubmitされた値を確認画面などでhidden_fieldに埋め込むケースが非常に多いです。

サンプルとしては、次のようなコードになります。

= f.hidden_field :opened_at

form_forなどに代表されるこれらのform methodは、第一引数で受け取ったカラム名を用いて、対象となるインスタンスに対して実行し、その結果をvalue属性に代入します。 今回はこの代入される値に問題がありました。次のようになります。

<input type="hidden" value="{1=&gt;2017, 2=&gt;2, 3=&gt;27, 4=&gt;21, 5=&gt;0, 6=&gt;0}" name="archive[opened_at]" id="archive_opened_at" />

これは、内部的に実行されるxxx_before_type_castという、その値をキャストする前の値を返す機構に起因します。 今回のケースでは、opened_atにはもともとdatetime_selectによって生成された複数のselect要素群から作られたHashが代入されています。 当然、validationを通過した結果の確認画面ではそのHashがopened_atの値として埋め込まれており、確認画面を経て、いざ作成しようとした際にエラーが発生して保存できない、といった問題が発生することになります。

この問題についてはPull Requestを投げてはいるものの、まだレビュー待ちという状態です。

反省点

今回は短期間で一気にアップグレードを実施しましたが、本来であれば計画的に実施すべきだと思います。 Railsを使ってアプリケーションを作って終わりではなく、定期的に依存するgemのアップグレードに追従しようとする姿勢こそがRailsと付き合っていくコツといえるでしょう。 そこでSakashoでは、bundle updateを自動で行い、アップグレードされたgemの差分を表示しつつ、そのGemfile.lockの変更差分をコミットするPull Requestを自動で作成する機構を導入しました。

これにより、普段の業務でも自分たちが採用しているossのリリースを考慮して取り組めるようになると考えています。

おわりに

大規模なRailsアプリケーションで、かつ長らくアップグレードされてこなかったプロダクトを最新バージョンにアップグレードするのは非常に骨の折れる作業でしたが、短期間で集中的に取り組めたことは良い経験となりました。

また、管理アプリケーションとしての特性上複数DBを前提としているなど、一般的なRailsアプリケーションのそれとは異なる構成であり、それをきっかけとして発見したRailsのいくつかの不具合を修正することができました。

このような環境下で運用されるRailsアプリケーションはそう多くはない認識ですし、Sakashoのような環境でしか発生しない不具合はまだ存在するはずです。Railsを利用する立場として、そういった不具合に積極的に対処してコミュニティに還元していく姿勢が今後重要であると考えています。

最後になりますが、Railsを採用される際には、用法用量を守って正しくお使いください。

ツイート
シェア
あとで読む
ブックマーク
送る
メールで送る