読者です 読者をやめる 読者になる 読者になる

nodeをv0.8系からv4系にアップデートした話

リクルートテクノロジー社で自社サービスをv0.8からv4系にアップデートしたという話を目にしたのだが、偶然うちも同じようにv0.8からv4にアップデートしている。当時のメモが出てきたのでここに記しておく。なお、すでに5系が出ていたが新規開発ではないし人的リソースも足りないので、LTSで妥協した。

アップデート後にメモリ消費が半分、ロードアベレージが1/3ぐらいに減少した。しかしレスポンスタイムは全く改善せず。まあ、WebアプリというやつはDBアクセスが律速段階なので仕方ないだろう。

バージョンアップで出現した問題は以下のとおり。修正はほぼ1日。もともとのコードがモダンに書かれており、テストコードがあったことも幸いした。

  • 管理ツールLDAPログインできない
  • ステージング環境でAPIリクエストが失敗する
  • 発生源不明のuncaughtException
  • ビルドサーバでnam installがエラー
  • process.nextTickをsetImmediateに置き換えたら動かなくなった
  • 外部リソースへのリクエスト(Webdavへの保存)が発生しない

管理ツールにログインできない

  • node-LDAPというモジュールがv4に対応していなかったため
  • ldapjsというモジュールに変更して解決
    • 後から知ったがnode-LDAPldap-clientという名前に変わっており、そちらはv4でも動いたらしい

テストサーバでAPIリクエストが失敗する

  • テストのAPIサーバがオレオレ証明書を使っていたため接続を拒否していた(v0.12でセキュリティの強化がされたらしい)
  • テスト環境のみrejectUnauthorized: falseオプションを付与してオレオレを許可するようにした
    • オレオレは微妙だが外部へのアクセスが制限されているサーバなので、仮にDNSが汚染されても問題ないであろう…
var req = http.request({
    protocol: 'https:',
    host: 'api.endpoint.example.com',
    port: 443,
    path: '/',
    method: 'GET',
    rejectUnauthorized: false
}, (res) => {
    console.log('status:', res.statusCode);
}

発生源不明のuncaughtException

  • HTTP APIの呼び出しでrequest#setTimeoutにundefinedが渡っておりエラー、v0.8系ではエラーにならず無視されていた。
  • バグなので修正、想定の5000msを渡すように変更

ビルドサーバでnam installがエラー

  • v8がC++11依存になっており、CentOS 6のGCCが古いためv8.hを読み込むネイティブモジュールがビルドできなかった
  • GCCをアップデートして対応

process.nextTickをsetImmediateに置き換えたら動かなくなった

  • バッファ処理が完了する前に、次のIOイベント(on data)が呼ばれてバッファ内容が想定外の状態になって正しく動作しなかった。nextTickはIOイベント前に呼ばれるので問題なかった。
  • バグなのでコードを直して対応

APIリクエストが発生しない

  • 以下のコードが動かなくなった
client.request(apiEndpointData, (res) => {
   // 何もしないのでコメント
   // res.on('data', (chunk) => {});

   res.on('end', () => {
      callback();   // このコールバックで次のAPIをコール 
   });
});
  • responseのendイベントは v0.8はTCP FINを受けた時点で発生する、v4はdataイベントで取得できるデータが無くなったら発生、つまりdataイベントを呼ばないと発生しなくなった
  • endイベントを待たずにcallbackを呼ぶように修正