Python|lru_cacheのキャッシュ導入設計の注意点
この記事のポイント
- 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レスポンスがキャッシュ固定化されています。状態揮発性を踏まえ適用対象を見直してください。
問題点
- レート更新されても古い値固定
- 状態変化に追従不能
崩壊構造モデル:副作用隠蔽型
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設計文化」
レビューアーは「外部依存ゼロを確認文化」を徹底確認すると崩壊抑止できます。
改善構造モデル:純粋責務整理
良くない実装例: ケース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)
@Reviewercache_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)
@Reviewermaxsize=None指定で肥大化リスクが高まります。容量見積と上限設定を整理してください。
問題点
- 無限展開によるメモリ逼迫
- 実行長期後に性能劣化
改善例:maxsize見積整理
maxsize見積版
@lru_cache(maxsize=1024)
def complex_combination(a, b, c, d):
return calculate(a, b, c, d)- 実使用想定件数で容量設計
観点チェックリスト
まとめ
lru_cache設計は「副作用設計臭の見極め訓練」そのものです。
レビューアーは「純粋関数文化・maxsize設計文化・無効化設計文化」を育成指導することで、
運用トラブル耐性の高いキャッシュ設計を実現できます。

