qsonaのブログ

プログラマーです。

「Node.jsでのゲームサーバ開発 愛すべきバッドノウハウ3選」の補足

本エントリは、東京Node学園祭2015でセッション発表いたしました「Node.jsでのゲームサーバ開発 愛すべきバッドノウハウ3選」についての補足記事です。

スライドはこちら:

speakerdeck.com

発表動画も上げて頂いています:

youtu.be

発表の概要

発表内容は概ね以下のようなものです。

  • 動機付け: Node.jsをサーバサイドでもっと普通に利用したい, その上での
  • バッドノウハウ1. (ES5まででの)非同期フロー制御
  • ノウハウ. マスターデータの利用
  • バッドノウハウ2. "サービス層" になんでも記述する行為
  • バッドノウハウ2.5. Dateを上書きする
  • バッドノウハウ3. process.uncaughtExceptionをlistenしてエラーを握りつぶす

現場の規模感

自分が約1年間開発に携わったプロジェクトでは、サーバサイド開発者が平均5人程度で、大体がNode.js未経験からスタートしています。普通のWebアプリケーションのサーバサイドの開発と言ってよいと思います。コードが約10万行くらい(テスト除く)。「フツウ」のサーバ開発、と形容しているのはこれをイメージしています。

BAD 1. 非同期フロー制御について

発表中、最も自分が言いたかったことを言い切っている章ですが、反応としては「yieldで解決するのでは」というものが多かったです。Node v4でのgeneratorsがサポートされ、そしてasync/awaitの文法が今後入ることがほぼ確定しており、それらを含んだ話ができずにスライドの中で「決定版が待たれる」のような曖昧な言い方で濁したのは、自分の怠慢だったと言わざるを得ません。

1年ほど前にcoを調査したときは、callbackを取る関数はthunkifyして使うものだと思っていましたが、最近見たらthunkifyはdeprecatedになり、Promisifyして使うべき、となっていました。Promiseが標準化したため、正しい選択といえますが、気分的にはthunkに比べてPromise経由はどうも遠回りに思えてしまいます。「フツウ」を標榜しているところが気にするべきことではないですが、オーバーヘッドはどうしてもthunk以上に気になるので、実コードを一部移行して検証してみようと思っています。

それと気になっているのが、非同期を同期スタイルにすることで、callbackスタイルからtry-catchのエラーハンドリングになることです。

1つは、V8のoptimizationは気にする必要がないレベルなのかどうか(場合によってはtry節を全部即時関数で囲ったほうがいいのか)ということ。

もう1つは、非同期エラーも、主にバグで出される同期エラーもいっしょくたにcatchされてしまうことになると思うので、その辺を区別する(上手な)方法はあるのか?という疑問があります。ここに関してはそもそも区別する必要がないんじゃないかという話も頂いていて、実質的にはユーザのアクセスによって到達するようなコードなのでそう問題にならないはずですが、とはいえdomainがdeprecatedになった理由なども考えると、あるべき論としては、この2つは分けて考えなければいけないのかなと思っています。

諸々検証して、中規模以上の開発で使えるプラクティスを構築できればと思います。

最後に現場での話をすると、io.jsは利用しておらず、Node v4への移行は大体出来る状態まで持ってこれた(ただし文法の移行はまだ)という状態です。varをconstやletに置き換えるなどは出来るでしょうが、asyncを利用した既存のコードが多い状態で、generatorsを利用した記法と共存させるのは規模感的にも難しいかなというイメージですが、うまく共有または移行をしていきたいと考えています。

BAD 2.5. Dateを上書きする

自作ライブラリ qsona/time-master · GitHub を紹介しましたが、 sinonjs/sinon · GitHub のFakeTimerを使うべきではないか、という指摘を懇親会で頂きました。自分もsinonをテスト上で使っていますが、このような用途で使うことは全く考えもついていなかったし、テストでFakeTimerを使ったことがなかった(ユーザのアクセス時間をアプリケーション中で使っているので、基本的にそれを書き換えていた)ので、調べてみました。

sinonのFakeTimerは、ある決まった時刻で固定する用途として使えるようです。プレゼン中では話せていませんでしたが、我々の(テスト以外の動作確認の)用途としては、ある時刻に移動させたあと、そのまま時間が流れていって欲しく、そのような機能はFakeTimerにはないようでした。

最後に

この記事は随時追記します。間違いや疑問点等あれば、ご遠慮なくコメント欄でご指摘いただければ幸いです。