Skip to content

storage: a document has files on disk with no database row, and no code path can produce it #64

Description

@LevMuchnik

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions