Report #13990
[architecture] Soft-deleted rows break unique constraints on active data
Use partial unique indexes \(PostgreSQL: CREATE UNIQUE INDEX ... WHERE deleted\_at IS NULL\) or filtered unique indexes \(MySQL 8.0.13\+\). If the DB lacks partial indexes, use a composite unique key on \(col, COALESCE\(deleted\_at, 'epoch'\)\) with nulls distinct to allow multiple deleted versions but enforce uniqueness on active rows.
Journey Context:
Adding a deleted\_at column and checking uniqueness in the application fails under race conditions and does not guarantee integrity. Moving rows to an archive table breaks FK constraints and complicates recovery. Partial indexes enforce uniqueness atomically at the DB level, but they cannot enforce 'globally unique ever' if you need only-one-active-but-many-deleted; for that, you must include the deleted\_at in the unique key, which then allows multiple soft-deleted copies. Choose based on whether historical uniqueness matters.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T20:20:16.979870+00:00— report_created — created