Found during the /plan-eng-review of #54, verified on live data.
file_hash e4b519a0dccf2dd8... has both storage copies on disk and no row
in documents:
storage/originals/e4b519a0dccf2dd8d63e4a3c0e954728a3e1a1b66be8890fbe87c66007a0bdc9.pdf
storage/filed/0000-00-00-000000-e4b519a0.pdf
SELECT * FROM documents WHERE file_hash LIKE 'e4b519a0%' -> no row
Not soft-deleted, not status='failed', not stored_filename IS NULL — absent.
(The 4 rows with a NULL stored_filename are ids 24, 40, 70, 274; none is this one.)
No code path can produce this.
grep -rn "DELETE FROM documents" backend/ scripts/ migrations/ returns nothing.
delete_document (api/documents.py:287) only sets is_deleted = 1 and never unlinks.
- Every ingestion path inserts the row after writing the file, so the row should exist.
Leading theory: a restore. Document ids run 1..314 with no gaps. A database
restored to a point before this upload, while storage/ kept the newer files,
fits: the freed id would be reused by the next upload, leaving no gap behind.
Forensics caveat: atomic_copy copies mtime from the source, so timestamps in
filed/ reflect the original file, not when filing happened. File times here say
nothing about when the row disappeared.
Evidence preserved. Per the #54 review the file is moved to
data/quarantine/ rather than deleted — outside all three backup trees, so it
stops riding in backups and stops being counted, while the artifact survives.
The originals/ copy is untouched and still backed up.
Detection going forward: #54 adds a filed_orphans count to verify_backup.
Once the quarantine move lands, that count settles at 0, so any future
non-zero value means database and storage have diverged again. That is the whole
reason the count was kept out of the problems damage list.
Low priority — the likeliest explanation is a manual database edit. Filed so a
second occurrence is recognisable rather than a fresh mystery.
Related: #54, #49.
Found during the
/plan-eng-reviewof #54, verified on live data.file_hash e4b519a0dccf2dd8...has both storage copies on disk and no rowin
documents:Not soft-deleted, not
status='failed', notstored_filename IS NULL— absent.(The 4 rows with a NULL
stored_filenameare ids 24, 40, 70, 274; none is this one.)No code path can produce this.
grep -rn "DELETE FROM documents" backend/ scripts/ migrations/returns nothing.delete_document(api/documents.py:287) only setsis_deleted = 1and never unlinks.Leading theory: a restore. Document ids run 1..314 with no gaps. A database
restored to a point before this upload, while
storage/kept the newer files,fits: the freed id would be reused by the next upload, leaving no gap behind.
Forensics caveat:
atomic_copycopies mtime from the source, so timestamps infiled/reflect the original file, not when filing happened. File times here saynothing about when the row disappeared.
Evidence preserved. Per the #54 review the file is moved to
data/quarantine/rather than deleted — outside all three backup trees, so itstops riding in backups and stops being counted, while the artifact survives.
The
originals/copy is untouched and still backed up.Detection going forward: #54 adds a
filed_orphanscount toverify_backup.Once the quarantine move lands, that count settles at 0, so any future
non-zero value means database and storage have diverged again. That is the whole
reason the count was kept out of the
problemsdamage list.Low priority — the likeliest explanation is a manual database edit. Filed so a
second occurrence is recognisable rather than a fresh mystery.
Related: #54, #49.