タグアーカイブ crypto

Node.js 24.16.0 (LTS) 登場、cryptoとテストランナーが大幅進化

Node.js 24.16.0 (LTS) 登場、cryptoとテストランナーが大幅進化

2026年5月21日、Node.js 24.16.0(コードネーム Krypton)が長期サポート(LTS)版としてリリースされた。

このバージョンでは、cryptoモジュールへのUUID v7サポート追加、debuggerへの式プローブ機能、HTTPクライアントの安全性強化、テストランナーの大幅な機能拡張など、実務に直結する複数の改善が含まれている。

本記事では、これらの新機能と内部改善を実装例とともに詳しく解説する。

UUID v7がNode.js標準機能に

UUID v7がNode.js標準機能に

今回のリリースで最も注目すべき追加機能のひとつが、crypto.randomUUIDv7()の実装だ。UUID v7(Universally Unique Identifier version 7)は、タイムスタンプベースの一意識別子であり、従来広く使われてきたランダムベースのUUID v4とは根本的な設計が異なる。

UUID v7とは何か

UUID v7は、RFC 9562で標準化された新しいUUIDバージョンだ。先頭48ビットにUnixタイムスタンプ(ミリ秒単位)を含み、続いてランダムビットが配置される。この構造により、生成時刻に基づいた自然なソート順が得られる。

データベースのプライマリキーとしてUUIDを使用する場合、v4ではランダムな値のためインデックスの断片化が発生しやすかった。一方、v7では時系列順に並ぶため、B-treeインデックスの効率が大幅に改善する。具体的には、書き込み性能が最大40〜60%向上したとの報告もある。

crypto.randomUUIDv7()の基本的な使い方

Node.js 24.16.0では、以下のようにシンプルに呼び出せる。

const { randomUUIDv7 } = require('node:crypto');

console.log(randomUUIDv7());
// 例: 0193c548-d70c-7a4a-b17b-3c2b12e5c6f1

戻り値は標準的なUUID文字列形式(8-4-4-4-12のハイフン区切り)で、第三フィールドの先頭ニブルが7になる点がv7の特徴だ。

従来のv4に代えてv7を採用するメリットを、概念図で整理する。

従来のUUID v4(Before)
550e8400 e29b-41d4 a716 446655440000
全てランダム値のため、インデックスに格納する際の位置が予測できず、B-treeのページ分割が多発する
⚠ 断片化率が高く、書き込み性能に悪影響
UUID v7(After)
0193c548 タイムスタンプ d70c-7a4a b17b-3c2b12e5c6f1 ランダム
先頭48ビットが時刻順で単調増加するため、B-treeへの追加が常に右端付近で済み、ページ分割が最小限になる
✅ インデックス効率が大幅に改善し、書き込みスループットが向上
ランダム値  タイムスタンプ部分  ランダム部分(残り)

上の図では、v4の全ランダム性に対して、v7が時刻情報を含む構造であることを対比している。時系列に沿ったINSERT性能が重要なシステムでは、移行を検討する価値がある。

実運用で注意すべき点

UUID v7は時刻情報を含むため、生成時刻が外部に推測される可能性がある。セキュリティ要件が厳しい環境では、この点を評価した上で採用を判断する必要がある。また、時計の巻き戻しが発生するケースでは単調増加性が保証されないため、Node.jsの実装では内部カウンターで対処している。

デバッグ体験を変えるedit-free式プローブ

デバッグ体験を変えるedit-free式プローブ

Node.js 24.16.0では、node inspectコマンドにedit-free runtime expression probesが導入された。これは、デバッグ対象のコードを一切変更することなく、実行時に任意の式を評価できる仕組みだ。

これまでのデバッグ手法との違い

従来、Node.jsアプリケーションの特定の変数の値や式の結果を確認するには、コードにconsole.log()を挿入するか、デバッガでブレークポイントを設定して手動で評価する必要があった。前者はコード改変と再起動を伴い、後者は手動操作の手間が大きい。

従来方式(Before)
コード修正 再起動 値の確認
コードへのconsole.log挿入が必須。本番環境では使えず、開発中も一手間かかる
式プローブ方式(After)
inspect接続 式を指定 即時評価
ソースコードに一切手を加えず、実行中のプロセスで任意の式の値を取得可能
手動・コード変更が必要な工程  ツール側の自動化工程  結果

式プローブを使えば、稼働中のプロセスに対して外部から評価式を注入できるため、トラブルシュートのスピードが大幅に向上する。特に、再起動が難しい本番環境での障害調査で威力を発揮するだろう。

式プローブの活用シナリオ

たとえば、メモリリークが疑われる長時間稼働プロセスで、特定オブジェクトの参照状況を調査するケースを考えてみる。従来であればヒープダンプの取得と解析が必要だが、式プローブを使えばprocess.memoryUsage()や特定変数の.lengthをその場で評価できる。

この機能の追加は、貢献者のJoyee Cheung氏によるPR #62713に基づいている。V8のインスペクタープロトコルを活用した実装で、Chrome DevToolsのExpression Watchに近い使用感だ。

HTTPクライアントの安全性と利便性の向上

HTTPクライアントの安全性と利便性の向上

Node.jsのHTTPクライアント機能に、2つの重要な強化が加わった。いずれも実際のプロダクションコードの安全性と記述性に直結する改善だ。

ClientRequestのオプションマージ強化

http.ClientRequestの内部で、ユーザーが渡したオプションとデフォルト値をマージする処理が厳格化された。この変更は、プロトタイプ汚染(Prototype Pollution)攻撃のベクトルを塞ぐことを主眼としている。

プロトタイプ汚染とは、Object.prototype__proto__を経由してアプリケーション全体のオブジェクトの振る舞いを改変する攻撃手法だ。Node.jsのHTTPクライアントはリクエストオプションを内部でマージする際、従来はプロトタイプチェーンを適切に処理していなかった。今回のPR #63082で、マージ時にnullプロトタイプオブジェクトを使用するよう修正され、この攻撃経路が遮断された。

従来のマージ処理(Before)
Object.assign({}, defaults, userOptions)
__proto__キー経由でプロトタイプが改変される可能性があった
強化後のマージ処理(After)
safeMerge(defaults, userOptions)
プロトタイプを持たないオブジェクトを使用し、__proto__やconstructorキーを無視

Node.jsのMatteo Collina氏が主導したこの修正は、Semver-Minorに分類されているが、セキュリティ上の重要性は高い。とくにユーザー入力からHTTPリクエストオプションを構築するアプリケーションでは、速やかなアップデートが推奨される。

req.signalでリクエスト中断が容易に

もう一つの改善は、http.IncomingMessageへのreq.signalプロパティの追加だ(PR #62541)。これはAbortSignalオブジェクトを提供し、AbortControllerと組み合わせることで、リクエストの中断処理をPromiseベースで簡潔に記述できる。

const controller = new AbortController();

const req = http.request(url, { signal: controller.signal });
req.end();

// 5秒後にタイムアウト
setTimeout(() => controller.abort(), 5000);

req.on('error', (err) => {
  if (err.name === 'AbortError') {
    console.log('リクエストが中断されました');
  }
});

従来はreq.destroy()を手動で呼び出す必要があり、後続のエラーハンドリングも煩雑だった。signalパターンの採用により、fetch APIと同じインターフェースで中断処理を統一的に扱えるようになった点が大きい。

テストランナーが実戦的な機能を獲得

テストランナーが実戦的な機能を獲得

Node.jsの組み込みテストランナー(node --test)は、ここ数バージョンで急速に成熟している。24.16.0では、テスト順序のランダム化、モックタイマーのAPI整備、AbortSignal.timeoutのモックサポートという3つの重要な機能が追加された。

テスト順序のランダム化

Pietro Marchini氏によるPR #61747では、テストファイル内のテスト実行順序をランダム化するオプションが導入された。これは、テスト間の暗黙的な依存関係を炙り出すための機能だ。

node --test --test-randomize-order test/*.test.js

特定の順序で実行されることを前提に書かれたテストは、ランダム化によって失敗する。これにより、グローバル状態への暗黙の依存や、テスト間のデータ共有の問題を早期に発見できる。テクニックとしては、CIパイプラインにランダム実行を常時組み込むことで、テストスイートの堅牢性を継続的に保証できる。

モックタイマーAPIの拡張

テストランナーのモックタイマー機能が、AbortSignal.timeout()にも対応した(PR #60751)。これにより、タイムアウトに依存する非同期処理のテストが、実際の時間を消費せずに実行できるようになった。

STEP 1 テスト内でAbortSignal.timeout(5000)を呼び出す
STEP 2 モックタイマーが即座に5秒後のタイムアウトをエミュレート
STEP 3 実際の5秒間を待たずにAbortErrorが発生

実際のタイムアウト時間を待つ必要がなくなるため、数百のテストを含むスイート全体の実行時間を大幅に短縮できる。テストの信頼性も向上し、CIでの不安定なテスト(flaky test)の削減に寄与する。

テストIDとOpenTelemetry対応

さらに、各テストにtestIdが付与され(PR #62772)、TracingChannelを通じたOpenTelemetryインスツルメンテーションとの統合が可能になった(PR #62502)。テストのトレーシング情報を本番系の監視ツールと統合することで、テスト品質の可視化と傾向分析ができるようになる。

ファイルシステムとストリームの機能強化

ファイルシステムとストリームの機能強化

fsモジュールとストリームモジュールにも、実務のユースケースに即した改善が複数含まれている。

fs.stat()がAbortSignalに対応

Mert Can Altin氏によるPR #57775で、fs.stat()signalオプションが追加された。ネットワークファイルシステム上のファイルをstatする際に、応答が遅い場合のタイムアウト制御が可能になる。

import { stat } from 'node:fs/promises';

const ac = new AbortController();
setTimeout(() => ac.abort(), 3000);

try {
  const stats = await stat('/mnt/nfs/large-file.dat', { signal: ac.signal });
  console.log(stats.size);
} catch (err) {
  if (err.name === 'AbortError') {
    console.error('statがタイムアウトしました');
  }
}

これは、HTTPリクエストの中断パターンと同じAPI設計で、Node.js全体で一貫した中断処理のセマンティクスが浸透しつつあることを示している。

statfsのfrsizeフィールド公開

Jinho Jang氏のPR #62277により、statfsの戻り値にfrsize(フラグメントサイズ)フィールドが追加された。これはファイルシステムの最小割り当て単位を示し、ディスク使用量の正確な計算に利用できる。

import { statfs } from 'node:fs/promises';

const s = await statfs('/data');
const actualSize = Math.ceil(fileSize / s.frsize) * s.frsize;
console.log(`実ディスク消費量: ${actualSize} バイト`);

これまではbsize(ブロックサイズ)しか公開されておらず、一部のファイルシステム(特にZFSやbtrfs)では正確な計算ができなかった。frsizeの追加により、クロスプラットフォームで信頼性の高いディスク容量の見積もりが可能になる。

duplexPairの破壊伝播の改善

stream.duplexPair()は、読み取り側と書き込み側が対になった2つのDuplexストリームを生成するユーティリティだ。Ahmed Elhor氏のPR #61098によって、片方のストリームが破壊(destroy)された場合に、もう片方にもその破壊が伝播するようになった。

これにより、ソケットの模擬テストやプロキシ処理で、一方のストリームがクローズされたにもかかわらず他方が生き続けてメモリリークを引き起こす問題が解決される。streamモジュールの内部的な一貫性を高める重要な修正だ。

util.styleTextの16進数カラー対応

util.styleTextの16進数カラー対応

Guilherme Araújo氏のPR #61556により、util.styleText()に16進数カラーコード(例: #ff5733)の直接指定が可能になった。ターミナル出力のスタイリングにおいて、より繊細な色表現が必要な場面で役立つ。

import { styleText } from 'node:util';

console.log(styleText('#ff5733', '注意: ディスク使用率が90%を超えています'));
console.log(styleText('#4ecdc4', '情報: バックアップが正常に完了しました'));

従来はstyleText('red', ...)styleText('green', ...)のように、限られた色名しか使えなかった。16進数指定のサポートにより、CIログの色分けやCLIツールのブランドカラー適用など、実務での表現力が格段に向上する。

内部実装としては、ターミナルの24ビットカラー(トゥルーカラー)エスケープシーケンスを使用している。サポートしていないターミナルでは近似の8色にフォールバックされるため、互換性の問題も起きにくい。

この記事のポイント

  • Node.js 24.16.0 (LTS) は、crypto.randomUUIDv7()を実装し、データベースのインデックス効率を改善する時系列ソート可能なUUID生成が標準機能になった
  • debuggerにedit-free式プローブが追加され、コード修正なしで実行中のプロセスの式評価が可能になった
  • HTTPクライアントのオプションマージが強化されプロトタイプ汚染攻撃が防止され、req.signalによるAbortパターンが統一された
  • テストランナーでテスト順序ランダム化、モックタイマーのAbortSignal対応、testIdとOpenTelemetry統合が実装された
  • fs.stat()へのsignal追加、statfsのfrsize公開、duplexPairの破壊伝播改善など、ファイルシステムとストリームの実用性が向上した
  • util.styleText()で16進数カラーコードが直接指定可能になり、CLIツールの色表現力が飛躍的に向上した