fix(pipeline): tolerate duplicate ORIGINAL_PDF artifacts in document lookup
Repeated knowledge-ingest of identical content (same sha256, new asset_id -> new canonical object key) reuses the Document row but appends a second ORIGINAL_PDF artifact, because ensure_artifact identity is (document_id, storage_key). process_document_id then crashed with MultipleResultsFound on scalar_one_or_none and Celery retried forever. New latest_artifact helper picks the newest row deterministically (created_at DESC, id DESC) — every duplicate references byte-identical objects (sha256 verified at ingest), so the latest reference is always safe. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -51,3 +51,32 @@ def ensure_artifact(
|
||||
)
|
||||
db.add(artifact)
|
||||
return artifact
|
||||
|
||||
|
||||
def latest_artifact(
|
||||
db: Session,
|
||||
*,
|
||||
document_id: uuid.UUID,
|
||||
artifact_type: str,
|
||||
) -> DocumentArtifact | None:
|
||||
"""Newest artifact of the given type, or ``None``.
|
||||
|
||||
Duplicates of one type are legal: repeated knowledge-ingest of identical
|
||||
content (same sha256, new asset_id → new canonical object key) appends a
|
||||
second ORIGINAL_PDF row because ensure_artifact identity is
|
||||
(document_id, storage_key). All such rows reference byte-identical objects
|
||||
(sha256 verified at ingest), so the newest reference is always a safe,
|
||||
deterministic pick — never scalar_one_or_none here (G6 MultipleResultsFound).
|
||||
"""
|
||||
return (
|
||||
db.execute(
|
||||
select(DocumentArtifact)
|
||||
.where(
|
||||
DocumentArtifact.document_id == document_id,
|
||||
DocumentArtifact.artifact_type == artifact_type,
|
||||
)
|
||||
.order_by(DocumentArtifact.created_at.desc(), DocumentArtifact.id.desc())
|
||||
)
|
||||
.scalars()
|
||||
.first()
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user