Content Hash Cache Pattern

affaan-m/ECC/docs/ja-JP/skills/content-hash-cache-pattern

作者 affaan-mef648e01899ba3e8dc6371642deaaf64b4477775無授權條款275K 個星標收錄於 2026年10月9日更新於 2026年10月9日儲存庫4 天前更新

SHA-256コンテンツハッシュを使用して、高コストなファイル処理結果をキャッシュします — パス非依存、自動無効化、サービスレイヤーの分離。

AI 產生的概覽

一種以 SHA-256 內容雜湊為鍵、快取高成本檔案處理結果的模式。

功能
這個技能說明一種快取模式:以檔案內容的 SHA-256 雜湊而非檔案路徑作為快取鍵,因此項目在檔案重新命名或搬移後仍然有效,並在內容變更時自動失效。內容涵蓋分塊讀取計算雜湊、凍結資料類別快取項目、以雜湊命名的 JSON 檔案,以及讓擷取函式保持單純的服務層包裝。文中也列出設計取捨、最佳實務與反面模式。
適用情境
適合用於建立 PDF 解析、OCR、文字擷取或影像分析等檔案處理流程,且同一檔案會反覆處理的情況。也適合需要快取開關的 CLI 工具,以及檔案會重複出現的批次工作。不適用於必須永遠保持最新的資料,或結果取決於檔案內容以外參數的情況。
執行需求
不隨附指令碼,僅為指引說明。範例假定使用 Python 3 標準函式庫(hashlib、json、dataclasses、pathlib),並需要可寫入的快取目錄。

コンテンツハッシュファイルキャッシュパターン

SHA-256コンテンツハッシュをキャッシュキーとして使用して、高コストなファイル処理結果(PDF解析、テキスト抽出、画像分析)をキャッシュします。パスベースのキャッシュとは異なり、このアプローチはファイルの移動/名前変更に対して生き残り、コンテンツが変更されたときに自動的に無効化されます。

起動条件

  • ファイル処理パイプラインの構築(PDF、画像、テキスト抽出)
  • 処理コストが高く、同じファイルが繰り返し処理される場合
  • --cache/--no-cacheCLIオプションが必要な場合
  • 既存の純粋な関数を変更せずにキャッシュを追加したい場合

コアパターン

1. コンテンツハッシュベースのキャッシュキー

パスではなくファイルコンテンツをキャッシュキーとして使用します:

python
import hashlibfrom pathlib import Path
_HASH_CHUNK_SIZE = 65536  # 大きなファイルには64KBチャンク
def compute_file_hash(path: Path) -> str:    """ファイルコンテンツのSHA-256(大きなファイルにはチャンク処理)。"""    if not path.is_file():        raise FileNotFoundError(f"File not found: {path}")    sha256 = hashlib.sha256()    with open(path, "rb") as f:        while True:            chunk = f.read(_HASH_CHUNK_SIZE)            if not chunk:                break            sha256.update(chunk)    return sha256.hexdigest()

なぜコンテンツハッシュ? ファイルの名前変更/移動 = キャッシュヒット。コンテンツ変更 = 自動無効化。インデックスファイル不要。

2. キャッシュエントリの凍結データクラス

python
from dataclasses import dataclass
@dataclass(frozen=True, slots=True)class CacheEntry:    file_hash: str    source_path: str    document: ExtractedDocument  # キャッシュされた結果

3. ファイルベースのキャッシュストレージ

各キャッシュエントリは{hash}.jsonとして保存されます — ハッシュによるO(1)検索、インデックスファイル不要。

python
import jsonfrom typing import Any
def write_cache(cache_dir: Path, entry: CacheEntry) -> None:    cache_dir.mkdir(parents=True, exist_ok=True)    cache_file = cache_dir / f"{entry.file_hash}.json"    data = serialize_entry(entry)    cache_file.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
def read_cache(cache_dir: Path, file_hash: str) -> CacheEntry | None:    cache_file = cache_dir / f"{file_hash}.json"    if not cache_file.is_file():        return None    try:        raw = cache_file.read_text(encoding="utf-8")        data = json.loads(raw)        return deserialize_entry(data)    except (json.JSONDecodeError, ValueError, KeyError):        return None  # 破損をキャッシュミスとして扱う

4. サービスレイヤーラッパー(SRP)

処理関数を純粋に保ちます。キャッシュを別のサービスレイヤーとして追加します。

python
def extract_with_cache(    file_path: Path,    *,    cache_enabled: bool = True,    cache_dir: Path = Path(".cache"),) -> ExtractedDocument:    """サービスレイヤー: キャッシュチェック -> 抽出 -> キャッシュ書き込み。"""    if not cache_enabled:        return extract_text(file_path)  # 純粋な関数、キャッシュの知識なし
    file_hash = compute_file_hash(file_path)
    # キャッシュを確認    cached = read_cache(cache_dir, file_hash)    if cached is not None:        logger.info("Cache hit: %s (hash=%s)", file_path.name, file_hash[:12])        return cached.document
    # キャッシュミス -> 抽出 -> 保存    logger.info("Cache miss: %s (hash=%s)", file_path.name, file_hash[:12])    doc = extract_text(file_path)    entry = CacheEntry(file_hash=file_hash, source_path=str(file_path), document=doc)    write_cache(cache_dir, entry)    return doc

主要な設計上の決定

決定根拠
SHA-256コンテンツハッシュパス非依存、コンテンツ変更で自動無効化
{hash}.jsonファイル命名O(1)検索、インデックスファイル不要
サービスレイヤーラッパーSRP: 抽出は純粋に保ち、キャッシュは別の関心事
手動JSONシリアル化凍結データクラスのシリアル化を完全制御
破損はNoneを返すグレースフルデグラデーション、次回の実行で再処理
cache_dir.mkdir(parents=True)最初の書き込み時に遅延ディレクトリ作成

ベストプラクティス

  • パスではなくコンテンツをハッシュ — パスは変わるが、コンテンツのアイデンティティは変わらない
  • 大きなファイルはチャンク処理でハッシュ — ファイル全体をメモリに読み込まないようにする
  • 処理関数を純粋に保つ — キャッシュについて何も知らないようにする
  • 切り捨てたハッシュでキャッシュヒット/ミスをログ記録 — デバッグのため
  • 破損をグレースフルに処理 — 無効なキャッシュエントリはミスとして扱い、クラッシュしない

避けるべきアンチパターン

python
# 悪い例: パスベースのキャッシュ(ファイルの移動/名前変更で壊れる)cache = {"/path/to/file.pdf": result}
# 悪い例: 処理関数内にキャッシュロジックを追加(SRP違反)def extract_text(path, *, cache_enabled=False, cache_dir=None):    if cache_enabled:  # この関数は今や2つの責任を持っている        ...
# 悪い例: ネストされた凍結データクラスでdataclasses.asdict()を使用# (複雑なネストされた型で問題を引き起こす可能性がある)data = dataclasses.asdict(entry)  # 代わりに手動シリアル化を使用

使用すべき場合

  • ファイル処理パイプライン(PDF解析、OCR、テキスト抽出、画像分析)
  • --cache/--no-cacheオプションが有益なCLIツール
  • 同じファイルが複数回にわたって現れるバッチ処理
  • 既存の純粋な関数を変更せずにキャッシュを追加する場合

使用すべきでない場合

  • 常に最新でなければならないデータ(リアルタイムフィード)
  • 非常に大きなキャッシュエントリ(代わりにストリーミングを検討)
  • ファイルコンテンツ以外のパラメータに依存する結果(例:異なる抽出設定)

來源與署名

來源:affaan-m/ECC位於docs/ja-JP/skills/content-hash-cache-pattern提交ef648e0

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架