虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

Deno 1.9 へのアップデートと変更事項まとめ

皆さんこんにちは、おっくんです。

去る 2021 年 4 月 13 日に Deno 1.9 がリリースされました。 今回も、リリース内容の中から気になったものをピックアップして、紹介したいと思います。

実行環境

  • macOS Catalina 10.15.7

アップデートのやり方

今回は Deno 1.8.3 から Deno 1.9.2 へのアップデートを行います。 (既に 2021 年 4 月 23 日 に Deno 1.9.2 にアップデートされていました。)

アップデートする Deno を導入した時のコマンドは以下の通りです。

curl -fsSL https://deno.land/x/install/install.sh | sh

アップデートは、以下のコマンドで実施しました。

$ deno upgrade

# バージョン指定する場合は次のコマンドで実行します。

# deno upgrade --version 1.8.3

ネットワーク環境もあるでしょうが、完了するまでに 20 秒ほどで終わりました。 これまでの更新の度に所要時間を確認していますが、毎回短縮されているように感じます。

方法については、こちらに記載があります。

deno.land

Deno 1.9

Deno 1.9 での変更事項をDeno 1.9 リリースノートを元に確認します

deno.com

ネイティブ HTTP/2 Web サーバー の導入

これまでのリリースノートでも、頻繁に紹介に使用されていたstd/httpは、スクリプト化された HTTP サーバーにかかわらず、驚くほど良いレイテンシで稼働しています。
しかし、std/http が対応しているのは HTTP1.1 のみで、HTTP2 には用意に対応できないそうです。

HTTPへの対応 は重要で、ネイティブコードで適切に実装された HTTP サーバーがあります。
したがい、Hyperを採用し、 Deno の HTTP/2 サーバー API を新たに作成しました。
(ここでいうネイティブコードとは Rust のことです。)

単純な hello-worldを返すだけのスループットは、std/httpを使用した時と比べて 48% 向上するそうです。

(https://deno.com/blog/v1.9 Deno 1.9 Release Notes より引用)

この HTTP/2 用の API は現在安定化を進めているもので、実行には--unstable フラグが必要です。

サンプルを動作させてみます。

const body = new TextEncoder().encode("Hello World");

for await (const conn of Deno.listen({ port: 4500 })) {
  (async () => {
    for await (const { respondWith } of Deno.serveHttp(conn)) {
      respondWith(new Response(body));
    }
  })();
}

実行時は、以下のコマンド。

$ deno -V
deno 1.9.2

$ deno run --unstable --allow-net http2.ts

ブラウザでlocalhost:4500アクセスすると、Hello World と表示されます。

注意すべきなのは、新しい API を使ったからといって HTTP2 になるわけではないという点です。

Hyper が使用しているクレートH2が、HTTP/2 over TCP の実装は目標としていないようなので、今後もこの様相は変わらない可能性が高そうです。
普段使用する有名どころのブラウザは HTTP/2 over TCP に軒並み対応していないので、開発環境でも HTTP2 を体験するには TLS を有効化する必要があります。
加えて次の ALPN の対応をおこなうひつようがあります。

Deno.listenTls での、ALPN サポート

ALPN は Application-Layer Protocol Negotiation の略で、RFC7301で標準化されたアプリケーション層でのプロトコルのネゴシエーションについての仕様です。

前述の通りstd/http は、http/1.1 のみサポートしていたので不要でしたが、Deno.serveHttp の導入より HTTP/2 をサポートするために Deno.listenTlsalpnProtocolsパラメータが追加されました。

リリースノートにある HTTP/2 を完全にサポートする HTTP サーバーの実装例は以下のようになります。

const listener = Deno.listenTls({
  port: 443,
  certFile: "./cert.pem",
  keyFile: "./key.pem",
  alpnProtocols: ["h2", "http/1.1"],
});

for await (const conn of listener) {
  handleConn(conn);
}

async function handleConn(conn: Deno.Conn) {
  const httpConn = Deno.serveHttp(conn);
  for await (const { request, respondWith } of httpConn) {
    respondWith(new Response(`Responding to ${request.url}`));
  }
}

serde_v8 の導入による、高速なネイティブコード(Rust)呼び出し

Deno は Rust で実装されています。
この 2 つの間のデータのやり取りは、ArrayBuffers をカスタムした「payload」に JSON や flatbuffers、カスタムしたバイナリエンコーディングを埋め込んで使用していました。
しかし、これがパフォーマンスのボトルネックと複雑さの断片化の原因となっていたそうです。

@AaronOが提案した v8 と Rust の間でのデータの受け渡しは直接シリアル化する方が効率的だと提案し、こちらが採用されました。

提案の Issue はこちら Optimize JSON ops: minimize allocs by decoding v8::Value directly

github.com

この提案から serde_v8が作られています。 serde_v8 の目的は、v8 と Rust の間に最大効率でオーバーヘッド 0 の全単射を提供することです。

以下のグラフは opcolls(rust 側の呼び出しあたりの、ナノ秒) から特定のクラスの最小コストを計測したものです。

(https://deno.com/blog/v1.9 Deno 1.9 Release Notes より引用)

上記の改善により、Deno の効率は大きく改善し、HTTP ベンチでスループットとレイテンシが向上しました。

(https://deno.com/blog/v1.9 Deno 1.9 Release Notes より引用)

1.8.3 と 1.9.0 の間での比較グラフが示している圧倒的な性能差には、驚く限りです。

fetch API で BlobURL のサポートと改善

blob: 形式の URL のサポートが追加されました。
ブラウザと同じ API を使用可能です。

const blob = new Blob(["Hello World!"]);
const url = URL.createObjectURL(blob);
console.log(url); // blob:null/7b09af21-03d5-461e-90a3-af329667d0ac

const resp = await fetch(url);
console.log(await resp.text()); // Hello World!

URL.revokeObjectURL(url);

加えて Deno 1.7 からインポートに使用できるようになっていた data: 形式の URL が fetch API でも使用できるようになりました。
実際に dataURL に変化して読み込みを試してみます。

[データ URL への変換]

$ echo Hello World! | base64
SGVsbG8gV29ybGQhCg==

[データ URL の利用]

$ deno
Deno 1.9.2
exit using ctrl+d or close()
> const resp = await fetch("data:text/plain;base64,SGVsbG8gV29ybGQhCg==");
undefined
> console.log(await resp.text());
Hello World!

undefined

--allow-env --allow-run がリストを受け付けるように機能拡張

Deno は実行環境のリソースアクセスに対して厳格に権限管理します。
環境変数へのアクセスを許可する --allow-env と外部プロセスの呼び出しを許可する --allow-runは、これまで in or nothing 、許可するか・しないかの操作しかできませんでした

deno 1.9 からはこれらの 2 つのオプションがリストを受け付けるようになりました。

動作確認してみます。

$ export DEBUG=true
$ export LOG=log/deno.log

$ deno run --allow-env=DEBUG https://deno.com/v1.9/env_permissions.ts
true
error: Uncaught PermissionDenied: Requires env access to "LOG", run again with the --allow-env flag
console.log(Deno.env.get("LOG"));
                     ^
    at unwrapOpResult (deno:core/core.js:99:13)
    at Object.opSync (deno:core/core.js:113:12)
    at Object.getEnv [as get] (deno:runtime/js/30_os.js:61:17)
    at https://deno.com/v1.9/env_permissions.ts:2:22

# オプションに追加で LOG を与えると実行できる
$ deno run --allow-env=DEBUG,LOG https://deno.com/v1.9/env_permissions.ts
true
log/deno.log
$ deno run --allow-run=deno https://deno.com/v1.9/run_permissions.ts
Download https://deno.com/v1.9/run_permissions.ts
Check https://deno.com/v1.9/run_permissions.ts
Hello from deno!
error: Uncaught (in promise) PermissionDenied: Requires run access to "echo", run again with the --allow-run flag
const echoProcess = Deno.run({
                         ^
    at unwrapOpResult (deno:core/core.js:99:13)
    at Object.opSync (deno:core/core.js:113:12)
    at opRun (deno:runtime/js/40_process.js:20:17)
    at Object.run (deno:runtime/js/40_process.js:104:17)
    at https://deno.com/v1.9/run_permissions.ts:6:26

# オプションに追加で echo を与えると実行できる
$ deno run --allow-run=deno,echo https://deno.com/v1.9/run_permissions.ts
Hello from deno!
Hello from echo!

対話的パーミッションプロンプト

Deno では、アクセス許可が不足していると、エラーがスローされて終了します。
--prompt を実行時に付与することで、権限を付与することができるようになりました。

前項で動作確認した env_permissions.ts の実行で試してみます。

## --allow-env=DEBUG,LOG を付与せずに、--promptを付与する
$ deno run --prompt https://deno.com/v1.9/env_permissions.ts
⚠️  ️Deno requests env access to "DEBUG". Grant? [g/d (g = grant, d = deny)] g
true
⚠️  ️Deno requests env access to "LOG". Grant? [g/d (g = grant, d = deny)] d
error: Uncaught PermissionDenied: Requires env access to "LOG", run again with the --allow-env flag
console.log(Deno.env.get("LOG"));
                     ^
    at unwrapOpResult (deno:core/core.js:99:13)
    at Object.opSync (deno:core/core.js:113:12)
    at Object.getEnv [as get] (deno:runtime/js/30_os.js:61:17)
    at https://deno.com/v1.9/env_permissions.ts:2:22

将来のリリースでは、デフォルトで ON にすることが検討されているそうです。
関連した Issues が、挙がっていました。 Re-implement "--no-prompt" permission flag

github.com

そのほか

前述の内容以外にも、Deno 1.9 では変更が入っています。

  • Deno Language Serve の改善
  • 新しい API が安定化
    • ファイルシステムに関する API が安定化しました。
      • Deno.fstat
      • Deno.fstatSync
      • Deno.ftruncate
      • Deno.ftruncateSync
    • Deno.File クラスにメソッドが追加
      • File.stat
      • File.statSync
      • File.truncate
      • File.truncateSync
  • いくつかの API が、非推奨になりました。
    • Deno.Buffer
    • Deno.readAll
    • Deno.readAllSync
    • Deno.writeAll
    • Deno.writeAllSync
    • Deno.iter
    • Deno.iterSync
  • 新しいデフォルトの TypeScript のオプションuseDefineForClassFields
    TypeScript の デフォルトの設定では、useDefineForClassFieldsは、false です。

    www.typescriptlang.org

    Deno 1.9 から true になっており、ユーザーは上書きすることができません。


Deno 1.9 のレポートを仕上げる中で、HTTP/2 の仕様として h2(HTTP/2 over TLS) と h2c(HTTP/2 over TCP) を調べていました。
主要 Web ブラウザが h2 にしか対応していない状況を見ると、開発環境をあたりまえに TLS 化するようになるかもしれませんね。

この記事をまとめている最中の 2021 年 5 月 11 日 にDeno 1.10が公開されました。 実にDeno の展開は早いです。 また Deno 1.10 のレポートもしていきたいと思います。

P.S.

直近のイベント情報

■5月21日(金)19:30~ とらのあなラボエンジニア座談会Vol.8【なぜとらラボエンジニアはラジオを始めたのか】
 を開催します!興味のある方はぜひご参加ください!

yumenosora.connpass.com

■5月28日(金)19:30~ 【オンライン】オタクが最新技術を追うLTイベント#24【初心者歓迎】【テーマフリー】  を開催します!こちらは発表者も募集中ですので、LT初心者という方でもぜひご応募ください!

yumenosora.connpass.com

採用情報

■募集職種
yumenosora.co.jp

カジュアル面談も随時開催中です

■お申し込みはこちら!
yumenosora.connpass.com

■ToraLab.fmスタートしました!

メンバーによるPodcastを配信中!
是非スキマ時間に聞いて頂けると嬉しいです。
anchor.fm

■Twitterもフォローしてくださいね!

ツイッターでも随時情報発信をしています
twitter.com