✓=レビュー済 ○=未レビュー ⚠=Suspect(複数同時表示あり。IDクリックで詳細へ)
| グループ | REQ | ARCH | SPEC | TST | IMPL |
|---|---|---|---|---|---|
| DB | REQ003 ✓ キャッシュのメタデータ(関数名、入力ID、バージョン、有効期限等)をデータベースに永続化できること。 抽象インターフェースにより、DBやストレージなどのバック... | ARCH003 ✓ ## コンポーネント構成 ```mermaid classDiagram class TaskDBCore { <<Protocol>> ... | SPEC007 ✓ ## インターフェース ```python class SQLiteTaskDB(TaskDBBase): def __init__(self, db... | TST007 ✓ ## 目的 `SQLiteTaskDB` はキャッシュメタデータの永続化を担う中核コンポーネントであり、DB の不整合はキャッシュの消失や重複実行を引き起こす... | IMPL007 ✓ ## 実装概要 `SQLiteTaskDB` クラスが中核。Writer Thread + Reader Threads パターンで実装。 単一の `_wri... |
| DB | REQ003 ✓ キャッシュのメタデータ(関数名、入力ID、バージョン、有効期限等)をデータベースに永続化できること。 抽象インターフェースにより、DBやストレージなどのバック... | ARCH003 ✓ ## コンポーネント構成 ```mermaid classDiagram class TaskDBCore { <<Protocol>> ... | SPEC008 ✓ ## インターフェース ```python @runtime_checkable class TaskDBCore(Protocol): def ge... | TST008 ✓ ## 目的 `TaskDBCore` / `TaskDBBase` のプロトコル階層は、サードパーティによるカスタムDB実装(Redis、PostgreSQL... | IMPL008 ✓ ## 実装概要 DB 層のプロトコル階層を定義。`TaskDBCore`(ランタイム必須の CRUD)、 `Flushable`(書き込み同期)、`Shutd... |
| リンク方向 | カバー数 | カバー率 | 未カバー |
|---|---|---|---|
| ARCH → REQ | 1 / 1 | 100.0% | — |
| SPEC → ARCH | 1 / 1 | 100.0% | — |
| TST → SPEC | 2 / 2 | 100.0% | — |
| IMPL → SPEC | 2 / 2 | 100.0% | — |
キャッシュのメタデータ(関数名、入力ID、バージョン、有効期限等)をデータベースに永続化できること。
抽象インターフェースにより、DBやストレージなどのバックエンド実装を差し替え可能であること。
親: —
子: ARCH003
classDiagram
class TaskDBCore {
<<Protocol>>
+init_schema() void
+get(cache_key) TaskRecord
+save(cache_key, ...) void
+delete(cache_key) bool
}
class Maintenable {
<<Protocol>>
+delete_expired() int
+prune(older_than) int
+delete_all() int
}
class TaskDBMaintenable {
<<Protocol>>
}
TaskDBCore <|-- TaskDBMaintenable
Maintenable <|-- TaskDBMaintenable
class SQLiteTaskDB {
-db_path: Path
-_write_queue: Queue
+init_schema() void
+get(cache_key) TaskRecord
+save(cache_key, ...) void
}
TaskDBMaintenable <|-- SQLiteTaskDB
| コンポーネント | 責務 | インターフェース |
|---|---|---|
TaskDBCore |
キャッシュ実行時に必要な最小限のメタデータDBアクセス | init_schema(), get(), save(), delete() |
Maintenable |
GCやCLI等、運用・保守に必要な拡張操作 | delete_expired(), prune(), delete_all() |
TaskDBMaintenable |
実行用と保守用の両方を備えた上位Protocol | TaskDBCore + Maintenable |
SQLiteTaskDB |
SQLiteを用いたデフォルト実装。非同期書き込みキューを内包 | TaskDBMaintenable 実装 |
sequenceDiagram
participant Spot as core.Spot
participant DB as SQLiteTaskDB
participant Q as WriteQueue
participant Thread as WriterThread
participant SQLite as SQLiteDB
Spot->>DB: get(cache_key)
DB->>SQLite: SELECT
SQLite-->>DB: TaskRecord
DB-->>Spot: TaskRecord
Spot->>DB: save(metadata)
DB->>Q: put(WriteTask)
DB-->>Spot: void (Non-blocking)
Thread->>Q: get()
Thread->>SQLite: INSERT / UPDATE
| 技術領域 | 選定 | 理由 |
|---|---|---|
| デフォルトメタデータDB | SQLite | ゼロ設定、組み込み可能、ローカルキャッシュとして十分な性能 |
| バックグラウンド書き込み | キュー + 専用スレッド | SQLiteの排他制御によるメインスレッドのブロックを防ぐため |
| 抽象化 | ProtocolベースのDI |
TaskDBCoreを満たせばPostgreSQL等に容易に差し替え可能 |
TaskDBCore, Maintenable) で定義され、利用側(core.Spotや_CacheManager)は実装に依存しない。親: REQ003
class SQLiteTaskDB(TaskDBBase):
def __init__(self, db_path: str | Path, timeout: float = 30.0): ...
def save(self, task: TaskRecord) -> None: ...
def get(self, input_key: str) -> TaskRecord | None: ...
_WriteTask キューに投入され、専用のバックグラウンドスレッドで直列に実行される。これにより SQLite の database is locked エラーを回避する。PRAGMA query_only = ON 状態で並行して実行される。tasks テーブルの存在を確認し、必要に応じて自動的に ALTER TABLE によるカラム追加(マイグレーション)を行う。| 設定 | デフォルト | 説明 |
|---|---|---|
journal_mode |
WAL |
書き込みと読み取りの並行性を高める設定 |
synchronous |
NORMAL |
パフォーマンスと耐久性のバランス設定 |
timeout |
30.0 |
データベースロック待機時間の閾値 |
sqlite3.Error をキャッチし、適切にラップして再送出する:memory: 指定時は、プロセスの生存期間中のみキャッシュが保持される親: ARCH003
@runtime_checkable
class TaskDBCore(Protocol):
def get(self, input_key: str) -> TaskRecord | None: ...
def save(self, record: TaskRecord) -> None: ...
class TaskDBBase(ABC):
# TaskDBCore を満たしつつ、メンテナンスのデフォルト実装を提供
| メソッド | 役割 |
|---|---|
get_blob_refs() |
全レコードの blob_key を列挙する(GC用) |
delete_expired() |
expires_at を経過したレコードを一括削除する |
get_history() |
実行履歴(統計)を取得する |
NotImplementedError を送出する代わりに、runtime_checkable による事前チェックを推奨する。TaskDBMaintenable 構成プロトコルとして扱う。親: ARCH003
SQLiteTaskDB はキャッシュメタデータの永続化を担う中核コンポーネントであり、DB の不整合はキャッシュの消失や重複実行を引き起こす。WALモード・専用ライタースレッドという独自アーキテクチャの信頼性を保証する。
親: SPEC007
子: —
TaskDBCore / TaskDBBase のプロトコル階層は、サードパーティによるカスタムDB実装(Redis、PostgreSQL 等)の差し替えを可能にする拡張ポイントである。インターフェース契約が不明確だと、カスタム実装が実行時に予期しないエラーを起こす。
TaskDBCore の必須メソッド(save, load, delete 等)を実装したクラスが isinstance チェックをパスすることTaskDBBase がメンテナンス系メソッド(prune, stats 等)のデフォルト実装を提供し、サブクラスが必須メソッドのみの実装で動作することbs.Spot(db=CustomDB()) でプロトコル準拠のカスタムDBが正常に動作すること親: SPEC008
子: —
SQLiteTaskDB クラスが中核。Writer Thread + Reader Threads パターンで実装。
単一の _writer_thread(デーモン)が _write_queue から書き込みタスクを取り出して
直列処理し、読み取りは threading.local() のスレッドローカル接続で並行実行する。
WAL モードにより読み取りと書き込みが並行可能。
SQLite は単一書き込み接続しかサポートしないため、全書き込みを1つのスレッドに 集約する Producer-Consumer パターンを採用。キュー経由でタスクを受け渡し、 ライタースレッドがコミット/ロールバックを管理する。 マルチスレッドからの並行書き込みによるロック競合を根本的に回避する。
threading.local() に _ReadConnWrapper を格納し、スレッドごとに
読み取り専用接続(PRAGMA query_only = ON)を保持する。
接続エラー時は自動的に再作成し、リーク防止のため WeakSet で全接続を追跡する。
書き込みタスクを PENDING → RUNNING → DONE(または CANCELLED)の
状態マシンで管理。タイムアウト時の try_cancel() により、
キュー内で待機中のタスクを安全にキャンセルできる。
既に RUNNING 状態のタスクはキャンセルせず完了を待つ。
PRAGMA table_info で既存カラムを確認し、
不足カラム(content_type, version, result_data, func_identifier, expires_at)を追加するget() 時に expires_at < NOW を確認し、期限切れなら None を返す。
物理削除は beautyspot gc コマンドに委譲するflush() は空の no-op タスクをキューに投入し、その完了を待つことで
先行する全書き込みの完了を保証するshutdown() は全読み取り接続のクローズ → _STOP センチネルの投入 →
ライタースレッドの join で安全に停止する_read_connect() のダブルチェックパターンで TOCTOU 競合を回避しているreferences: src/beautyspot/db.py
親: SPEC007
子: —
DB 層のプロトコル階層を定義。TaskDBCore(ランタイム必須の CRUD)、
Flushable(書き込み同期)、Shutdownable(安全な停止)、
Maintenable(GC・統計等の管理操作)の4つのプロトコルと、
それらを統合した TaskDBMaintenable を提供する。
TaskDBBase は ABC として Maintenable のデフォルト実装(安全な no-op)を提供する。
単一の巨大インターフェースではなく、責務ごとにプロトコルを分割した。
これにより、カスタム DB 実装は TaskDBCore のみ実装すればランタイムで動作し、
メンテナンス機能は段階的にオプトインできる。
TaskDBBase のメンテナンスメソッド(delete_expired(), prune() 等)は
デフォルトで空リスト返却や no-op を行い、サブクラスが未実装でも安全に動作する。
CLI の gc コマンドが未対応の DB バックエンドでもエラーにならない設計。
Flushable.flush(timeout) のデフォルトは no-op。SQLiteTaskDB のみがキュー同期を実装Shutdownable.shutdown(wait) のデフォルトは no-op。リソースを持たない軽量実装に配慮isinstance(db, Maintenable) で機能の有無を判定できるreferences: src/beautyspot/db.py
親: SPEC008
子: —