quick-lru

JavaScriptNode.jsキャッシュライブラリLRUメモリ管理

GitHub概要

sindresorhus/quick-lru

Simple “Least Recently Used” (LRU) cache

スター733
ウォッチ9
フォーク58
作成日:2017年3月30日
言語:JavaScript
ライセンス:MIT License

トピックス

なし

スター履歴

sindresorhus/quick-lru Star History
データ取得日時: 2025/10/22 10:04

キャッシュライブラリ

quick-lru

概要

quick-lruは軽量で高速なLRU(Least Recently Used)キャッシュ実装のJavaScriptライブラリです。シンプルなAPIと高性能を重視した設計で、メモリ使用量を制限しながら効率的なキャッシュ機能を提供します。

詳細

quick-lru(クイック・エルアールユー)はhashlruアルゴリズムにインスパイアされたLRUキャッシュライブラリですが、文字列キーのみをサポートするhashlruとは異なり、Mapを使用することで任意の型のキーと値(undefinedも含む)をサポートしています。Map のサブクラスとして実装されており、標準的なMap APIとの互換性を保ちながら、LRU機能とTTL(Time-to-Live)機能を追加しています。キャッシュサイズが最大値に達すると、最も使用頻度の低いアイテムが自動的に削除されます。イベントハンドリング機能により、アイテムがキャッシュから削除される直前にコールバック関数を実行できるため、オブジェクトURLのクリーンアップなどの副作用処理に対応しています。動的な設定変更機能により、本番システムでのキャッシュサイズのリアルタイム調整が可能です。軽量で高性能な特性により、複雑な機能よりもシンプルさとパフォーマンスを重視するアプリケーションに最適です。

メリット・デメリット

メリット

  • 高速パフォーマンス: 最も高速なLRUキャッシュの一つとして最適化
  • 軽量: 最小限の依存関係とコードサイズ
  • 柔軟なキー・値: 任意の型のキーと値(undefinedも含む)をサポート
  • TTL機能: オプションのTime-to-Live設定による自動期限切れ
  • イベント処理: アイテム削除時のコールバック処理対応
  • Map互換: 標準MapのサブクラスでイテレーターとMap APIに対応
  • 動的設定: 実行時のキャッシュサイズ調整が可能

デメリット

  • 機能の制限: 他のキャッシュライブラリと比べて高度な機能が限定的
  • 永続化なし: メモリ内のみでディスク永続化機能はなし
  • 分散対応なし: 単一プロセス内のみで分散キャッシュ機能なし
  • 統計情報不足: 詳細なキャッシュ統計やメトリクス機能が限定的
  • バックエンド制約: Redisなど外部ストレージとの統合機能なし

主要リンク

書き方の例

基本使用方法

import QuickLRU from 'quick-lru';

// 基本的なLRUキャッシュの作成
const lru = new QuickLRU({maxSize: 1000});

// 値の設定と取得
lru.set('🦄', '🌈');
console.log(lru.has('🦄')); //=> true
console.log(lru.get('🦄')); //=> '🌈'

// キーの存在確認
if (lru.has('🦄')) {
    console.log('Key exists in cache');
}

// キーの削除
lru.delete('🦄');

TTL(Time-to-Live)機能

// TTL付きキャッシュの作成
const cache = new QuickLRU({
    maxSize: 500,
    maxAge: 60000 // 60秒後に期限切れ
});

// 値の設定(デフォルトTTL)
cache.set('session:123', 'user_data');

// 個別のTTLを指定
cache.set('temp:data', 'temporary', {
    maxAge: 30000 // 30秒後に期限切れ
});

// 期限切れチェック
setTimeout(() => {
    console.log(cache.has('temp:data')); // false(期限切れ)
}, 35000);

イベント処理とクリーンアップ

const cache = new QuickLRU({
    maxSize: 100,
    onEviction: (key, value) => {
        console.log(`Evicting ${key}:`, value);
        
        // オブジェクトURLのクリーンアップ例
        if (typeof value === 'string' && value.startsWith('blob:')) {
            URL.revokeObjectURL(value);
        }
    }
});

// ファイルURLをキャッシュ
const file = new File(['content'], 'test.txt');
const blobUrl = URL.createObjectURL(file);
cache.set('file:123', blobUrl);

異なるデータ型の使用

const cache = new QuickLRU({maxSize: 200});

// 様々な型のキーと値
cache.set('string-key', 'string-value');
cache.set(42, 'number-key');
cache.set(Symbol('sym'), 'symbol-key');
cache.set(['array', 'key'], 'array-key');

// オブジェクトキー
const objKey = {id: 1, type: 'user'};
cache.set(objKey, {name: 'John', age: 30});

// undefined値の保存
cache.set('undefined-value', undefined);
console.log(cache.has('undefined-value')); // true
console.log(cache.get('undefined-value')); // undefined

イテレーション機能

const cache = new QuickLRU({maxSize: 5});

// データの追加
cache.set('a', 1);
cache.set('b', 2);
cache.set('c', 3);

// キーの反復処理
for (const key of cache.keys()) {
    console.log('Key:', key);
}

// 値の反復処理
for (const value of cache.values()) {
    console.log('Value:', value);
}

// エントリの反復処理(古い順)
for (const [key, value] of cache) {
    console.log(`${key}: ${value}`);
}

// エントリの反復処理(新しい順)
for (const [key, value] of cache.entriesDescending()) {
    console.log(`${key}: ${value}`);
}

動的サイズ調整

const cache = new QuickLRU({maxSize: 100});

// 運用中のサイズ変更
console.log(cache.maxSize); // 100

// サイズを動的に変更
cache.maxSize = 200;
console.log(cache.maxSize); // 200

// メモリ使用量に基づく動的調整例
function adjustCacheSize() {
    const memoryUsage = process.memoryUsage();
    const heapUsed = memoryUsage.heapUsed / 1024 / 1024; // MB
    
    if (heapUsed > 100) {
        cache.maxSize = Math.max(50, cache.maxSize * 0.8);
    } else if (heapUsed < 50) {
        cache.maxSize = Math.min(1000, cache.maxSize * 1.2);
    }
    
    console.log(`Adjusted cache size to: ${cache.maxSize}`);
}

実践的な使用例

import QuickLRU from 'quick-lru';

class ApiCache {
    constructor() {
        this.cache = new QuickLRU({
            maxSize: 1000,
            maxAge: 5 * 60 * 1000, // 5分
            onEviction: (key, value) => {
                console.log(`Cache miss for ${key}`);
            }
        });
    }

    async get(endpoint, params = {}) {
        const key = this.buildKey(endpoint, params);
        
        if (this.cache.has(key)) {
            console.log(`Cache hit for ${key}`);
            return this.cache.get(key);
        }

        console.log(`Cache miss for ${key}`);
        const response = await fetch(endpoint, params);
        const data = await response.json();
        
        this.cache.set(key, data);
        return data;
    }

    buildKey(endpoint, params) {
        return `${endpoint}:${JSON.stringify(params)}`;
    }

    clear() {
        this.cache.clear();
    }

    getStats() {
        return {
            size: this.cache.size,
            maxSize: this.cache.maxSize
        };
    }
}

// 使用例
const apiCache = new ApiCache();
const userData = await apiCache.get('/api/users/123');
console.log(apiCache.getStats()); // {size: 1, maxSize: 1000}