JeongSeop Byeon

免責事項: 母国語が英語ではないため、記事の構成と洗練にAIを活用しました。

数日前、私は Node.jsでゼロアロケーションS3-FIFOキャッシュを実装する——事前割り当てされた TypedArray を使用してNode.jsのガベージコレクション(GC)圧力を排除する S3-FIFO (Simple and Scalable Scan-Resistant FIFO) キャッシュアルゴリズムの実装を示す探索的v0.1リリース——を共有しました。

本日、広範なストレステスト、アーキテクチャの強化、および100%ユニットテストカバレッジを経て、s3fifo v1.0.0のリリースを発表できることを嬉しく思います! 🚀

簡単な振り返り: S3-FIFO + ゼロアロケーションの理由

従来の LRU (Least Recently Used) キャッシュには、本番環境における2つの大きな欠点があります:

  1. ワンヒットワンダーによるキャッシュ汚染: スキャンやシーケンシャルクエリが1回しかアクセスされないアイテムでキャッシュを溢れさせ、高頻度のワーキングセットを追い出してしまいます。
  2. ガベージコレクションのオーバーヘッド: ナイーブなオブジェクトベースのキャッシュ実装は、set() のたびにノードを動的に割り当て、高スループットのワークロードで頻繁なGCの一時停止を引き起こします。 S3-FIFO は、キャッシュエントリを3つの軽量キューに整理することでキャッシュ汚染を解決します:
  3. Small (S): ワンヒットワンダーを素早くフィルタリング(通常、総容量の約10%)。
  4. Main (M): 複数アクセスの高頻度アイテムを保持(容量の約90%)。
  5. Ghost (G): Smallから追い出されたキーを記憶し、再リクエストされた場合に即座にMainに昇格させます。 s3fifo は、これらのキューを連続したTypedArray(Uint32ArrayFloat64Array)と再利用可能なインデックスプールで支えることで、ホットな get/set サイクル中に動的オブジェクト割り当てをゼロにします。 --- ## v1.0.0の新機能(本番環境対応) v0.1がコアアルゴリズムの速度に焦点を当てていたのに対し、v1.0.0 はミッションクリティカルなNode.jsマイクロサービスに必要な開発者の人間工学、ライフサイクルツール、および安全保証をすべて提供します。 ### 1. 💾 コールドスタート永続化(dump & load) コンテナの再起動やデプロイメント時のデータベースサンダリング herd / キャッシュスタンピードを防ぎます。 s3fifo 1.0 は、アクティブな常駐エントリを元の作成タイムスタンプと残りのTTLとともにシリアライズすることをサポートします:
import fs from "node:fs";
import { S3Fifo } from "s3fifo";
const cache = new S3Fifo<string>({ max: 10000 });
// 1. アクティブなキャッシュアイテムをエクスポート(一時キーをオプションでフィルタリング)
const dumpData = cache.dump((key, value) => !key.startsWith("temp:"));
fs.writeFileSync("cache-snapshot.json", JSON.stringify(dumpData));
// 2. サーバー起動時/事前ウォーミング時: キャッシュ状態を即座に復元
const snapshot = JSON.parse(fs.readFileSync("cache-snapshot.json", "utf-8"));
cache.load(snapshot);

全画面モードに入る 全画面モードを終了

2. ♻️ 安全なリソースライフサイクル(dispose コールバック)

キャッシュアイテムが追い出されたり、上書きされたり、クリアされたりする場合、外部リソースを解放する必要があります(例: ファイル記述子のクローズ、DBハンドルの破棄、または追い出しメトリクスの追跡)。

const cache = new S3Fifo<Buffer>({
  max: 500,
  dispose: (key, buffer, reason) => {
    console.log(`Key ${key} removed due to: ${reason}`); // 'evict' | 'set' | 'delete' | 'clear'
    // ネイティブメモリまたはリソースを安全に解放
  },
});

全画面モードに入る 全画面モードを終了

🛡️ 再入可能性保護: dispose コールバックは、キャッシュ操作が完了するまで安全に延期されるため、コールバックが cache.set() または cache.delete() を再帰的に呼び出しても内部状態の破損を防ぎます。

3. 🔍 副作用のない検査(peek

ログ記録、ヘルスチェック、または監視のために、頻度カウンターをバンプしたり追い出しステータスを変更したりせずにキャッシュされた値を確認する必要がありますか? peek() は純粋で副作用のない読み取りを可能にします:

// S3-FIFOの頻度ビットフィールドやTTLタイムスタンプを変更しません
const val = cache.peek("user:1001");

全画面モードに入る 全画面モードを終了

4. 🔄 標準ES6イテレータとMap API

s3fifo 1.0 は、JavaScriptのネイティブイテレーションプロトコルとシームレスに統合されます:

// 標準JS Mapスタイルのイテレータ
for (const [key, value] of cache) {
  console.log(key, value);
}
const keys = Array.from(cache.keys());
const values = Array.from(cache.values());
const entries = Array.from(cache.entries());
cache.forEach((val, key) => {
  /* ... */
});

全画面モードに入る 全画面モードを終了

5. 🧹 リークフリーなライフサイクルティアダウン(close

サーバーレス、ホットリロード(HMR)、またはテスト環境では、バックグラウンドタイマーがプロセス終了を妨げる可能性があります。 close() メソッドは、アクティブなバックグラウンドTTLティッカーをクリアし、メモリバッファを解放し、メモリリークを防ぎます:

// シャットダウン時のクリーンなティアダウン
cache.close();
console.log(cache.isClosed); // true

全画面モードに入る 全画面モードを終了


📊 ベンチマーク: lru-cacheとのヒット率とスループットの比較

Zipfian分布(歪度 0.99、ワーキングセット 100,000 キー)で s3fifo v1.0 と Nodeの人気 lru-cache を比較してテストしました:

平均ヒット率 (%)

キャッシュサイズ(プール比) lru-cache s3fifo
1% 48.90% 58.30%
5% 65.00% 71.10%
10% 72.30% 76.40%
25% 82.10% 82.70%
50% 89.00% 86.30%

平均スループット (ops/sec)

キャッシュサイズ(プール比) lru-cache s3fifo
1% 10.8M 15.5M
5% 10.8M 14.4M
10% 10.2M 14.3M
25% 10.3M 13.5M
50% 10.3M 14.7M

主なポイント:

  • 最大+9.4%高いヒット率 キャッシュ容量がデータセットサイズに対して小さい場合(1%–10%の範囲)——フロントエンドデータベースキャッシュや高並行性マイクロサービスに最適。

- 約40%高いスループット(約14.5M–15.5M ops/sec 対 約10.5M ops/sec) get/set 操作中のオブジェクト割り当てがゼロであるため。

はじめに

npm経由でインストール:

npm install s3fifo

全画面モードに入る 全画面モードを終了

基本的な使用法:

import { S3Fifo } from "s3fifo";
const cache = new S3Fifo<string>({
  max: 1000,
  ttl: 60000, // 60sグローバルTTL
});
cache.set("session:abc, "user_data");
console.log(cache.get("session:abc")); // 'user_data'
console.log(cache.size); // 1

全画面モードに入る 全画面モードを終了


リンクとフィードバック

  • 📦 NPM: s3fifo
  • 🐙 GitHubリポジトリ: BJS-kr/s3fifo Node.jsでスキャン耐性があり、最小限のGCフットプリントを持つインメモリキャッシュの代替を探しているなら、s3fifo を試してみてください! バグレポート、フィードバック、GitHubスターを大いに歓迎します! 🙏