# 31. 非同期保存処理におけるエラー可視化とコンテキストの導入

Date: 2026-02-20

## ステータス

Accepted

## 背景と課題

beautyspotでは、関数の実行結果をキャッシュに保存する際、`wait=False` を指定することでバックグラウンドスレッドでの非同期保存 (`_save_result_safe`) を行うことができます。これにより、メインのアプリケーションロジック（特にWebリクエストのレスポンスなど）をブロックせずにパフォーマンスを向上させることが可能です。

しかし、バックグラウンドでの保存処理中にシリアライズエラーやストレージへの書き込みエラーが発生した場合、これまでは `logger.error` によってログが出力されるのみ（サイレントフェイル）でした。メインスレッドをクラッシュさせないという点では安全ですが、ユーザー（開発者）からすると「なぜかキャッシュが効かない」という原因究明が困難な状態に陥るリスクがありました。

また、本番環境においてSentryやDatadogなどの監視ツールへエラーを通知したり、カスタムのリカバリ処理を行ったりするためのフック（介入ポイント）が存在しませんでした。

## 提案された選択肢

1. **現状維持 (ログ出力のみ):** メイン処理への影響を完全に排除できるが、可観測性（オブザーバビリティ）が低い。
2. **メインスレッドへ例外を伝播させる:** 非同期処理のメリットを打ち消してしまい、アプリケーションの安定性を損なうため不採用。
3. **コールバック関数 (`on_background_error`) の導入:** `Spot` クラスの初期化時に、エラー時に発火する任意のコールバック関数を受け付ける。

## 決定事項

選択肢3（コールバック関数の導入）を採用し、以下の設計方針で実装します。

1. **`on_background_error` 引数の追加:**
   `Spot.__init__` に `on_background_error: Optional[Callable[[Exception, SaveErrorContext], None]]` を追加します。
2. **型安全なコンテキストオブジェクト (`SaveErrorContext`) の導入:**
   エラーハンドラーには、単なる `kwargs` の辞書ではなく、`dataclasses` を用いた専用の `SaveErrorContext` を渡します。これにより、IDEでの型補完が有効になり、開発者体験（DX）が大きく向上します。
3. **`result` (評価済みオブジェクト) の包含と警告:**
   デバッグを容易にするため、`SaveErrorContext` にはキャッシュしようとした実際の戻り値 (`result: Any`) を含めます。ただし、巨大なデータ（DataFrameなど）の参照をユーザーが長期間保持してメモリリークを引き起こすリスクがあるため、Docstringにて明確な警告（一時的な用途に留めること）を記載します。
4. **コールバック内の例外保護:**
   ユーザーが定義したコールバック関数内で予期せぬ例外が発生した場合でも、バックグラウンドスレッドがクラッシュしないよう、内部で `try-except` で保護し、エラーログとして記録します。

## 結果

* **オブザーバビリティの向上:** ユーザーは、バックグラウンドでのキャッシュ保存の失敗を確実に検知し、任意の監視システムへ通知できるようになります。
* **高い開発者体験:** `SaveErrorContext` データクラスの恩恵により、直感的かつ安全にエラーコンテキスト（対象の関数名、キャッシュキー、オブジェクト等）へアクセスできます。
* **安全性の維持:** コールバック機構を導入しても、メインのアプリケーションロジックのパフォーマンスや安定性には影響を与えません。
