fix: quarantine failed product cleanups in failed_cleanup state

This commit is contained in:
2026-09-12 15:00:33 +08:00
parent b3ab1bfc52
commit bd78eef161
7 changed files with 149 additions and 19 deletions
+28 -10
View File
@@ -318,7 +318,9 @@ class ProductArchiveStore:
raise ValueError(f"unknown version_id for product: {version_id}")
def mark_pending_cleanup(self, product_id: str, now: datetime) -> ProductRecord:
self.get_product(product_id)
product = self.get_product(product_id)
if product.status not in ("active", "failed_cleanup"):
raise ValueError("product cannot be marked pending cleanup")
purge_after = now + timedelta(days=30)
self._execute(
"UPDATE products SET status = ?, purge_after = ?, updated_at = ? WHERE product_id = ?",
@@ -355,8 +357,8 @@ class ProductArchiveStore:
)
conn.execute(
"""UPDATE cleanup_records SET status = ?, completed_at = ?
WHERE product_id = ? AND status = ?""",
("restored", restored_at.isoformat(), product_id, "pending_cleanup"),
WHERE product_id = ? AND status IN ('pending_cleanup', 'failed_cleanup')""",
("restored", restored_at.isoformat(), product_id),
)
return self.get_product(product_id)
@@ -405,13 +407,29 @@ class ProductArchiveStore:
return None
return self._product_from_row(row)
def release_product_purge(self, product_id: str) -> None:
"""Return a failed physical deletion claim to its pending state."""
self._execute(
"""UPDATE cleanup_records SET status = 'pending_cleanup'
WHERE product_id = ? AND status = 'purging'""",
(product_id,),
)
def fail_product_purge(self, product_id: str) -> ProductRecord:
"""Quarantine a purge whose physical deletion failed mid-way."""
with self._lock, self._connect() as conn:
conn.execute("BEGIN IMMEDIATE")
claim = conn.execute(
"""SELECT 1 FROM cleanup_records
WHERE product_id = ? AND status = 'purging' LIMIT 1""",
(product_id,),
).fetchone()
if claim is None:
raise ValueError("product purge is not claimed")
updated = conn.execute(
"""UPDATE cleanup_records SET status = 'failed_cleanup'
WHERE product_id = ? AND status = 'purging'""",
(product_id,),
)
if updated.rowcount != 1:
raise ValueError("product purge is not claimed")
conn.execute(
"UPDATE products SET status = 'failed_cleanup', updated_at = ? WHERE product_id = ?",
(_now().isoformat(), product_id),
)
return self.get_product(product_id)
def finalize_product_purge(self, product_id: str, now: datetime) -> ProductRecord:
"""Atomically mark a claimed product purged after file removal."""