✓=レビュー済 ○=未レビュー ⚠=Suspect(複数同時表示あり。IDクリックで詳細へ)
| グループ | REQ | ARCH | SPEC | TST | IMPL | ADR |
|---|---|---|---|---|---|---|
| LIFE | REQ007 ✓ キャッシュの有効期限を関数名パターンに基づくルールで設定できること。個別の関数に対して保持期間を指定でき、期限切れのキャッシュを自動的に無効化できること。 | ARCH007 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|マッチング要求| B[LifecyclePolicy]... | SPEC016 ✓ ## インターフェース ```python class LifecyclePolicy: def add_rule(self, pattern: st... | TST016 ✓ ## 目的 `LifecyclePolicy` はキャッシュデータの保持期間を関数名パターンで制御する。パターンマッチングの不備は、重要なキャッシュの意図しな... | IMPL016 ✓ ## 実装概要 `LifecyclePolicy` クラスは `Rule` オブジェクトのリストを保持し、 関数名に基づいてキャッシュの保持期間を決定する。 ... | ADR032 ✓ # Declarative Lifecycle Policy ## Context and Problem Statement / コンテキスト 機械学習や... |
| LIFE | REQ007 ✓ キャッシュの有効期限を関数名パターンに基づくルールで設定できること。個別の関数に対して保持期間を指定でき、期限切れのキャッシュを自動的に無効化できること。 | ARCH007 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|マッチング要求| B[LifecyclePolicy]... | SPEC017 ✓ ## インターフェース ```python def parse_retention(val: Any) -> float | None: ... ``` #... | TST017 ✓ ## 目的 `Retention` はキャッシュの有効期間をユーザーフレンドリーな形式で指定する値オブジェクトである。パースの不備は意図しない保持期間(例: ... | IMPL017 ✓ ## 実装概要 `Retention` クラスを名前空間として使用し、保持期間のパースや 特殊な定数(`INDEFINITE`, `FOREVER`)を管理す... | ADR032 ✓ # Declarative Lifecycle Policy ## Context and Problem Statement / コンテキスト 機械学習や... |
| LIFE | REQ007 ✓ キャッシュの有効期限を関数名パターンに基づくルールで設定できること。個別の関数に対して保持期間を指定でき、期限切れのキャッシュを自動的に無効化できること。 | ARCH007 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|マッチング要求| B[LifecyclePolicy]... | SPEC016 ✓ ## インターフェース ```python class LifecyclePolicy: def add_rule(self, pattern: st... | TST016 ✓ ## 目的 `LifecyclePolicy` はキャッシュデータの保持期間を関数名パターンで制御する。パターンマッチングの不備は、重要なキャッシュの意図しな... | IMPL016 ✓ ## 実装概要 `LifecyclePolicy` クラスは `Rule` オブジェクトのリストを保持し、 関数名に基づいてキャッシュの保持期間を決定する。 ... | ADR038 ✓ # Probabilistic Auto-Eviction for Storage Maintenance ## Context and Problem St... |
| LIFE | REQ007 ✓ キャッシュの有効期限を関数名パターンに基づくルールで設定できること。個別の関数に対して保持期間を指定でき、期限切れのキャッシュを自動的に無効化できること。 | ARCH007 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|マッチング要求| B[LifecyclePolicy]... | SPEC017 ✓ ## インターフェース ```python def parse_retention(val: Any) -> float | None: ... ``` #... | TST017 ✓ ## 目的 `Retention` はキャッシュの有効期間をユーザーフレンドリーな形式で指定する値オブジェクトである。パースの不備は意図しない保持期間(例: ... | IMPL017 ✓ ## 実装概要 `Retention` クラスを名前空間として使用し、保持期間のパースや 特殊な定数(`INDEFINITE`, `FOREVER`)を管理す... | ADR038 ✓ # Probabilistic Auto-Eviction for Storage Maintenance ## Context and Problem St... |
| リンク方向 | カバー数 | カバー率 | 未カバー |
|---|---|---|---|
| ARCH → REQ | 1 / 1 | 100.0% | — |
| SPEC → ARCH | 1 / 1 | 100.0% | — |
| TST → SPEC | 2 / 2 | 100.0% | — |
| IMPL → SPEC | 2 / 2 | 100.0% | — |
| ADR → REQ | 1 / 1 | 100.0% | — |
キャッシュの有効期限を関数名パターンに基づくルールで設定できること。個別の関数に対して保持期間を指定でき、期限切れのキャッシュを自動的に無効化できること。
親: —
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 等の定数により、グローバルポリシーの個別上書きに対応親: REQ007
class LifecyclePolicy:
def add_rule(self, pattern: str, retention: Any) -> None: ...
def resolve_with_fallback(self, func_name: str) -> float | None: ...
順次照合: 登録された順にルールを走査する
パターンマッチ: fnmatch.fnmatch を使用し、関数名がパターンに合致するか判定
二段階解決:
最初に「完全修飾名 (module.qualname)」で照合
マッチしなければ「短縮名 (qualname)」で照合
結果返却: 最初にマッチしたルールの保持期間を秒数に変換して返す
パラメータ例説明pattern"my_mod.*", "*_test"Glob形式のパターンretention"30d", 3600保持期間の定義
default_retention パラメータで、どのルールにもマッチしない場合のデフォルト保持期間を指定できるLifecyclePolicy.default() のデフォルト値は "30d"(30日)None を指定すると無期限保持(従来互換)default_retention で指定された保持期間を返す(デフォルト: 30日)親: ARCH007
def parse_retention(val: Any) -> float | None: ...
"7d" (7日), "12h" (12時間), "30m" (30分), "10s" (10秒) 形式を秒数に変換する。大文字小文字は区別しない。int, float: 秒数としてそのまま扱う。timedelta: total_seconds() を取得。None: ポリシーに従う(または無期限)を意味する。| 単位 | 倍率 (秒) | 備考 |
|---|---|---|
d |
86400 | 日数 |
h |
3600 | 時間 |
m |
60 | 分 |
s |
1 | 秒 |
ValidationError を送出する。ValidationError となる。Retention.FOREVER (無限大の秒数) を提供し、ポリシーに関わらず永続化を指示できる。親: ARCH007
LifecyclePolicy はキャッシュデータの保持期間を関数名パターンで制御する。パターンマッチングの不備は、重要なキャッシュの意図しない削除(データロス)や不要なキャッシュの蓄積(ディスク圧迫)を引き起こす。
"train_*", "*.preprocess" 等)がルールの func_pattern として正しく機能し、マッチした関数に対して指定の Retention が適用されることmodule.qualname)でルールが見つからない場合に、短縮名(qualname のみ)でフォールバック検索が行われることdefault_retention で指定された保持期間(デフォルト: 30日)が適用されることdefault_retention=None を指定した場合に無期限保持(None)が返されることreferences: tests/unit/test_lifecycle.py
親: SPEC016
子: —
Retention はキャッシュの有効期間をユーザーフレンドリーな形式で指定する値オブジェクトである。パースの不備は意図しない保持期間(例: "7d" を 7秒と誤解釈)を招き、重要なキャッシュの早期削除やディスクの際限ない肥大化に繋がる。
"7d" → 7日、"12h" → 12時間、"30m" → 30分、"10s" → 10秒として正しく解釈されることtimedelta(days=7) がそのまま保持期間として受け入れられることRetention.FOREVER が無期限保持を表し、シングルトンであること(is 比較が成立)"-1d")やゼロ("0s")が ValidationError を送出すること。不正な形式("abc", "" 等)も拒否されることreferences: tests/unit/test_lifecycle_extended.py
親: SPEC017
子: —
LifecyclePolicy クラスは Rule オブジェクトのリストを保持し、
関数名に基づいてキャッシュの保持期間を決定する。
resolve() メソッドが fnmatch.fnmatch() を使って関数名をパターンと照合し、
最初にマッチしたルールの保持期間を返す。
ルールのリストは順序が意味を持ち、最初にマッチしたものが採用される。
これにより、特定プレフィックスの関数には短い保持期間を設定し、
最後に *(ワイルドカード)でデフォルトポリシーを設定するような
フォールバック構造が簡単に記述できる。
後方互換性と柔軟性のため、まず func_identifier (モジュール名付きの完全修飾名) で
マッチングを試み、マッチしなかった場合は func_name (短い関数名) で再度マッチングを
試みる resolve_with_fallback() を提供している。
LifecyclePolicy コンストラクタに default_retention パラメータを追加。
どのルールにもマッチしない場合に返す保持期間を指定できる。
LifecyclePolicy.default() は default_retention="30d" で生成し、
デフォルトで30日の保持期間を設定する。
LifecyclePolicy.default() は空のルールリスト + default_retention="30d" を持つdefault_retention=None を指定すれば従来通り無期限保持となるreferences: src/beautyspot/lifecycle.py
親: SPEC016
子: —
Retention クラスを名前空間として使用し、保持期間のパースや
特殊な定数(INDEFINITE, FOREVER)を管理する。
parse_retention() 関数は文字列("7d", "12h"等)、timedelta、
または秒数(int/float)を受け取り、標準化された timedelta オブジェクトに変換する。
Retention.FOREVER はポリシーを強制的にバイパスするための特殊値。
PEP 703 (free-threading) 環境での安全性を考慮し、
_ForeverSentinel は threading.Lock を用いたダブルチェックロッキングで
厳密なシングルトンとして実装されている。
"7d", "12h", "30m", "10s" のような文字列フォーマットを
正規表現 _TIME_PATTERN でパースすることで、設定ファイルや
ハードコード時の可読性を高めている。
parse_retention() は無効なフォーマットや負の値に対して ValidationError を送出する_ForeverSentinel は __bool__() で True を返すようオーバーライドされているreferences: src/beautyspot/lifecycle.py
親: SPEC017
子: —
機械学習や生成AIの実験プロセスにおいて、生成されるデータの重要度は均一ではありません。数ヶ月保持すべき「最終モデル」もあれば、数時間で不要になる「一時的なデバッグ出力」もあります。
現在、これらの古いデータを削除するには、ユーザーが MaintenanceService.prune(older_than=...) を呼び出すスクリプトを自作し、定期実行する必要があります。これはユーザーにとって負担であり、設定を忘れるとディスク容量を圧迫する原因となります。また、beautyspot は常駐プロセスを持たないため、クリーンアップのトリガー設計が課題となります。
retention パラメータとデータベースでの有効期限管理を導入し、アクセス時のチェック(Lazy Expiration)と CLI による一括削除を組み合わせる。Chosen option: Option 2.
ユーザーが「データの寿命(What)」を宣言するだけで済むよう、以下の仕組みを導入します。
retention Parameter for @markデコレータおよび run メソッドに retention 引数を追加します(例: "7d", "1h", None)。
expires_at)タスク作成時に寿命を計算し、tasks テーブルの expires_at カラムに保存します。
キャッシュ取得時(spot.db.get)に、expires_at < current_time であれば「キャッシュミス」とみなして None を返します。この時点では物理削除は行わず、レイテンシへの影響を最小限にします。
期限切れデータの物理削除は、CLI コマンド $ beautyspot gc --expired によって一括で行います。
親: REQ007
子: —
beautyspot はキャッシュの TTL をサポートしており、期限切れデータは論理的に無効化されます。しかし、ユーザーが明示的にクリーンアップを呼び出さない限り、有効期限切れのメタデータや巨大な孤立 Blob ファイルは物理的に削除されず、ストレージが肥大化し続けるという問題がありました。
ユーザーにインフラ管理を意識させない「黒子」としての哲学を維持しつつ、自動でガベージコレクション(エビクション)を行う仕組みが必要になりました。
Chosen option: Option 3.
確率的自動エビクションを採用します。
Spot の初期化パラメータに eviction_rate: float = 0.0 を追加し、オプトイン方式とします。_BackgroundLoop にオフロードします。親: REQ007
子: —