✓=レビュー済 ○=未レビュー ⚠=Suspect(複数同時表示あり。IDクリックで詳細へ)
| グループ | REQ | ARCH | SPEC | TST | IMPL | ADR |
|---|---|---|---|---|---|---|
| MAINT | REQ010 ✓ 期限切れキャッシュの削除、孤立Blobファイルのガベージコレクション、時間ベースの一括削除(prune)等のメンテナンス操作ができること。 | ARCH010 ✓ ## コンポーネント構成 ```mermaid graph TD A[MaintenanceService] -->|操作集約| B[TaskDBBase... | SPEC020 ✓ ## インターフェース ```python class MaintenanceService: def clean_garbage(self, gra... | TST020 ✓ ## 目的 `MaintenanceService` は CLI やダッシュボードからのシステム管理操作を仲介するサービス層である。タスク詳細の表示ミスは運用... | IMPL020 ✓ ## 実装概要 `MaintenanceService` クラスがDBとBlobストレージ間の整合性チェック、 期限切れキャッシュの削除、孤立ファイルの検出と... | ADR016 ✓ # Interactive Deletion and Cleanup Policy ## Context and Problem Statement / コン... |
| MAINT | REQ010 ✓ 期限切れキャッシュの削除、孤立Blobファイルのガベージコレクション、時間ベースの一括削除(prune)等のメンテナンス操作ができること。 | ARCH010 ✓ ## コンポーネント構成 ```mermaid graph TD A[MaintenanceService] -->|操作集約| B[TaskDBBase... | SPEC020 ✓ ## インターフェース ```python class MaintenanceService: def clean_garbage(self, gra... | TST020 ✓ ## 目的 `MaintenanceService` は CLI やダッシュボードからのシステム管理操作を仲介するサービス層である。タスク詳細の表示ミスは運用... | IMPL020 ✓ ## 実装概要 `MaintenanceService` クラスがDBとBlobストレージ間の整合性チェック、 期限切れキャッシュの削除、孤立ファイルの検出と... | ADR018 ✓ # Expansion of Testing Strategy: Introduction of High-Level Integration Tests #... |
| MAINT | REQ010 ✓ 期限切れキャッシュの削除、孤立Blobファイルのガベージコレクション、時間ベースの一括削除(prune)等のメンテナンス操作ができること。 | ARCH010 ✓ ## コンポーネント構成 ```mermaid graph TD A[MaintenanceService] -->|操作集約| B[TaskDBBase... | SPEC020 ✓ ## インターフェース ```python class MaintenanceService: def clean_garbage(self, gra... | TST020 ✓ ## 目的 `MaintenanceService` は CLI やダッシュボードからのシステム管理操作を仲介するサービス層である。タスク詳細の表示ミスは運用... | IMPL020 ✓ ## 実装概要 `MaintenanceService` クラスがDBとBlobストレージ間の整合性チェック、 期限切れキャッシュの削除、孤立ファイルの検出と... | ADR020 ✓ # Tolerant Deletion Policy for Task Cleanup ## Context and Problem Statement / ... |
| MAINT | REQ010 ✓ 期限切れキャッシュの削除、孤立Blobファイルのガベージコレクション、時間ベースの一括削除(prune)等のメンテナンス操作ができること。 | ARCH010 ✓ ## コンポーネント構成 ```mermaid graph TD A[MaintenanceService] -->|操作集約| B[TaskDBBase... | SPEC020 ✓ ## インターフェース ```python class MaintenanceService: def clean_garbage(self, gra... | TST020 ✓ ## 目的 `MaintenanceService` は CLI やダッシュボードからのシステム管理操作を仲介するサービス層である。タスク詳細の表示ミスは運用... | IMPL020 ✓ ## 実装概要 `MaintenanceService` クラスがDBとBlobストレージ間の整合性チェック、 期限切れキャッシュの削除、孤立ファイルの検出と... | ADR021 ✓ # Code Visualization and Quality Metrics Strategy ## Context and Problem Statem... |
| MAINT | REQ010 ✓ 期限切れキャッシュの削除、孤立Blobファイルのガベージコレクション、時間ベースの一括削除(prune)等のメンテナンス操作ができること。 | ARCH010 ✓ ## コンポーネント構成 ```mermaid graph TD A[MaintenanceService] -->|操作集約| B[TaskDBBase... | SPEC020 ✓ ## インターフェース ```python class MaintenanceService: def clean_garbage(self, gra... | TST020 ✓ ## 目的 `MaintenanceService` は CLI やダッシュボードからのシステム管理操作を仲介するサービス層である。タスク詳細の表示ミスは運用... | IMPL020 ✓ ## 実装概要 `MaintenanceService` クラスがDBとBlobストレージ間の整合性チェック、 期限切れキャッシュの削除、孤立ファイルの検出と... | ADR023 ✓ # Service Layer for Maintenance Operations ## Context and Problem Statement / コ... |
| MAINT | REQ010 ✓ 期限切れキャッシュの削除、孤立Blobファイルのガベージコレクション、時間ベースの一括削除(prune)等のメンテナンス操作ができること。 | ARCH010 ✓ ## コンポーネント構成 ```mermaid graph TD A[MaintenanceService] -->|操作集約| B[TaskDBBase... | SPEC020 ✓ ## インターフェース ```python class MaintenanceService: def clean_garbage(self, gra... | TST020 ✓ ## 目的 `MaintenanceService` は CLI やダッシュボードからのシステム管理操作を仲介するサービス層である。タスク詳細の表示ミスは運用... | IMPL020 ✓ ## 実装概要 `MaintenanceService` クラスがDBとBlobストレージ間の整合性チェック、 期限切れキャッシュの削除、孤立ファイルの検出と... | ADR029 ✓ # Recursive Storage Cleanup and Zombie Project Collection ## Context and Proble... |
| MAINT | REQ010 ✓ 期限切れキャッシュの削除、孤立Blobファイルのガベージコレクション、時間ベースの一括削除(prune)等のメンテナンス操作ができること。 | ARCH010 ✓ ## コンポーネント構成 ```mermaid graph TD A[MaintenanceService] -->|操作集約| B[TaskDBBase... | SPEC020 ✓ ## インターフェース ```python class MaintenanceService: def clean_garbage(self, gra... | TST020 ✓ ## 目的 `MaintenanceService` は CLI やダッシュボードからのシステム管理操作を仲介するサービス層である。タスク詳細の表示ミスは運用... | IMPL020 ✓ ## 実装概要 `MaintenanceService` クラスがDBとBlobストレージ間の整合性チェック、 期限切れキャッシュの削除、孤立ファイルの検出と... | ADR041 ✓ # Garbage Collection Strategy for Abandoned Temporary Files ## Context and Prob... |
| リンク方向 | カバー数 | カバー率 | 未カバー |
|---|---|---|---|
| ARCH → REQ | 1 / 1 | 100.0% | — |
| SPEC → ARCH | 1 / 1 | 100.0% | — |
| TST → SPEC | 1 / 1 | 100.0% | — |
| IMPL → SPEC | 1 / 1 | 100.0% | — |
| ADR → REQ | 1 / 1 | 100.0% | — |
期限切れキャッシュの削除、孤立Blobファイルのガベージコレクション、時間ベースの一括削除(prune)等のメンテナンス操作ができること。
親: —
子: ADR016, ADR018, ADR020, ADR021, ADR023, ADR029, ADR041, ARCH010
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() |
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をキックし、手動メンテなしでの健康度を維持 |
親: REQ010
子: SPEC020
class MaintenanceService:
def clean_garbage(self, grace_period: int = 3600) -> GarbageStats: ...
def prune(self, days: int, func_name: str | None = None) -> int: ...
def clear(self, func_name: str | None = None) -> int: ...
clean_garbage)delete_expired() でDBから古いタスクを除去blob_key とストレージ内の全ファイルを比較grace_period 以上経過したものを削除| パラメータ | デフォルト | 説明 |
|---|---|---|
grace_period |
3600 |
孤立Blobを削除するまでの猶予期間(秒)。並行実行中の保存を保護する |
days |
なし | prune で削除対象とする経過日数 |
clear(func_name) は前方一致ではなく、完全一致(または glob)で判定する。親: ARCH010
MaintenanceService は CLI やダッシュボードからのシステム管理操作を仲介するサービス層である。タスク詳細の表示ミスは運用者の誤判断を招き、メンテナンス操作の不備はデータの意図しない削除に繋がる。
None が返却され、例外が送出されないことreferences: tests/unit/test_maintenance.py
親: SPEC020
子: —
MaintenanceService クラスがDBとBlobストレージ間の整合性チェック、
期限切れキャッシュの削除、孤立ファイルの検出といったガベージコレクション(GC)を担当。
主にCLIの beautyspot gc コマンドから呼び出される。
対象となる DB (TaskDBMaintenable) とストレージ (BlobStorageMaintenable) を受け取り、
複数のフェーズに分けてクリーンアップを実行する。
clean_garbage() は以下の順序で安全にGCを実行する:
1. 期限切れDBレコードの削除
2. Blobストレージの一時ファイル(.spot_tmp)のクリーンアップ
3. 孤立したBlobファイル(DBに存在しないファイル)の特定
4. 孤立ファイルの削除
5. 空ディレクトリの剪定
実行中のタスクがBlobを書き込んだ直後で、まだDBにメタデータが保存されていない
タイミングでGCが走ると、必要なファイルが孤立と誤認されるリスクがある。
これを防ぐため、orphan_grace_seconds(デフォルト60秒)より新しいファイルは
孤立判定から除外する設計とした。
scan_garbage() は、ストレージの全キーを列挙し、
DBの get_blob_keys() との差分を取ることで行うscan_orphan_projects() も提供されるreferences: src/beautyspot/maintenance.py
親: SPEC020
子: —
beautyspot は「試行錯誤の高速化」を掲げていますが、ユーザーが失敗した実験結果(キャッシュ)を即座に破棄する手段が Dashboard 上に存在しませんでした。
また、キャッシュ削除のロジックが cli.py に直接実装されており、Core API (Spot クラス) として提供されていないため、プログラムからの制御やテストが困難な状態でした。
加えて、削除機能を実現するには BlobStorageBase (Storage backend) に物理ファイルを削除するインターフェースが必要ですが、これまでの定義には save と load しか存在しませんでした。
delete メソッドを追加し、Storage インターフェースを拡張して、Dashboard からも操作可能にする。Chosen option: Option 2.
Core API への delete メソッドの追加
Spot.delete(cache_key) を正式な API として追加します。このメソッドは、「DBレコードの削除」と「Blobファイルの削除」をアトミック(ベストエフォート)に行う責任を持ちます。
Storage Interface への拡張
BlobStorageBase 抽象基底クラスに delete(location) メソッドを追加します。
delete メソッドの実装が必要となります。Idempotency: ファイルが既に存在しない場合でもエラーを送出せず、静かに終了する(冪等な挙動)ことを推奨実装とします。
Dashboard への削除機能の露出
Dashboard に削除ボタンを配置します。誤操作を防ぐため、確認ダイアログを経由する UI とし、Spot.delete を呼び出します。
BlobStorageBase を継承したカスタムクラスを持つ既存ユーザーコードは、delete メソッドの実装が必要になる。親: REQ010
子: —
現在、beautyspot のテストスイート(tests/)は、主にユニットテストや特定の機能(シリアライザー、リミッターなど)に焦点を当てたテストで構成されている。これらは個々のコンポーネントの正確性を保証する上では有効だが、以下の課題がある:
CliRunner やモックオブジェクトを使用しているため、実際のファイルシステムや SQLite データベースに対する副作用(ファイルの生成、削除、ロック競合など)を検証できていない。stats, clean など)と Python API による実行結果の整合性を、ユーザーの一連の操作フローとして検証する仕組みがない。「個々の部品は正しいが、組み合わせると意図した通りに動かない」という統合レベルのバグを防ぐため、テスト戦略を見直す必要がある。
tests/integration/ ディレクトリを新設し、E2E(End-to-End)シナリオテストを導入する。Chosen option: Option 3.
テストピラミッドの上層として、以下の戦略でインテグレーションテストを導入する:
tests/integration/ を新設し、ユニットテストとは明確に分離する。beautyspot CLI コマンド(stats, list 等)を実行し、出力が正しいことを検証する。tmp_path フィクスチャを活用)。beautyspot.Spot クラスを実際にインスタンス化し、ユーザーコードと同じインターフェース経由で操作を行う。def test_ml_pipeline_lifecycle(spot_env):
# 1. Pipeline Definition (Load -> Process -> Train)
# 2. Initial Run (Assert all executed)
# 3. Cache Hit Run (Assert none executed)
# 4. Version Upgrade of Middle Task (Assert downstream re-executed)
# 5. CLI "stats" command check (Assert DB integrity)
親: REQ010
子: —
spot.delete(key) 機能は、特定のタスクに関連する「キャッシュレコード(DB)」と「実データ(Blob/File)」の両方を削除することを目的としています。
ローカルファイルシステムやS3などの外部ストレージにおいて、Blobの削除操作は様々な理由(ネットワーク障害、一時的な権限エラー、ファイルが既に手動で削除されている等)で失敗する可能性があります。
このとき、厳密な整合性を求めて「Blob削除に失敗したらDBレコードの削除もロールバック(中断)する」という実装にすると、以下の問題が発生します:
Chosen option: Option 2.
削除操作において 「メタデータの削除を優先する」 ポリシーを採用します。
WARNING レベルのログを出力してエラーを捕捉する。beautyspot の管理下からタスクを確実に削除できる。親: REQ010
子: —
プロジェクトの規模拡大に伴い、以下の課題が発生しています:
Chosen option: Option 2.
以下のツールを導入し、開発フローに統合します。
tools/ に配置する。graphviz (システムパッケージ) のインストールが必須となる。親: REQ010
子: —
現在、src/beautyspot/cli.py には以下のビジネスロジックが直接記述されています:
1. Pruning: タイムスタンプに基づく古いタスクの削除。
2. Cleaning: 孤立した Blob ファイルの特定と削除(ガベージコレクション)。
これらのロジックは sqlite3 ドライバに直接アクセスしたり、ファイルパスを操作したりしており、TaskDB や BlobStorageBase の抽象化をバイパスしています。これにより、CLI が SQLite やローカルファイルストレージの詳細に密結合し、他のインターフェース(スクリプトや Web UI)から同じロジックを再利用できないという問題が発生しています。また、S3 などの外部ストレージに対する「掃除」機能の実装も困難になっています。
Spot クラスに追加する。MaintenanceService を新設し、管理ロジックを分離する。Chosen option: Option 3.
メンテナンスロジックを cli.py から抽出し、専用の MaintenanceService (src/beautyspot/maintenance.py) に移動します。
Spot クラスはアプリケーションの実行コンテキストとキャッシュ制御に集中させ、メンテナンス(行政的タスク)は別の関心事として分離します。
MaintenanceService: レコードの整理、孤立した Blob の掃除、履歴の照会などを担当。TaskDB と BlobStorageBase を調整(Orchestrate)する。Spot: 実行時の登録 (mark) と実行 (run) に専念する。Spot) を軽量に保ちつつ、管理ロジックを独立して進化させることができる。list_keys 等を実装していれば、S3 等でも自動的に GC がサポートされる。TaskDB や BlobStorageBase に新たなメソッド(list_keys, get_blob_refs 等)を追加する必要がある。親: REQ010
子: —
これまでの beautyspot clean コマンドの実装には、以下の課題がありました。
clean コマンドは個別のファイル削除のみを行い、空になったディレクトリを削除していませんでした。.DS_Store などのシステムファイルが存在する場合、ディレクトリが空とみなされず、削除できないケースがありました。.db) のみを削除した場合、対応する Blob ディレクトリが管理外のゴミ(ゾンビプロジェクト)として残り続け、削除する手段がありませんでした。.DS_Store 等)に惑わされず、意図した通りにクリーンアップを実行できること。gc コマンドを導入する。Chosen option: Option 2.
ストレージのクリーンアップ戦略を以下のように刷新します。
clean コマンドを2段階に分割します。
* Phase 1 (File Deletion): DB参照のない孤立ファイルを削除。
* Phase 2 (Directory Pruning): LocalStorage に prune_empty_dirs() メソッドを追加し、再帰的に空ディレクトリを一括削除。
システム生成ファイル(.DS_Store, Thumbs.db, desktop.ini)のみが存在する場合、それらを「実質的な空」とみなし、強制的に削除した上で親ディレクトリを削除します。
DBファイルが失われたプロジェクトを回収するための gc コマンドを実装します。対応する .db ファイルが存在しないディレクトリを「ゾンビプロジェクト」と判定し、shutil.rmtree で強制的に一括削除します。
Storage クラス内に隠蔽できた。os.walk による再帰探索を行うため、ファイル数が膨大な場合にオーバーヘッドが発生する。親: REQ010
子: —
Blob データの保存時、アトミックな書き込みを実現するために一時ファイルを作成し、完了後にリネームする設計を採用しています。しかし、アンチウイルスソフトやバックアッププロセスの介入によりリネームが失敗するエッジケースが存在します。この際、一時ファイルの削除もロックにより失敗すると、一時ファイルがストレージ上に永久に残留し続ける(ストレージリーク)という問題がありました。
MaintenanceService による遅延ガベージコレクション (GC) を導入する。Chosen option: Option 3.
アトミック書き込みのフェイルセーフとして、以下の機構を導入します。
.spot_tmp という専用のサフィックスを付与し、追跡を容易にします。MaintenanceService.clean_garbage に、一時ファイルの削除処理を統合します。親: REQ010
子: —