✓=レビュー済 ○=未レビュー ⚠=Suspect(複数同時表示あり。IDクリックで詳細へ)
| グループ | REQ | ARCH | SPEC | TST | IMPL |
|---|---|---|---|---|---|
| BGIO | REQ006 ✓ キャッシュの保存処理をバックグラウンドで非同期に実行し、関数の応答レイテンシに影響を与えないモードを提供すること。 | ARCH006 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|非同期タスク投入| B[_BackgroundLoop... | SPEC014 ✓ ## インターフェース ```python class _BackgroundLoop: def submit(self, coro: Corouti... | TST014 ✓ ## 目的 `_BackgroundLoop` はデーモンスレッドで asyncio イベントループを駆動し、非同期キャッシュ保存を処理する。スレッド間のコル... | IMPL014 ✓ ## 実装概要 `_BackgroundLoop` クラスが非同期タスクのバックグラウンド実行を管理する。 初期化時に `asyncio.new_event_... |
| BGIO | REQ006 ✓ キャッシュの保存処理をバックグラウンドで非同期に実行し、関数の応答レイテンシに影響を与えないモードを提供すること。 | ARCH006 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|非同期タスク投入| B[_BackgroundLoop... | SPEC015 ✓ ## インターフェース ```python def flush(timeout: float | None = None) -> bool: ... @sp... | TST015 ✓ ## 目的 `save_sync` パラメータと `flush` / `drain` メカニズムは、キャッシュ保存のレイテンシとデータ安全性のトレードオフをユ... | IMPL015 ✓ ## 実装概要 `Spot` クラスにおいて、バックグラウンド書き込みの同期と完了待機を制御する。 `save_sync=False` の場合、キャッシュへの... |
| リンク方向 | カバー数 | カバー率 | 未カバー |
|---|---|---|---|
| ARCH → REQ | 1 / 1 | 100.0% | — |
| SPEC → ARCH | 1 / 1 | 100.0% | — |
| TST → SPEC | 2 / 2 | 100.0% | — |
| IMPL → SPEC | 2 / 2 | 100.0% | — |
graph TD
A[core.Spot] -->|非同期タスク投入| B[_BackgroundLoop]
B -->|Daemon Thread| C[asyncio.AbstractEventLoop]
C -->|I/O| D[TaskDB / BlobStorage]
A -->|ライフサイクル制御| E[atexit / ContextManager]
| コンポーネント | 責務 | インターフェース |
|---|---|---|
| _BackgroundLoop | デーモンスレッドでのイベントループ管理とタスク投入 | submit(), run_forever() |
| core.Spot | 非同期保存のトリガーとエラーハンドリング | _save_metadata_async() |
| ContextManager | 処理終了時のタスク完了待機(Flush) | __exit__, flush() |
sequenceDiagram
participant Main as メインスレッド
participant BGLoop as _BackgroundLoop
participant Store as ストレージ
Main->>BGLoop: submit(save_coro)
Note over Main: 関数は即座に結果を返す (save_sync=False)
BGLoop->>Store: 書き込み実行 (I/O)
alt 成功
Store-->>BGLoop: OK
else 失敗
BGLoop->>Main: on_background_error(error)
end
| 技術領域 | 選定 | 理由 |
|---|---|---|
| 並行処理 | asyncio in Thread | GILを解放しつつ、多数のI/Oタスクを効率的に多重化 |
| スレッド種類 | Daemon Thread | アプリケーション終了時にプロセスをブロックしない |
| 終了制御 | atexit / drain | 強制終了時も、可能な限り保留中の書き込みを完了させる |
透過性: 保存失敗時もユーザーコードの例外にはせず、コールバック経由で通知
一貫性: flush() により、クリティカルな処理の直後での保存完了を保証
安全性: シャットダウンシーケンス中に新規タスクを受け付けない排他制御を導入
親: REQ006
class _BackgroundLoop:
def submit(self, coro: Coroutine) -> None: ...
def stop(self) -> None: ...
def is_running(self) -> bool: ...
submit 時にデーモンスレッドを作成し、asyncio.run() を開始するrun_coroutine_threadsafe を使用して、メインスレッドからコルーチンをイベントループへ投入| 内部属性 | 型 | 説明 |
|---|---|---|
_loop |
AbstractEventLoop |
スレッド内で稼働するループ本体 |
_thread |
Thread |
daemon=True 設定のスレッド |
on_background_error コールバックで処理される親: ARCH006
def flush(timeout: float | None = None) -> bool: ...
@spot.mark(save_sync=False)
def my_func(): ...
save_sync=True)save_sync=False)BackgroundLoop に委譲し、即座に関数の戻り値を返す。flush)_inflight タスク(保存中)のリストを確認し、それら全てが完了するまで、またはタイムアウトまで待機する。| パラメータ | デフォルト | 説明 |
|---|---|---|
timeout |
None |
flush の最大待機時間(秒)。None は無限待機 |
on_background_error |
logger.error |
非同期保存失敗時のエラーハンドラ |
flush がタイムアウトした場合、False を返し、一部のデータが未保存である可能性を通知する。with spot: 終了時に自動的に flush が呼ばれ、プロセス終了前のデータ保存を確実にする。親: ARCH006
_BackgroundLoop はデーモンスレッドで asyncio イベントループを駆動し、非同期キャッシュ保存を処理する。スレッド間のコルーチン受け渡しは競合状態やデッドロックが発生しやすく、シャットダウン時のタスク消失はデータロスに直結する。
submit(coro) で投入したコルーチンが正しく実行され、結果がDBとストレージに反映されることdrain_timeout 秒以内に完了すること。タイムアウト後は強制終了されることsubmit() 呼び出しが受け付けられないこと親: SPEC014
子: —
save_sync パラメータと flush / drain メカニズムは、キャッシュ保存のレイテンシとデータ安全性のトレードオフをユーザーが制御する手段である。save_sync=False 使用時にデータが失われないことと、コンテキストマネージャによる確実なフラッシュを保証する。
spot.flush(timeout) 呼び出しで全ペンディング保存が完了するまで待機すること。タイムアウト時の挙動が明確であることwith spot: ブロック終了時に未完了の保存が自動的に drain されることwith ブロックで再利用でき、各ブロック終了時に適切に drain されることon_background_error コールバックが呼ばれ、メインスレッドには影響しないこと親: SPEC015
子: —
_BackgroundLoop クラスが非同期タスクのバックグラウンド実行を管理する。
初期化時に asyncio.new_event_loop() で新しいイベントループを作成し、
_thread(デーモンスレッド)上で loop.run_forever() を実行する。
外部からは submit() を通じてコルーチンを投入でき、run_coroutine_threadsafe で
スレッドセーフにループへ渡される。
スレッドは daemon=True とし、メインスレッド終了時にプロセスがブロックされないようにしている。
しかし、安全なリソース解放のために drain() メソッドを提供し、
投入された全タスクの完了を drain_timeout の範囲で待機する。
メインスレッドのイベントループと競合しないよう、専用のイベントループを
作成してバックグラウンドスレッドで駆動する。これにより save_sync=False 時の
保存処理などが、呼び出し元の asyncio 環境から完全に隔離される。
submit() は投入されたタスクを _tasks 集合に登録し、add_done_callback で
完了時に自身を削除することで、未完了タスクの追跡を可能にしている_lock による排他制御を行っているatexit ハンドラとしてもシャットダウンが登録されるreferences: src/beautyspot/core.py
親: SPEC014
子: —
Spot クラスにおいて、バックグラウンド書き込みの同期と完了待機を制御する。
save_sync=False の場合、キャッシュへの保存処理は _bg_loop.submit() で
バックグラウンドに投入される。これら未完了のタスクやDBキューを同期するため、
flush() およびコンテキストマネージャによる drain が実装されている。
flush(): DBライタースレッドのキュー (self.db.flush()) を空になるまで待機する__exit__: コンテキストマネージャ終了時に flush() を呼び出した上で、
さらに _bg_loop.drain() を呼び出し、非同期タスクの完了も待つ
これにより、スクリプト終了時やバッチ処理の区切りで、データの完全な永続化を保証する。バックグラウンド保存時のエラーは呼び出し元のメインフローを妨げないよう、
on_background_error コールバックに通知され、ログ出力のみ行う。
例外の再送出は行わない。
flush() にはタイムアウトを設定でき、ハングアップを防止しているon_background_error は SaveErrorContext を引数に取り、失敗時の
キーや関数の詳細情報を提供するreferences: src/beautyspot/core.py
親: SPEC015
子: —