fix(addons): dedup existing Ring devices before the unique index (R15)
Codex R15: on an upgrade where the pre-index race already produced duplicate config.ring_id rows, CREATE UNIQUE INDEX would abort the migration. Now the migration first collapses duplicates (keep the earliest id per ring_id; the rest cascade-delete their entities/state) using portable subquery SQL that runs on Postgres (prod) and SQLite (tests) alike. Verified: 3 duplicates → 1 on Postgres, index then created; suite 43 green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>feat/phase-1-bootstrap
parent
8086a9647b
commit
ca98e21c18
|
|
@ -7,6 +7,23 @@ return new class extends Migration
|
|||
{
|
||||
public function up(): void
|
||||
{
|
||||
// Reconcile any duplicates the pre-index race may already have created, otherwise the
|
||||
// CREATE UNIQUE INDEX below would abort on existing data. Keep the earliest row per
|
||||
// ring_id; the rest cascade-delete their entities/state (both FKs cascadeOnDelete).
|
||||
// Portable subquery form (works on Postgres in prod and SQLite in tests).
|
||||
DB::statement(<<<'SQL'
|
||||
DELETE FROM devices
|
||||
WHERE config->>'ring_id' IS NOT NULL
|
||||
AND id NOT IN (
|
||||
SELECT min_id FROM (
|
||||
SELECT MIN(id) AS min_id
|
||||
FROM devices
|
||||
WHERE config->>'ring_id' IS NOT NULL
|
||||
GROUP BY config->>'ring_id'
|
||||
) keep
|
||||
)
|
||||
SQL);
|
||||
|
||||
// Concurrent first-sight Ring messages (retained info + motion on bridge startup) must not
|
||||
// create two rows for one device. A partial unique index on the JSON ring_id lets the
|
||||
// second concurrent insert fail so the ingest job can re-fetch the winner (race-safe).
|
||||
|
|
|
|||
Loading…
Reference in New Issue