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 从公开仓库中收录这些内容。

举报或申请下架