active: true
derived: false
gherkin: |
  Feature: SQLiteTaskDB
    Scenario: Managing cache tasks in a SQLite database
      Given a SQLiteTaskDB instance with a configured timeout
      When a background thread writes to the database and readers query concurrently
      Then data is safely stored without locking errors and queries execute concurrently
groups:
- DB
header: |
  SQLiteTaskDB
level: 2.7
links:
- ARCH003: 2KJqTKqCFN5mQa7-RMJPnJyDRcoDbYEP3VEEldkVXMY=
normative: true
ref: ''
reviewed: 88A_UWCTo2UywsFiVDo9S2kXZa3mKn9tE9phw-M4tCY=
text: |
  ## インターフェース

  ```python
  class SQLiteTaskDB(TaskDBBase):
      def __init__(self, db_path: str | Path, timeout: float = 30.0): ...
      def save(self, task: TaskRecord) -> None: ...
      def get(self, cache_key: str, *, include_expired: bool = False) -> TaskRecord | None: ...
  ```

  ## 振る舞い

  ### ライタースレッド方式
  - 書き込み操作は全て `_WriteTask` キューに投入され、専用のバックグラウンドスレッドで直列に実行される。これにより SQLite の `database is locked` エラーを回避する。

  ### 読み取り並行性
  - 読み取りはスレッドローカルな接続を使用し、`PRAGMA query_only = ON` 状態で並行して実行される。

  ### スキーマ管理
  - コンストラクタ (`__init__`) でスキーマ初期化を完了し、構築後のインスタンスは即座に使用可能な状態とする（RAII）。
  - `init_schema()` は冪等であり、複数回呼び出しても安全である。
  - `tasks` テーブルの存在を確認し、必要に応じて自動的に `ALTER TABLE` によるカラム追加（マイグレーション）を行う。

  ### タイムアウトとフェイルファスト
  - 全ての書き込み操作は `timeout` 秒以内に完了する必要がある。
  - **PENDING（未着手）状態**: `timeout` を超えた場合、タスクはキューから破棄（キャンセル）され、呼び出し元に `TimeoutError` を返す。
  - **RUNNING（実行中）状態**: `timeout` を超えた場合、`sqlite3.Connection.interrupt()` を用いて実行中のクエリを中断し、呼び出し元に `TimeoutError` を返す。これにより、ライタースレッドの恒久的なハングを防ぎ、システムの可用性を維持する。

  ## パラメータ詳細

  | 設定 | デフォルト | 説明 |
  |---|---|---|
  | `journal_mode` | `WAL` | 書き込みと読み取りの並行性を高める設定 |
  | `synchronous` | `NORMAL` | パフォーマンスと耐久性のバランス設定 |
  | `timeout` | `30.0` | データベースロック待機およびクエリ実行のタイムアウト閾値 |

  ## エラーハンドリング

  - **タイムアウト**: `TimeoutError` を送出する。
  - **接続失敗**: `sqlite3.Error` をキャッチし、適切にラップして再送出する。
  - **シャットダウン時**: ライタースレッドへの新規タスク投入を停止し、保留中のタスクを完了させてから接続を閉じる。

  ## エッジケース

  - **破損DB**: DBファイルが壊れている場合は、初期化時に検知しエラーとする。
  - **インメモリDB**: `:memory:` 指定時は、プロセスの生存期間中のみキャッシュが保持される。

  ## 設計判断

  デフォルトのメタデータ保存先として、追加のサーバー構築が不要で、かつ高速なSQLiteを使用するため。WALモードなどを駆使して並行書き込み性能を確保する具体的な実装方針を定義した。
