Yearly Node.js 2018

Node.js 2018 まとめ

この記事は HTML5j カンファレンスで発表した、 Node.js 2018 のまとめの話をブログに起こしたものです。

speakerdeck.com

ちょっとずるいですが、この記事一つで Node.js アドベントカレンダーJavaScript アドベントカレンダーの25日目の記事です。

10月にNode.js v11 がリリース

Node.js v11 は変更点はいくつかありますが、v11.0.0ではそんなに大きな機能はありません。代わりに性能向上と安定性向上を行っています。

これにはNode.jsのコア変更ポリシーが関わっています。

Node.js Core Policyである 「Less is More」

Less is More という言葉をNode.js の文脈で最初に使ったのはこの jsconf での発表が初めてですね。

www.youtube.com

要は「豊富な機能を追加するのではなく、最小限の機能でコアは小さくシンプルにする」という話です。 もともとは建築家の言葉で、「これ以上ないことは豊かなことである」とも訳されます。

さて、この発表の中でも大きな機能が少ない代わりに、フォーカスすることとして、安定性・性能・セキュリティを向上させることについて触れています。

今回はNode.js が2018年の中で、どういう形でこれらの非機能要件とも言える情報について改善を重ねてきたかを解説します。

安定性

安定性と一口に言ってもいろいろあるのでここでは、Node.jsが安定性を得るためにやってることを紹介します。

LTS

Node.js は長期間サポート(LTS)があります。

https://github.com/nodejs/Release/raw/master/schedule.png

このサポートはLTS対象のバージョンであれば2年間のパッチリリースが確約され、セキュリティのアップデートは3年間受けられるというものです。

今だとv6.xが4月までセキュリティアップデート期間、v8.xが4月までバグ修正のアップデート期間です。v10.xは予定では2020年の10月まで受けられます。

V8 の ABI 互換性サポート

Node.js の内部のJavaScriptエンジンであるV8はNode.jsに対しての非互換の修正があるときには事前の通知がされるようになっています。 また、V8自身はアップデートがある度に、Node.jsに対して最新のmasterを追加し、テストが落ちないかを定期的にモニタリングしています。

github.com

Jenkins によるCI実行

Node.jsでは、PR毎に各種CPU, OSのビルドをJenkins上でCIを回すことで壊れるかどうかを確認しています。

jenkins-node-ci
jenkins-ci

ただ、Node.jsのテストも膨大な数があるので、testが実行されると中には PASS したり、 FAIL したりする flaky なテストがあります。これらについてはレポートされる仕組みがあり、その中で詳細な調査を行いながら修正されていきます

flaky test
flaky-test

つい最近 flaky なテストが全部PASSして、グリーンになった!ということでコアメンバーが喜んでました。

(ただこのツイートにもあったとおり、明日にはすぐにイエローになってしまいましたが。)

アプリケーションを安定化させる試み

大きな機能追加はないですが、アプリケーション内でCPU負荷が高いときのinspectorや非同期処理実行時にtraceする async_hook といった機能が追加、改善されています。

Inspector | Node.js v11.5.0 Documentation

Async Hooks | Node.js v11.5.0 Documentation

これらの機能はnode.jsの内部状態をもとに解析するツールです。CPUのプロファイラはV8の内部プロファイラの機能を利用して状況を把握するためのツールです。こういった機能が増えることで、安定性を改善しています。

const inspector = require('inspector');
const fs = require('fs');
const session = new inspector.Session();
session.connect();

session.post('Profiler.enable', () => {
  session.post('Profiler.start', () => {
    // invoke business logic under measurement here...

    // some time later...
    session.post('Profiler.stop', (err, { profile }) => {
      // write profile to disk, upload, etc.
      if (!err) {
        fs.writeFileSync('./profile.cpuprofile', JSON.stringify(profile));
      }
    });
  });
});

性能

Node.js においては性能も重要な指標の一つです。これも維持するために色々やっています。

ベンチマークを常に測る

benchmarking グループというワーキンググループがNode.jsのパフォーマンスを常に計測しています。

benchmark
benchmark

これを見ながら大きなregressionが起きていないかとか、バージョン間で差を見ることもできます。基本的にV8がパフォーマンスを向上させているので、省メモリになっていたり、高速になっています。

また、マイクロベンチマークだけではなく、現実のアプリケーションでもどれくらい下がっているかを計測するためにExpressを使った航空機予約システムである、acmeair という模擬システムでも評価しています。

GitHub - acmeair/acmeair-nodejs: A Node.js implementation of the Acme Air Sample Application. With datastore support of MongoDB, Cloudant, Cassandra. With runtime support of Bluemix/CloudFoundry, Docker... With Micro-Services.

Worker

Node.js の昨今の利用例として、ネットワークサーバだけではなく、babel, webpackといったフロントエンドのツールとして使われることが多いです。この様な時には大量のファイルを変換したり、文字列連結をしたりするので、IOの時間よりもCPUの時間が支配的になります。結果としてマルチスレッド・マルチプロセスでの処理の方が効率的にCPUが利用できます。

Node.js: The Road to Workers

Turbo Boost Next Node.js - Speaker Deck

実際に筆者もbabelを使ってmulti-threadとmulti-process、シンプルに一つのプロセスを使ったもので比較してみました。筆者の計測結果を以下に載せます。

worker result
worker result

これを見ると、ファイル数が100以上であればマルチプロセスよりもマルチスレッドのほうが高速になるという結果が出ました。プロセスを起動するよりもスレッドを起動するほうがコスト的に若干安いので、こういう結果になりますが、まだSharedArrayBufferは利用していないので、メモリ共有をしだすとどうなるかはまだ考察していません。

ちなみに最近入った llhttp というパーサがやばい

最近入った Fedor Indutny 製の HTTP Parser ですね。

github.com

これ、えげつないっす。

HTTP Parser はこれまで C で書かれた http_parser が使われてました。 しかしながら、 Cのhttp_parserは中身を見るとメンテナンスしやすいとは言えず、またアクティブなメンテナもいなかったので徐々にブラックボックス化していました。

Fedor の作った llhttp は「JavaScriptで書いた処理をLLVMバイトコードC言語に変換することでHTTPのパーサを作ってしまう」というものです。正確にはTypeScriptで書かれており、TypeScriptで書いた処理をC言語LLVMバイトコードに変換しています。

これ、普通だと逆で、C言語 / LLVM で書かれたものを JavaScript でも呼べるように asm.js や wasm に変換する」というアプローチを取りそうですが、 Fedor は「 JavaScript で書いた処理を C言語 / LLVM に変換」しています。

中身を読むと分かりますが、実際にはCやLLVMのジェネレータがあります。Node.jsで試すなら、build optionで --experimental-http-parser を付けてビルドするか、 実行時に --http-parser=llhttp とやると最新の Node.js では実行できます。

llhttp
llhttp

セキュリティ

Node.js でも昨今問題になっているセキュリティについてもコアでの取り組みを紹介します。

セキュリティワーキンググループ

Node.js 内部では TSC と呼ばれるコアの内部で話し合いが行われています。毎回セキュリティのトピックは話されており、特に OpenSSLや V8 といった内部依存ライブラリの脆弱性があると事前に関係者だけに告知され、パッチの適用後、全体に通知されます。

nodejs.org

あんまり知られていませんが、 Bug Bounty プログラムも行われています。これにより、セキュリティの脆弱性をついたバグには報奨金が支払われるようになっています。

hackerone.com

セキュリティリリースがあると以下のように告知されます。

nodejs.org

セキュリティの取組み(npm, yarn)

コアのセキュリティではありませんが、3rd party製のライブラリでもセキュリティ障害が見られることがあります。 記憶にあたらしい所で行くと、 event-stream が別メンテナーによってセキュリティの問題を仕込まれた事がありました。

github.com

このような問題は急成長している npm のモジュールだと発生しがちです。対策としては今の所事後策で自分のリポジトリ内に問題があるかを調査することしかできません。そのようなコマンドを npm も yarn も用意しているので、積極的に使っていきましょう。

https://docs.npmjs.com/cli/audit

https://yarnpkg.com/lang/en/docs/cli/audit/

この手の npm|yarn audit コマンドを実行すると、自分の package-lock.json や yarn.lock 内にあるリポジトリ脆弱性の報告がないかを検証してくれます。

Web Standards

「Less is More」といっても例外があります。 Web 標準のAPI に関しては機能追加しようとする動きがあります。

コアコミッターの一人である James Snell さんの話にあった言葉を紹介します。

why node.js needs web standards
why node.js needs web standards

「Node.js は主にウェブアプリケーション開発者プラットフォームとしても今までも、今も存在している。一方で Node.js のコアは small core という哲学を表明している。この small core の中には Web Standards によるものも含まれている。」

これらの流れから、HTTP2 や ES Modules 、 Promise の改善といった機能追加は Web 標準の API と合わせるために行われています。

Promisify や fs.promises は Promise 改善の流れです。

util.promisify が追加された - from scratch

File System | Node.js v11.5.0 Documentation

また、 HTTP/2 から HTTP/3 までの流れも検討はされています。

ngtcp2をベースに HTTP/3 の JS 実装をしようという検討は書かれています。

今後の流れ: Unified JavaScript Platform

今後は JavaScript の共通プラットフォームとして統合していこうとする流れがあります。

Unified JavaScript Platform
Unified JavaScript Platform

現在は、 Web Standard API に関しては W3CWHATWG といったグループが作っています。Node.js の Standard API は我々 Node.js core memberが作っています。また、それらの中間にある JavaScript そのものの API や文法は ECMA/TC39 といったグループが作っています。

これらのグループには特にコンセンサスが取られているわけではなく、それぞれがそれぞれで緩く繋がっていましたが、今後はこの繋がりを強化して、もう少しお互いのコンセンサスを取りながら統合していきたいという話が NodeFest 2018 の Node Discussion でされていました。

W3C / WHATWG で作られたAPI と Node.js の API は歩み寄りをしていき、なるべく寄せていきます。また、その中央で ECMA/TC39 が仕様を固めるという風に三者がまとまりながら話を進めていくようにしていきたいという話がされていました。

Web API も Node.js APIECMAScript も求めているのは "ユースケース" です。さらにWeb API も Node APIECMAScriptも全部丸っと知っているのは仕様策定者よりも開発者になります。開発者、つまり僕らがライブラリやアプリケーションを作ってユースケースを作っていき、仕様策定者側にフィードバックしていく必要があります

つまり、リードしていくのは、仕様作成者だけではなく、我々です。

2019年は Node 学園祭は jsconf.jp として生まれ変わる予定ですが、そこでも仕様フィードバックの場を設けて今後の未来を一緒に作れるようにしていきたいとおもいます。

蛇足

この手のNode.js と JavaScriptアドベントカレンダーの活動も今後は JavaScript アドベントカレンダー一つにして、記事数が足りなくなったら「その2, その3」と増やせるようにしていきたいですね (というわけで Unified Advent Calendar Entry にしてみました)。