← 全体レポートに戻る

局所トレーサビリティビュー グループ: STORE

生成日時: 2026-03-13 23:59:51

対象アイテム

17

REQ

1

ARCH

1

SPEC

4

TST

4

IMPL

4

ADR

3

レビュー済

17/17

Suspect

0

グループ

STORE
アイテム: REQ004 ARCH004 SPEC009 SPEC010 SPEC011 SPEC024 TST009 TST010 TST011 TST024 IMPL009 IMPL010 IMPL011 IMPL024 ADR030 ADR044 ADR046

トレーサビリティマトリクス

✓=レビュー済 ○=未レビュー ⚠=Suspect(複数同時表示あり。IDクリックで詳細へ)

グループREQARCHSPECTSTIMPLADR
STOREREQ004
大きなキャッシュデータを外部ストレージ(ローカルファイルシステムやS3等)に保存できること。 DB直接保存とBlob保存を自動判定できること。
ARCH004
## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|判定| B[StoragePolicyProtocol...
SPEC009
## インターフェース ```python class LocalStorage(BlobStorageMaintenable): def __ini...
TST009
## 目的 `LocalStorage` はBlobデータのファイルシステム永続化を担い、並行書き込み時のデータ破損やパストラバーサルによるセキュリティ脆弱性...
IMPL009
## 実装概要 - `LocalStorage` クラスが `BlobStorageBase` を実装。 ファイルは `{base_dir}/{key}....
ADR030
# Declarative Storage Policy ## Context and Problem Statement / コンテキスト これまでの `...
STOREREQ004
大きなキャッシュデータを外部ストレージ(ローカルファイルシステムやS3等)に保存できること。 DB直接保存とBlob保存を自動判定できること。
ARCH004
## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|判定| B[StoragePolicyProtocol...
SPEC010
## インターフェース ```python class S3Storage(BlobStorageMaintenable): def __init__...
TST010
## 目的 `S3Storage` はクラウド環境での大規模Blob保存を担い、ネットワーク障害・認証エラー・バケット不在などオンプレミスにはない障害モードが...
IMPL010
## 実装概要 `S3Storage` クラスが `BlobStorageBase` を実装。 `s3://bucket/prefix/` 形式の URI を...
ADR030
# Declarative Storage Policy ## Context and Problem Statement / コンテキスト これまでの `...
STOREREQ004
大きなキャッシュデータを外部ストレージ(ローカルファイルシステムやS3等)に保存できること。 DB直接保存とBlob保存を自動判定できること。
ARCH004
## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|判定| B[StoragePolicyProtocol...
SPEC011
## インターフェース ```python class StoragePolicyProtocol(Protocol): def should_sav...
TST011
## 目的 ストレージポリシーは「データをDB直接保存するか、Blobストレージに分離するか」を決定する戦略レイヤーである。閾値判定の誤りはDBの肥大化(性能...
IMPL011
## 実装概要 `StoragePolicyProtocol` が `should_save_as_blob(data: bytes) -> bool` を定...
ADR030
# Declarative Storage Policy ## Context and Problem Statement / コンテキスト これまでの `...
STOREREQ004
大きなキャッシュデータを外部ストレージ(ローカルファイルシステムやS3等)に保存できること。 DB直接保存とBlob保存を自動判定できること。
ARCH004
## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|判定| B[StoragePolicyProtocol...
SPEC024
## インターフェース ```python class BlobStorageBase(ABC): @abstractmethod def s...
TST024
## 目的 `BlobStorageBase` は全ストレージバックエンド(LocalStorage, S3Storage, サードパーティ)が 準拠すべき抽...
IMPL024
## 実装概要 `storage.py` 内の `BlobStorageBase` ABC が SPEC024 の中核実装。 5つの抽象メソッド(`save`...
ADR030
# Declarative Storage Policy ## Context and Problem Statement / コンテキスト これまでの `...
STOREREQ004
大きなキャッシュデータを外部ストレージ(ローカルファイルシステムやS3等)に保存できること。 DB直接保存とBlob保存を自動判定できること。
ARCH004
## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|判定| B[StoragePolicyProtocol...
SPEC009
## インターフェース ```python class LocalStorage(BlobStorageMaintenable): def __ini...
TST009
## 目的 `LocalStorage` はBlobデータのファイルシステム永続化を担い、並行書き込み時のデータ破損やパストラバーサルによるセキュリティ脆弱性...
IMPL009
## 実装概要 - `LocalStorage` クラスが `BlobStorageBase` を実装。 ファイルは `{base_dir}/{key}....
ADR044
# Drop Absolute Path Support in LocalStorage ## Context and Problem Statement /...
STOREREQ004
大きなキャッシュデータを外部ストレージ(ローカルファイルシステムやS3等)に保存できること。 DB直接保存とBlob保存を自動判定できること。
ARCH004
## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|判定| B[StoragePolicyProtocol...
SPEC009
## インターフェース ```python class LocalStorage(BlobStorageMaintenable): def __ini...
TST009
## 目的 `LocalStorage` はBlobデータのファイルシステム永続化を担い、並行書き込み時のデータ破損やパストラバーサルによるセキュリティ脆弱性...
IMPL009
## 実装概要 - `LocalStorage` クラスが `BlobStorageBase` を実装。 ファイルは `{base_dir}/{key}....
ADR046
# Delegate Workspace Initialization to Storage Backends ## Context and Problem ...
STOREREQ004
大きなキャッシュデータを外部ストレージ(ローカルファイルシステムやS3等)に保存できること。 DB直接保存とBlob保存を自動判定できること。
ARCH004
## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|判定| B[StoragePolicyProtocol...
SPEC010
## インターフェース ```python class S3Storage(BlobStorageMaintenable): def __init__...
TST010
## 目的 `S3Storage` はクラウド環境での大規模Blob保存を担い、ネットワーク障害・認証エラー・バケット不在などオンプレミスにはない障害モードが...
IMPL010
## 実装概要 `S3Storage` クラスが `BlobStorageBase` を実装。 `s3://bucket/prefix/` 形式の URI を...
ADR044
# Drop Absolute Path Support in LocalStorage ## Context and Problem Statement /...
STOREREQ004
大きなキャッシュデータを外部ストレージ(ローカルファイルシステムやS3等)に保存できること。 DB直接保存とBlob保存を自動判定できること。
ARCH004
## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|判定| B[StoragePolicyProtocol...
SPEC010
## インターフェース ```python class S3Storage(BlobStorageMaintenable): def __init__...
TST010
## 目的 `S3Storage` はクラウド環境での大規模Blob保存を担い、ネットワーク障害・認証エラー・バケット不在などオンプレミスにはない障害モードが...
IMPL010
## 実装概要 `S3Storage` クラスが `BlobStorageBase` を実装。 `s3://bucket/prefix/` 形式の URI を...
ADR046
# Delegate Workspace Initialization to Storage Backends ## Context and Problem ...
STOREREQ004
大きなキャッシュデータを外部ストレージ(ローカルファイルシステムやS3等)に保存できること。 DB直接保存とBlob保存を自動判定できること。
ARCH004
## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|判定| B[StoragePolicyProtocol...
SPEC011
## インターフェース ```python class StoragePolicyProtocol(Protocol): def should_sav...
TST011
## 目的 ストレージポリシーは「データをDB直接保存するか、Blobストレージに分離するか」を決定する戦略レイヤーである。閾値判定の誤りはDBの肥大化(性能...
IMPL011
## 実装概要 `StoragePolicyProtocol` が `should_save_as_blob(data: bytes) -> bool` を定...
ADR044
# Drop Absolute Path Support in LocalStorage ## Context and Problem Statement /...
STOREREQ004
大きなキャッシュデータを外部ストレージ(ローカルファイルシステムやS3等)に保存できること。 DB直接保存とBlob保存を自動判定できること。
ARCH004
## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|判定| B[StoragePolicyProtocol...
SPEC011
## インターフェース ```python class StoragePolicyProtocol(Protocol): def should_sav...
TST011
## 目的 ストレージポリシーは「データをDB直接保存するか、Blobストレージに分離するか」を決定する戦略レイヤーである。閾値判定の誤りはDBの肥大化(性能...
IMPL011
## 実装概要 `StoragePolicyProtocol` が `should_save_as_blob(data: bytes) -> bool` を定...
ADR046
# Delegate Workspace Initialization to Storage Backends ## Context and Problem ...
STOREREQ004
大きなキャッシュデータを外部ストレージ(ローカルファイルシステムやS3等)に保存できること。 DB直接保存とBlob保存を自動判定できること。
ARCH004
## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|判定| B[StoragePolicyProtocol...
SPEC024
## インターフェース ```python class BlobStorageBase(ABC): @abstractmethod def s...
TST024
## 目的 `BlobStorageBase` は全ストレージバックエンド(LocalStorage, S3Storage, サードパーティ)が 準拠すべき抽...
IMPL024
## 実装概要 `storage.py` 内の `BlobStorageBase` ABC が SPEC024 の中核実装。 5つの抽象メソッド(`save`...
ADR044
# Drop Absolute Path Support in LocalStorage ## Context and Problem Statement /...
STOREREQ004
大きなキャッシュデータを外部ストレージ(ローカルファイルシステムやS3等)に保存できること。 DB直接保存とBlob保存を自動判定できること。
ARCH004
## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|判定| B[StoragePolicyProtocol...
SPEC024
## インターフェース ```python class BlobStorageBase(ABC): @abstractmethod def s...
TST024
## 目的 `BlobStorageBase` は全ストレージバックエンド(LocalStorage, S3Storage, サードパーティ)が 準拠すべき抽...
IMPL024
## 実装概要 `storage.py` 内の `BlobStorageBase` ABC が SPEC024 の中核実装。 5つの抽象メソッド(`save`...
ADR046
# Delegate Workspace Initialization to Storage Backends ## Context and Problem ...

カバレッジ(局所)

リンク方向カバー数カバー率未カバー
ARCH → REQ 1 / 1 100.0%
SPEC → ARCH 1 / 1 100.0%
TST → SPEC 4 / 4 100.0%
IMPL → SPEC 4 / 4 100.0%
ADR → REQ 1 / 1 100.0%

アイテム詳細

REQ004 REQ {h(g)} ✓ レビュー済

大きなキャッシュデータを外部ストレージ(ローカルファイルシステムやS3等)に保存できること。

DB直接保存とBlob保存を自動判定できること。

親:

子: ADR030, ADR044, ADR046, ARCH004

ARCH004 ARCH {h(g)} ✓ レビュー済

コンポーネント構成

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) 業界標準のプロトコルによる高い互換性とスケーラビリティ

非機能要件方針

親: REQ004

子: SPEC009, SPEC010, SPEC011, SPEC024

SPEC009 SPEC {h(g)} ✓ レビュー済

インターフェース

class LocalStorage(BlobStorageMaintenable):
    def __init__(self, base_dir: str | Path): ...
    def save(self, key: str, data: bytes) -> str: ...
    def load(self, location: str) -> bytes: ...

振る舞い

アトミック書き込み (save)

  1. 指定ディレクトリ内に tempfile.mkstemp で一時ファイルを作成
  2. データを書き込み、flush() および os.fsync() でディスク到達を保証
  3. os.replace で最終的なパスへリネーム(アトミックな置換)

パストラバーサル対策 (load / delete)

パラメータ詳細

パラメータ 説明 制限
key 保存ファイル名のベース ../ を含む場合は ValidationError
location save が返した識別子 相対パス形式を推奨

エラーハンドリング

エッジケース

親: ARCH004

子: IMPL009, TST009

SPEC010 SPEC {h(g)} ✓ レビュー済

インターフェース

class S3Storage(BlobStorageMaintenable):
    def __init__(self, s3_uri: str, s3_opts: dict | None = None): ...

振る舞い

URIベースの管理

大容量対応 (save)

高速なメタデータ取得 (get_mtime)

パラメータ詳細

設定 内容
s3_uri s3://my-bucket/my-prefix 形式
s3_opts boto3.client('s3', **s3_opts) に渡される設定

エラーハンドリング

エッジケース

親: ARCH004

子: IMPL010, TST010

SPEC011 SPEC {h(g)} ✓ レビュー済

インターフェース

class StoragePolicyProtocol(Protocol):
    def should_save_as_blob(self, data: bytes) -> bool: ...

振る舞い

判定ロジック

パラメータ詳細

ポリシー型 パラメータ デフォルト 説明
Threshold threshold なし バイト単位の閾値。10MB等を推奨
Warning warning_threshold なし 警告を出すサイズ閾値

エラーハンドリング

エッジケース

親: ARCH004

子: IMPL011, TST011

SPEC024 SPEC {h(g)} ✓ レビュー済

インターフェース

class BlobStorageBase(ABC):
    @abstractmethod
    def save(self, key: str, data: ReadableBuffer) -> str: ...
    @abstractmethod
    def load(self, location: str) -> bytes: ...
    @abstractmethod
    def delete(self, location: str) -> None: ...
    @abstractmethod
    def list_keys(self) -> Iterator[str]: ...
    @abstractmethod
    def get_mtime(self, location: str) -> float: ...

振る舞い

パラメータ詳細

パラメータ 説明
key str キャッシュキー(通常はハッシュ値)
data bytes / memoryview シリアライズ済みバイナリ
location str 実体へのポインタ(ファイルパス、S3 URI等)

エラーハンドリング

エッジケース

親: ARCH004

子: IMPL024, TST024

TST009 TST {h(g)} ✓ レビュー済

目的

LocalStorage はBlobデータのファイルシステム永続化を担い、並行書き込み時のデータ破損やパストラバーサルによるセキュリティ脆弱性が発生しうる。アトミック書き込みとセキュリティバリデーションの正確性を保証する。

検証観点

references: tests/integration/storage/test_local.py

親: SPEC009

子:

TST010 TST {h(g)} ✓ レビュー済

目的

S3Storage はクラウド環境での大規模Blob保存を担い、ネットワーク障害・認証エラー・バケット不在などオンプレミスにはない障害モードが存在する。boto3 オプショナル依存のガード処理も含め、クラウドストレージ統合の信頼性を検証する。

検証観点

references: tests/integration/storage/test_s3.py

親: SPEC010

子:

TST011 TST {h(g)} ✓ レビュー済

目的

ストレージポリシーは「データをDB直接保存するか、Blobストレージに分離するか」を決定する戦略レイヤーである。閾値判定の誤りはDBの肥大化(性能劣化)や不要なBlob分離(オーバーヘッド増加)を招く。3種のポリシー実装がそれぞれの契約を正しく満たすことを検証する。

検証観点

references: tests/unit/test_storage_policy.py

親: SPEC011

子:

TST024 TST {h(g)} ✓ レビュー済

目的

BlobStorageBase は全ストレージバックエンド(LocalStorage, S3Storage, サードパーティ)が 準拠すべき抽象契約を定義する。この契約が正しく機能しないと、キャッシュデータの 保存・復元・削除・メンテナンス(GC)の全てが破綻する。 LocalStorage を具象実装として使い、インターフェース契約を網羅的に検証する。

検証観点

references: tests/integration/storage/test_blob_storage_base.py

親: SPEC024

子:

IMPL009 IMPL {h(g)} ✓ レビュー済

実装概要

設計判断

アトミック書き込みパターン

一時ファイルに書き込み → fsync でディスクに確実に反映 → os.replace で アトミックにリネーム。この3段階により、書き込み中のクラッシュや 並行アクセスでファイルが半壊状態になることを防ぐ。 一時ファイルには .spot_tmp 接尾辞を使用し、残存時のクリーンアップを容易にした。

パストラバーサル防止の二重ガード

実装メモ

references: src/beautyspot/storage.py

親: SPEC009

子:

IMPL010 IMPL {h(g)} ✓ レビュー済

実装概要

S3Storage クラスが BlobStorageBase を実装。 s3://bucket/prefix/ 形式の URI を解析し、boto3 の S3 クライアントを使用。 ファイルは {prefix}/{key}.bin のキーで S3 に保存される。

設計判断

オプショナル依存のガード

boto3 は import 時にガードし、未インストール時は クラス定義は成功するがインスタンス化で明確なエラーメッセージを表示する。 beautyspot 全体が boto3 に依存しないよう、遅延インポートパターンを採用。

URI ベースのバックエンド選択

s3://bucket/prefix 形式の URI から bucket と prefix を自動解析する _parse_s3_uri() ヘルパーにより、ファクトリ関数がスキームに基づいて LocalStorage と S3Storage を透過的に切り替えられる。

実装メモ

references: src/beautyspot/storage.py

親: SPEC010

子:

IMPL011 IMPL {h(g)} ✓ レビュー済

実装概要

StoragePolicyProtocolshould_save_as_blob(data: bytes) -> bool を定義。 3つの実装を提供: - ThresholdStoragePolicy: len(data) > threshold で判定 - WarningOnlyPolicy: 常に False を返し、閾値超過時に WARNING ログを出力 - AlwaysBlobPolicy: 常に True を返す

設計判断

Strategy パターンによるポリシー分離

保存先判定ロジックを core.Spot から分離し、差し替え可能なポリシーオブジェクトに 委譲する。@mark(save_blob=True/False) の明示指定はポリシーより優先されるが、 save_blob=None(デフォルト)時にポリシーが判定を行う。

WarningOnlyPolicy をデフォルトにした理由

v2.0 との後方互換性を維持するため。v2.0 では全データが DB 直接保存だったため、 デフォルトを ThresholdStoragePolicy にすると既存ユーザーの挙動が変わる。 WARNING ログにより、ユーザーに Blob 分離の恩恵を段階的に周知する。

実装メモ

references: src/beautyspot/storage.py

親: SPEC011

子:

IMPL024 IMPL {h(g)} ✓ レビュー済

実装概要

storage.py 内の BlobStorageBase ABC が SPEC024 の中核実装。 5つの抽象メソッド(save, load, delete, list_keys, get_mtime)を定義し、 LocalStorageS3Storage が具象実装として継承する。

加えて、ランタイム型チェックのために BlobStorageCoreMaintenableBlobStorageMaintenable の3つの Protocol クラスを定義し、 isinstance() での型判定を可能にしている(@runtime_checkable)。

設計判断

ABC と Protocol の併用

BlobStorageBase は ABC として抽象メソッドを強制する一方、 BlobStorageCore / Maintenable は Protocol として構造的部分型を提供する。 これにより、BlobStorageBase を継承しないサードパーティ実装でも isinstance(obj, BlobStorageCore) で利用可能になる柔軟性を確保した。

3層の Protocol 分割

これにより、save/load/delete のみを実装した軽量バックエンドも受け入れ可能。

ReadableBuffer 型エイリアス

bytes | bytearray | memoryviewReadableBuffer として定義。 memoryview を受け入れることでゼロコピー書き込みが可能になり、 大きなデータを保存する際のメモリ効率を改善している。

実装メモ

references: src/beautyspot/storage.py

親: SPEC024

子:

ADR030 ADR {h(g)} ✓ レビュー済

Declarative Storage Policy

Context and Problem Statement / コンテキスト

これまでの beautyspot では、関数の実行結果を Blob ストレージ(ファイル)に保存するか、DB のレコードに直接埋め込むかは、ユーザーが save_blob=True フラグで明示的に指定する必要がありました。 また、データサイズが大きい場合に警告を出す機能はありましたが、自動的に対処する機能はありませんでした。これにより、ユーザーはデータのサイズを予測してフラグを管理するという負担を強いられていました。

Decision Drivers / 要求

Considered Options / 検討

Decision Outcome / 決定

Chosen option: Option 2.

ストレージ保存方式の決定ロジックを抽象化した StoragePolicy プロトコルを導入します。

  1. StoragePolicy Interface:

    • should_save_as_blob(data: bytes) -> bool を持つプロトコルを定義します。
    • 配置場所は src/beautyspot/storage.py とし、ストレージ関連の責務を凝集させます。
  2. Standard Implementations:

    • ThresholdStoragePolicy: 指定したバイト数を超えた場合に Blob 保存を選択する(推奨デフォルト)。
    • WarningOnlyPolicy: 従来動作互換。Blob 化はせず、閾値超えでログ警告のみ行う。
  3. Precedence (優先順位):

    • 優先度1: 関数ごとの明示的指定 (@mark(save_blob=...))。
    • 優先度2: ポリシーによる自動判定。

Consequences / 決定

親: REQ004

子:

ADR044 ADR {h(g)} ✓ レビュー済

Drop Absolute Path Support in LocalStorage

Context and Problem Statement / コンテキスト

v1.x 系の LocalStorage では、ファイルパス(location)として絶対パスを許容・解決する挙動が含まれていました。v2.0 の開発において、パストラバーサル脆弱性を防ぐために base_dir に対する厳密なセキュリティチェックを導入しましたが、これが旧来の絶対パスの挙動と競合し、意図せぬクラッシュを引き起こす可能性がありました。

Decision Drivers / 要求

Considered Options / 検討

Decision Outcome / 決定

Chosen option: Option 2.

LocalStorage.load() および関連するファイルアクセスにおいて、絶対パスによる後方互換性の維持を公式に 放棄 します。

すべての location パラメータは base_dir に対する相対パスとしてのみ解釈されます。絶対パスの形式であっても、それが base_dir のサブディレクトリ内に解決されない限り、セキュリティチェックにより ValueError として処理されます。

Consequences / 決定

親: REQ004

子:

ADR046 ADR {h(g)} ✓ レビュー済

Delegate Workspace Initialization to Storage Backends

Context and Problem Statement / コンテキスト

これまで beautyspot では、 Factory 関数 Spot() の初期化時において、デフォルトのキャッシュディレクトリ(.beautyspot/)の作成と .gitignore の配置を一律で行っていました。しかし、ユーザーがカスタムのパスを指定した場合でも、意図せずカレントディレクトリに .beautyspot/ が作成されてしまうという課題がありました。また、コンポーネント(DB、ストレージ)が自身の永続化先の詳細を自己管理できておらず、関心の分離の観点で不完全な設計となっていました。

Decision Drivers / 要求

Considered Options / 検討

Decision Outcome / 決定

Chosen option: Option 2.

__init__.py および core.py からワークスペース初期化ロジック(_setup_workspace)を完全に削除します。代わりに、ローカルファイルシステムに依存する各バックエンドコンポーネント(LocalStorage および SQLiteTaskDB)の初期化処理内で、自身が使用するディレクトリの作成と .gitignore の配置を行うように責務を委譲します。

Consequences / 決定

親: REQ004

子: