ARCH - Document View

# ARCH Specification

Generated at 2026-03-09 22:49:07

## 1.0 序文 ARCH014

本ドキュメントは、『beautyspot』のアーキテクチャ設計を定義する。

## 2.0 構成要素 ARCH015

システムを構成する主要コンポーネントとその責務、およびコンポーネント間の相互作用を定義する。

2.1 関数キャッシュアーキテクチャ ARCH001
CACHE

## コンポーネント構成

    graph TD
      A[bs.Spot Factory] -->|DI/Composition| B[core.Spot]
      B --> C[TaskDBBase]
      B --> D[SerializerProtocol]
      B --> E[BlobStorageBase]
      B --> F[StoragePolicyProtocol]
      B --> G[LimiterProtocol]
      B --> H[LifecyclePolicy]

## コンポーネント責務

コンポーネント | 責務 | インターフェース
--- | --- | ---
core.Spot | キャッシュエンジン。キー生成→検索→実行→保存の制御 | `mark()`, `cached_run()`
TaskDBBase | メタデータ永続化。キャッシュキーによる検索と保存 | `find_by_key()`, `insert()`
SerializerProtocol | データのシリアライズ/デシリアライズ | `pack()`, `unpack()`
BlobStorageBase | 大規模データの外部ストレージ保存 | `save()`, `load()`, `delete()`
StoragePolicyProtocol | Blob保存の判定ポリシー | `should_save_as_blob()`

## データフロー

    sequenceDiagram
      participant User as ユーザーコード
      participant Spot as core.Spot
      participant DB as TaskDB
      participant Ser as Serializer
      participant Blob as BlobStorage

      User->>Spot: fn(args)
      Spot->>DB: find_by_key(cache_key)
      alt キャッシュヒット
        DB-->>Spot: cached_data
        Spot->>Ser: unpack(data)
      else キャッシュミス
        Spot->>Spot: fn(args) 実行
        Spot->>Ser: pack(result)
        Spot->>DB: insert(metadata)
        opt Blob保存
          Spot->>Blob: save(key, data)
        end
      end
      Spot-->>User: result

## 技術選定

技術領域 | 選定 | 理由
--- | --- | ---
メタデータDB | SQLite | ゼロ設定、組み込み可能、十分な性能
シリアライズ | MessagePack | JSONより高速・コンパクト、バイナリ対応
ストレージ | ローカルファイル / クラウド | 小規模はローカル、大規模は外部ストレージ等で拡張可能

## 非機能要件方針

- 透過性: ユーザー関数の入出力に影響を与えないこと。
- 拡張性: 全コンポーネントはProtocol/ABCで抽象化し、DIで差し替え可能とすること。

2.2 キャッシュキー生成アーキテクチャ ARCH002
KEY

## コンポーネント構成

    graph TD
      A[core.Spot] -->|キャッシュキー生成要求| B[KeyGenPolicy]
      B -->|バインド| C[bound_keygen]
      C -->|引数別戦略| D[Strategy]
      C -->|正規化| E[canonicalize]
      C -->|ハッシュ計算| F[KeyGen.hash_items]
      E --> F

## コンポーネント責務

コンポーネント | 責務 | インターフェース
--- | --- | ---
`KeyGenPolicy` | 関数の引数に対するハッシュ化戦略（無視、ファイル内容等）の宣言的定義と保持。 | `bind(func)`
`Strategy` | 引数ごとの処理方法（DEFAULT, IGNORE, FILE_CONTENT, PATH_STAT）の定義。 | -
`canonicalize` | Pythonオブジェクトを再帰的に正規化し、Msgpackで安定してシリアライズ可能な状態に変換。 | `@singledispatch canonicalize(obj)`
`KeyGen` | 正規化されたリストからSHA-256ハッシュを生成。レガシーデフォルトのハッシュ生成も担う。 | `hash_items(items)`, `from_file_content(path)`

## データフロー

    sequenceDiagram
      participant Spot as core.Spot
      participant KP as KeyGenPolicy
      participant Sig as inspect.signature
      participant Can as canonicalize
      participant KG as KeyGen

      Spot->>KP: bind(func)
      KP->>Sig: signature(func)
      Sig-->>KP: bound_keygen 生成
      KP-->>Spot: bound_keygen
      Spot->>Spot: bound_keygen(*args, **kwargs)
      loop 各引数
        alt Strategy.IGNORE
          Spot->>Spot: スキップ
        else Strategy.FILE_CONTENT
          Spot->>KG: from_file_content(path)
        else Strategy.PATH_STAT
          Spot->>KG: from_path_stat(path)
        else Strategy.DEFAULT
          Spot->>Can: canonicalize(val)
          Can-->>Spot: 正規化済みデータ
        end
      end
      Spot->>KG: hash_items(items_to_hash)
      KG-->>Spot: SHA-256 ハッシュ文字列 (キャッシュキー)

## 技術選定

技術領域 | 選定 | 理由
--- | --- | ---
引数のバインド | `inspect.signature` | argsとkwargsを定義順に正確にマッピングし、デフォルト値も適用するため
正規化 | `functools.singledispatch` | 型ごとの正規化ロジックを拡張可能かつクリーンに実装するため
シリアライズ | `msgpack` | 高速でバイナリセーフであり、一貫したバイト列表現を得るため
ハッシュ関数 | SHA-256 | 衝突確率が極めて低く、暗号学的に安全かつ広くサポートされているため

## 非機能要件方針

- 安定性: プロセスの再起動やPythonのバージョン（辞書の順序等）に依存せず、同一入力に対して常に同一のハッシュを生成すること。
- 性能: 複雑なオブジェクト構造でも高速に正規化とハッシュ計算を行えること。ファイル内容のハッシュ化はチャンク読み込みでメモリ効率を確保する。
- 拡張性: ユーザーが独自の `KeyGenPolicy` を定義可能にし、柔軟なキャッシュ戦略をサポートする。

2.3 メタデータDBアーキテクチャ ARCH003
DB

## コンポーネント構成

    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等に容易に差し替え可能

## 非機能要件方針

- 性能: 読み込みはメインスレッドで高速に処理（WALモード）、書き込みは専用スレッドによる非同期化。
- スレッド安全性: SQLiteへの書き込みは1つのスレッドに直列化し、データベースロックを回避する。
- 拡張性: すべての操作はProtocol (`TaskDBCore`, `Maintenable`) で定義され、利用側（`core.Spot`や`_CacheManager`）は実装に依存しない。

2.4 Blobストレージ構成 ARCH004
STORE

## コンポーネント構成

    graph TD
      A[core.Spot] -->|判定| B[StoragePolicyProtocol];
      A -->|保存/読込| C[BlobStorageBase];
      D[LocalStorage] --> C;
      E[S3Storage] --> C;
      A -->|メタデータ| F[TaskDBBase];

## コンポーネント責務

コンポーネント | 責務 | インターフェース
--- | --- | ---
StoragePolicyProtocol | データのサイズ等に基づき、DB保存かBlob保存かを判定する | `should_save_as_blob()`
BlobStorageBase | 大規模データの外部ストレージ保存を抽象化する | `save()`, `load()`, `delete()`
LocalStorage | ローカルファイルシステムへのアトミックな保存と検証 | `base_dir`, `os.replace`
S3Storage | AWS S3互換オブジェクトストレージへの保存 | `s3://` URI, `boto3`

## データフロー

    sequenceDiagram
      participant Spot as core.Spot
      participant Policy as StoragePolicy
      participant DB as TaskDB
      participant Blob as BlobStorage

      Spot->>Policy: should_save_as_blob(data)
      alt Policy: True (Blob保存)
        Spot->>Blob: save(key, data)
        Blob-->>Spot: location (path/URI)
        Spot->>DB: insert(metadata, storage_type=FILE, blob_key=location)
      else Policy: False (Inline保存)
        Spot->>DB: insert(metadata, storage_type=DIRECT_BLOB, result_data=data)
      end

## 技術選定

技術領域 | 選定 | 理由
--- | --- | ---
保存判定 | 閾値ベース (Threshold) | DBの肥大化を防ぎつつ、小容量データのI/Oを最適化
アトミック性 | 一時ファイル + Rename | 保存中のクラッシュや並行書き込みによる破損を完全に防止
クラウド対応 | Boto3 (S3) | 業界標準のプロトコルによる高い互換性とスケーラビリティ

## 非機能要件方針

- 安全性: LocalStorage では `is_relative_to` によるパストラバーサル防止を徹底

- 信頼性: 保存時は `fsync` を実行し、OSレベルでのデータ到達を確認

- 効率性: S3 保存時は `upload_fileobj` を使用し、5GB超のマルチパートアップロードに自動対応

2.5 シリアライゼーション構成 ARCH005
SERIAL

## コンポーネント構成

    graph TD
      A[MsgpackSerializer] -->|Registry| B[TypeRegistryProtocol]
      A -->|LRU Cache| C[Thread-Local Storage]
      A -->|Core| D[msgpack-python]

## コンポーネント責務

コンポーネント | 責務 | インターフェース
--- | --- | ---
MsgpackSerializer | MessagePackベースの高速・コンパクトなバイナリ変換 | `dumps()`, `loads()`
TypeRegistryProtocol | ユーザー定義型（ExtType）のエンコーダ・デコーダ登録 | `register()`
Thread-Local Cache | free-threading環境でのロック競合を回避するMRO解決キャッシュ | `OrderedDict` (LRU)

## データフロー

    sequenceDiagram
      participant User as ユーザーコード
      participant Ser as MsgpackSerializer
      participant Reg as TypeRegistry
      participant MP as msgpack

      User->>Ser: dumps(obj)
      Ser->>Ser: 型チェック & MRO解決
      opt カスタム型
        Ser->>Reg: エンコーダ取得
        Reg-->>Ser: encoder_fn
        Ser->>MP: ExtType(code, encoder_fn(obj))
      end
      MP-->>Ser: binary
      Ser-->>User: bytes

## 技術選定

技術領域 | 選定 | 理由
--- | --- | ---
フォーマット | MessagePack | JSONより高速・コンパクト。バイナリをネイティブにサポート
スレッド安全 | Copy-on-Write (CoW) | 読み取りパスからロックを排除し、マルチスレッド性能を最大化
型拡張 | ExtType (0-127) | カスタム型を1バイトのオーバーヘッドで表現可能

## 非機能要件方針

- 性能: MRO（継承関係）解決結果をLRUキャッシュし、深層木探索のコストを最小化
- 柔軟性: ユーザー定義のエンコーダがプリミティブを返せば、再帰的にシリアライズ可能
- 堅牢性: デコード時の `ExtCode` 未登録エラーを検知し、互換性問題を明示的に報告

2.6 バックグラウンドIO構成 ARCH006
BGIO

## コンポーネント構成

    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()` により、クリティカルな処理の直後での保存完了を保証

- 安全性: シャットダウンシーケンス中に新規タスクを受け付けない排他制御を導入

2.7 ライフサイクル管理構成 ARCH007
LIFE

## コンポーネント構成

    graph TD
      A[core.Spot] -->|マッチング要求| B[LifecyclePolicy]
      B -->|ルールリスト| C[Rule]
      C -->|パターン照合| D[fnmatch]
      B -->|パース| E[Retention]

## コンポーネント責務

コンポーネント | 責務 | インターフェース
--- | --- | ---
LifecyclePolicy | 複数のルールを管理し、関数名に最適な有効期限を決定する | `resolve_with_fallback()`
Rule | 1つのパターンとそれに対応する保持期間のペア | `match(func_name)`
Retention | 多様な時間表現（文字列/数値）を秒数に正規化する | `parse_retention()`

## データフロー

    sequenceDiagram
      participant Spot as core.Spot
      participant Policy as LifecyclePolicy
      participant DB as TaskDB

      Spot->>Policy: resolve_with_fallback("module.func")
      Policy->>Policy: ルール順次照合 (fnmatch)
      Policy-->>Spot: expires_at (timestamp)
      Spot->>DB: save(..., expires_at)
      Note over DB: get() 時に現在時刻と比較判定

## 技術選定

技術領域 | 選定 | 理由
--- | --- | ---
パターンマッチ | fnmatch (Glob) | 正規表現よりも直感的で、開発者にとって馴染みのある指定方法
保持期間指定 | 文字列パーサー ("30d"等) | 設定ファイルやデコレータでの可読性を向上
判定タイミング | 遅延判定 (Lazy) | 書き込み・読み込み時のオーバーヘッドを最小化

## 非機能要件方針

- 優先順位: 完全修飾名（モジュール名込み）が短縮名より優先される二段構えのマッチング
- 堅牢性: ポリシーにマッチしない場合は「無期限」とするフェイルセーフな設計
- 拡張性: `Retention.FOREVER` 等の定数により、グローバルポリシーの個別上書きに対応

2.8 レート制限構成 ARCH008
LIMIT

## コンポーネント構成

    graph TD
      A["@spot.consume"] -->|宣言的適用| B[LimiterProtocol]
      C[core.Spot] -->|DI| B
      D[TokenBucket] --> B
      D -->|アルゴリズム| E[GCRA]
      D -->|時刻同期| F[monotonic clock]

## コンポーネント責務

コンポーネント | 責務 | インターフェース
--- | --- | ---
TokenBucket | GCRAアルゴリズムによるスムースなトラフィック制御 | `consume()`, `consume_async()`
@spot.consume | 関数実行前のトークン消費を透過的に行うデコレータ | `cost` (int or callable)
LimiterProtocol | レートリミッターの差し替えを可能にするインターフェース | `LimiterProtocol`

## データフロー

    sequenceDiagram
      participant User as ユーザーコード
      participant Dec as @spot.consume
      participant Bucket as TokenBucket

      User->>Dec: call func()
      Dec->>Bucket: consume(cost)
      Bucket->>Bucket: 次回実行許可時刻の計算 (GCRA)
      alt 許可
          Bucket-->>Dec: OK
          Dec->>User: execute func()
      else 拒否 (Over rate)
          Bucket-->>Dec: raise ValueError/RateLimitError
      end

## 技術選定

技術領域 | 選定 | 理由
--- | --- | ---
アルゴリズム | GCRA (Generic Cell Rate Algorithm) | 固定ウィンドウと違い「スムースな」流量制御が可能
待機制御 | asyncio.sleep / time.sleep | 同期・非同期の両方のコンテキストで正確なスロットリングを実現
コスト計算 | ダイナミックコスト | 引数に応じて消費トークン数を動的に変更可能

## 非機能要件方針

- 正確性: `time.monotonic()` を使用し、システム時刻の変更（NTP同期等）の影響を受けない

- バースト制御: `max_burst` 設定により、アイドル後の過度なバーストを防止

- スレッド安全: `threading.Lock` により、マルチスレッド環境での二重消費を防止

2.9 フック機能構成 ARCH009
HOOK

## コンポーネント構成

    graph TD
      A[core.Spot] -->|イベント通知| B[HookBase]
      C[ThreadSafeHookBase]　--> B
      B -->|コンテキスト| D[Context Objects]
      E[PreExecuteContext] --> D
      F[CacheHitContext] --> D
      G[CacheMissContext] --> D

## コンポーネント責務

コンポーネント | 責務 | インターフェース
--- | --- | ---
HookBase | 実行ライフサイクルの各段階で呼び出される抽象基底クラス | `pre_execute`, `on_cache_hit`, `on_cache_miss`
ThreadSafeHookBase | RLockを用いて各フックメソッドの実行を直列化する | `__init_subclass__` による自動ラップ
Context Objects | フックに渡される実行時の引数、結果、例外、メタデータのコンテナ | `ctx.args`, `ctx.result` 等

## データフロー

    sequenceDiagram
      participant Spot as core.Spot
      participant Hook as HookBase
      participant Fn as Target Function

      Spot->>Hook: pre_execute(ctx)
      alt キャッシュヒット
          Spot->>Hook: on_cache_hit(ctx)
      else キャッシュミス
          Spot->>Fn: execute()
          Fn-->>Spot: result
          Spot->>Hook: on_cache_miss(ctx)
      end

## 技術選定

技術領域 | 選定 | 理由
--- | --- | ---
パターン | オブザーバー | コアロジックを汚さずに、ログ出力や統計取得等の横断的関心を分離
スレッド安全 | 自動装飾 (Decorator) | ユーザーが意識せずに、サブクラス化するだけで安全なフックを実装可能
実行制御 | 準同期実行 | フック内の例外をキャッチしログ出力に留めることで、主処理を保護

## 非機能要件方針

- 透過性: フックの実行失敗（例外）は主処理の結果に影響を与えない

- 柔軟性: 関数ごとに異なるフックリストを `hooks=[...]` で指定可能

- パフォーマンス: フック未登録時のオーバーヘッドを最小化するガード条件を実装

2.10 メンテナンス構成 ARCH010
MAINT

## コンポーネント構成

    graph TD
      A[MaintenanceService] -->|操作集約| B[TaskDBBase]
      A -->|操作集約| C[BlobStorageBase]
      A -->|自動実行| D[Probabilistic Eviction]

## コンポーネント責務

コンポーネント | 責務 | インターフェース
--- | --- | ---
MaintenanceService | DBとストレージを跨ぐクリーンアップ操作のファサード | `clean_garbage()`, `prune()`
TaskDBBase | 参照カウントや期限切れレコードの提供 | `get_blob_refs()`, `delete_expired()`
BlobStorageBase | 物理ファイルの列挙と削除 | `list_keys()`, `delete()`

## データフロー (GCプロセス)

    sequenceDiagram
      participant Service as MaintenanceService
      participant DB as TaskDB
      participant Storage as BlobStorage

      Service->>DB: get_blob_refs()
      DB-->>Service: 有効な参照リスト (Whitelist)
      Service->>Storage: list_keys()
      Storage-->>Service: 全ファイルリスト
      Service->>Service: 差分抽出 (孤立ファイルの特定)
      Service->>Storage: delete(orphan_keys)
      Note over Service: 猶予期間 (grace_period) を考慮して削除

## 技術選定

技術領域 | 選定 | 理由
--- | --- | ---
削除ポリシー | ホワイトリスト方式 | DBに記録がないBlobを削除対象とすることで、データの整合性を担保
競合防止 | 更新時刻確認 (mtime) | 保存中のファイルを誤って消さないよう、一定時間経過した孤立のみ削除
実行頻度 | 確率的オートエビクション | 書き込み時に低確率でGCをキックし、手動メンテなしでの健康度を維持

## 非機能要件方針

- 安全性: DBレコードの削除 → Blobの削除の順序を徹底し、参照エラーを防止
- 可用性: メンテナンス中も通常のキャッシュ読み取りを妨げないロック粒度
- 観測性: 削除されたタスク数や解放された容量を統計として返却

2.11 CLI構成 ARCH011
CLI

## コンポーネント構成

    graph TD
      A[beautyspot CLI] -->|Command| B[Typer App]
      B -->|Business Logic| C[MaintenanceService]
      B -->|Output| D[Rich Console]
      B -->|Dashboard| E[Dashboard App]

## コンポーネント責務

コンポーネント | 責務 | インターフェース
--- | --- | ---
Typer App | コマンドライン引数のパースとサブコマンドのディスパッチ | `main()`, `gc()`, `list()`
MaintenanceService | 実際のキャッシュ管理ロジックの実行 | `clean_garbage()`, `clear()`
Rich Console | ターミナルでの視覚的に分かりやすいテーブルやパネルの描画 | `Console`, `Table`
Dashboard App | キャッシュ状態をインタラクティブに閲覧するためのTUI | `dashboard.py`

## データフロー

    sequenceDiagram
      participant User as ユーザー
      participant CLI as CLI App
      participant Service as MaintenanceService
      participant Output as Rich Console

      User->>CLI: beautyspot gc --name my-project
      CLI->>Service: clean_garbage()
      Service-->>CLI: result (removed count, etc.)
      CLI->>Output: print summary table
      Output-->>User: formatted output

## 技術選定

技術領域 | 選定 | 理由
--- | --- | ---
フレームワーク | Typer | 型ヒントに基づいた堅牢なコマンドラインインターフェースを迅速に構築
出力装飾 | Rich | プログレスバーやステータス表示により、長時間処理の進捗を可視化
連携 | Factory DI | カレントディレクトリや設定から自動的に `Spot` インスタンスを組み立て

## 非機能要件方針

- UX: 破壊的な操作（`clear`等）には対話的な確認プロンプトを表示
- 互換性: 標準出力のパイプ連携を妨げないよう、色の自動無効化に対応
- 利便性: 特定のプロジェクトだけでなく、全DBを横断的にスキャンする機能を提供

2.12 依存注入構成 ARCH012
DI

## コンポーネント構成

    graph TD
      A[bs.Spot Factory] -->|構築| B[core.Spot]
      A -->|DI| C[TaskDBBase]
      A -->|DI| D[BlobStorageBase]
      A -->|DI| E[SerializerProtocol]
      A -->|DI| F[StoragePolicyProtocol]
      A -->|DI| G[LimiterProtocol]

## コンポーネント責務

コンポーネント | 責務 | インターフェース
--- | --- | ---
bs.Spot (Factory) | 環境（名前、パス等）に応じたデフォルトコンポーネントの選定と組み立て | `bs.Spot()`
core.Spot | 注入された依存関係を使用して、キャッシュロジックをオーケストレーションする | `mark()`, `cached_run()`
Protocols / ABCs | 各コンポーネントが満たすべき契約（インターフェース）の定義 | `SerializerProtocol` 等

## 技術選定

技術領域 | 選定 | 理由
--- | --- | ---
パターン | Constructor Injection | 依存関係を明示的に渡し、テスト時のMock差し替えを容易にする
インターフェース | Protocol (Duck Typing) | 厳密な継承を強制せず、構造的部分型によりサードパーティ実装を受け入れ
デフォルト設定 | 規約より構成 (CoC) | `.beautyspot/` ディレクトリを基点とした標準パスを自動生成

## 非機能要件方針

- 結合度: `core.Spot` は具体的なクラス名（SQLite等）を知らず、Protocolのみに依存する
- テスト容易性: メモリ上のDBや仮装ストレージを注入することで、高速な単体テストが可能
- 拡張性: ユーザーが独自のシリアライザやストレージを自作して注入可能

2.13 Thundering Herd対策構成 ARCH013
HERD

## コンポーネント構成

    graph TD
      A[core.Spot] -->|管理要求| B[CacheManager]
      B -->|In-flight追跡| C[_inflight Dict]
      C -->|同期待機| D[threading.Event]
      C -->|非同期待機| E[asyncio.Future]

## コンポーネント責務

コンポーネント責務インターフェースCacheManagerキャッシュキーごとの実行状態管理と実行のシリアライズ`get_or_create_inflight()`_inflight実行中のタスクを保持し、後続リクエストをイベントで待機させる`ExecutionState`core.Spot待機結果の受け取りとエラーの伝播`cached_run()`

## データフロー

    sequenceDiagram
      participant T1 as Thread 1 (First)
      participant T2 as Thread 2 (Second)
      participant CM as CacheManager
      participant Fn as Target Function

      T1->>CM: get_or_create(key)
      Note over CM: 新規作成 (Executor)
      T2->>CM: get_or_create(key)
      Note over CM: 既存あり (Waiter)

      T1->>Fn: execute()
      Fn-->>T1: result
      T1->>CM: set_result(key, result)
      Note over CM: Eventをセット
      CM-->>T2: notify result
      T1-->>T1: return result
      T2-->>T2: return result

## 技術選定

技術領域選定理由待機機構Event / FutureOS/ランタイムレベルの待機を使用し、CPU負荷（ビジーループ）を回避スコープキャッシュキー単位異なる関数の実行は妨げず、同一入力のみを直列化安全策タイムアウト & リトライ実行者がハングした場合に、待機側がデッドロックしないよう保護

## 非機能要件方針

- 一貫性: 実行者が成功すれば全員に結果を、失敗すれば全員に同じ例外を伝播

- 効率性: メモリリークを防ぐため、結果配布後に `_inflight` エントリを即座に削除

- 信頼性: 強参照 (`StrongReference`) で実行中のタスクを保持し、GCによる消失を防止