この記事のポイント

  • lru_cache導入時の設計責任をレビュー視点で読み取れる
  • キャッシュの責務整理と設計臭を整理できる
  • 実務でのキャッシュ運用文化を体系化できる
  • PlantUMLでキャッシュ適用範囲と責務依存を可視化整理できる

そもそもlru_cacheとは

Python標準のfunctools.lru_cache「引数による純粋関数キャッシュ」を提供します。

  • 関数呼び出し結果をメモリ内に保存
  • 同一引数呼び出し時にキャッシュ利用
  • 引数のハッシュ可能性が前提条件
  • 最大保持件数(maxsize)指定可能
lru_cache基本例

from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)
  • 再帰計算の負荷軽減
  • 計算結果の使い回し最適化

なぜこれをレビューするのか

lru_cacheは「副作用が少ない時だけ安全」という条件付きの道具です。
レビューアーは以下の整理視点を持つ必要があります。

レビューアー視点

  • キャッシュ導入対象が純粋関数責務か確認
  • 副作用・外部依存が隠蔽されていないか
  • キャッシュサイズ設計が妥当か
  • キャッシュ生存時間制御が意識されているか
  • 更新・無効化設計が明示されているか

開発者視点

  • 手軽なのでつい副作用含みの関数にも付与しがち
  • 外部API呼出に安易にlru_cache適用
  • 無限maxsize指定
  • 無効化制御を後回しにしがち

lru_cache崩壊の典型設計臭

崩壊しやすいパターン
  • DB/API呼出系にlru_cache適用
  • 内部状態依存を含む関数をキャッシュ
  • 状態変化後も古いキャッシュ使用
  • キャッシュ肥大によるメモリ圧迫
  • 無効化手段不備で障害時影響拡大

崩壊例:外部API誤用型

外部APIキャッシュ崩壊例

from functools import lru_cache

@lru_cache(maxsize=1000)
def get_exchange_rate():
    response = requests.get("https://api.exchangeratesapi.io/latest")
    return response.json()
@Reviewer
外部APIレスポンスがキャッシュ固定化されています。状態揮発性を踏まえ適用対象を見直してください。

問題点

  • レート更新されても古い値固定
  • 状態変化に追従不能

崩壊構造モデル:副作用隠蔽型

UML Diagram

lru_cache適用整理の原則

以下の責務整理文化が適用対象選定に有効です。

① 純粋関数限定文化

  • 入力→出力が完全決定
  • 外部状態参照ゼロ

② 計算コスト優先文化

  • 計算量高 → キャッシュ価値高
  • IO依存系は別制御優先

③ maxsize設計文化

  • 惰性的無限指定禁止
  • 典型: 32, 128, 1024など段階的見積

④ 生存期間分離文化

  • 定常キャッシュ vs 一時キャッシュ設計分離

⑤ 無効化責務設計文化

  • cache_clear()設計責務の意識化

改善例:純粋計算限定版

純粋計算版

from functools import lru_cache

@lru_cache(maxsize=256)
def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n-1)
「副作用ゼロ文化+maxsize設計文化」

レビューアーは「外部依存ゼロを確認文化」を徹底確認すると崩壊抑止できます。

改善構造モデル:純粋責務整理

UML Diagram

良くない実装例: ケース1(内部状態依存型)

内部状態依存例

counter = 0

@lru_cache(maxsize=128)
def get_and_increment():
    global counter
    counter += 1
    return counter
@Reviewer
状態更新含む関数にlru_cacheが適用されています。純粋計算関数に限定してください。

問題点

  • 結果が状態依存
  • キャッシュ固定で意図逸脱

良くない実装例: ケース2(無効化不能型)

無効化不能例

@lru_cache()
def compute_heavy(value):
    # 計算負荷高い処理
    return heavy_process(value)
@Reviewer
cache_clear呼出設計が欠落しています。障害復旧手段を設計段階で整理してください。

問題点

  • 再計算不能化リスク
  • 障害調査時に困難発生

改善例:無効化設計整理版

無効化整理版

@lru_cache(maxsize=128)
def compute_heavy(value):
    return heavy_process(value)

def refresh_cache():
    compute_heavy.cache_clear()
  • 明示的無効化設計
  • 障害時の運用対応性向上

良くない実装例: ケース3(全件展開肥大)

肥大化例

@lru_cache(maxsize=None)
def complex_combination(a, b, c, d):
    # 組合せ爆発
    return calculate(a, b, c, d)
@Reviewer
maxsize=None指定で肥大化リスクが高まります。容量見積と上限設定を整理してください。

問題点

  • 無限展開によるメモリ逼迫
  • 実行長期後に性能劣化

改善例:maxsize見積整理

maxsize見積版

@lru_cache(maxsize=1024)
def complex_combination(a, b, c, d):
    return calculate(a, b, c, d)
  • 実使用想定件数で容量設計

観点チェックリスト

まとめ

lru_cache設計は「副作用設計臭の見極め訓練」そのものです。
レビューアーは「純粋関数文化・maxsize設計文化・無効化設計文化」を育成指導することで、
運用トラブル耐性の高いキャッシュ設計を実現できます。