diff --git a/backend/api/documents.py b/backend/api/documents.py index 2a119aa..2715444 100644 --- a/backend/api/documents.py +++ b/backend/api/documents.py @@ -658,11 +658,13 @@ async def delete_document( }, ) - ok = await storage.delete_document(session, doc_id, skip_quota=is_cloud) + # auto_commit=False defers the commit so the audit log write below happens + # in the same transaction — avoids the split-transaction gap (WR-08). + ok = await storage.delete_document(session, doc_id, skip_quota=is_cloud, auto_commit=False) if not ok: raise HTTPException(404, "Document not found") - # D-13: document deleted event — written AFTER successful delete, size_bytes only (T-04-07-02) + # D-13: document deleted event — written in the same transaction as the delete (WR-08). await write_audit_log( session, event_type="document.deleted", diff --git a/backend/services/storage.py b/backend/services/storage.py index 7a04de8..f0844d4 100644 --- a/backend/services/storage.py +++ b/backend/services/storage.py @@ -140,7 +140,12 @@ async def list_metadata( return rows -async def delete_document(session: AsyncSession, doc_id: str, skip_quota: bool = False) -> bool: +async def delete_document( + session: AsyncSession, + doc_id: str, + skip_quota: bool = False, + auto_commit: bool = True, +) -> bool: """Delete a document's MinIO object and its PostgreSQL row. Returns False if the document is not found; True on success. @@ -149,6 +154,11 @@ async def delete_document(session: AsyncSession, doc_id: str, skip_quota: bool = skip_quota=True skips the quota decrement — used for cloud-stored documents that were never charged against the user's MinIO quota (T-06.2-03-01). + + auto_commit=False defers the session.commit() to the caller, allowing the + caller to write an audit log entry in the same transaction before committing + (avoids the split-transaction gap where a failed audit write loses the record + while the document row is already gone). """ try: uid = uuid.UUID(doc_id) @@ -179,7 +189,8 @@ async def delete_document(session: AsyncSession, doc_id: str, skip_quota: bool = ) await session.delete(doc) - await session.commit() + if auto_commit: + await session.commit() return True