mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
A restore of an account with 1.9 million readings, most of them deleted by the nightly consolidation and still awaiting the purge, was cancelled in its clearing step: `DELETE FROM measurements` ran past the 60 s statement timeout every pooled connection carries, and the whole restore rolled back with a message that named no cause (#1031). Most of that statement was not the delete. Two columns point into `measurements` with ON DELETE SET NULL and had no index, so Postgres scanned `personal_records` and `ecg_recordings` once per deleted reading: 38 of 40 s on a seeded account with 300 personal records. Migration 0362 indexes both, which also speeds up the tombstone purge and account deletion. The restore transaction now sets its own statement and idle-in-transaction limits to the transaction budget with SET LOCAL, and the budget counts the rows the account holds as well as the rows the file carries. A rolled-back restore names what the database refused it over (timeout, lock, constraint, storage, connection, other) on the job row, the console, the audit row and the wide event.
28 lines
1.4 KiB
SQL
28 lines
1.4 KiB
SQL
-- Index the two columns that point into `measurements`.
|
|
--
|
|
-- `personal_records.source_measurement_id` and `ecg_recordings.measurement_id`
|
|
-- are foreign keys into `measurements` with ON DELETE SET NULL, and neither
|
|
-- column had an index. Postgres enforces such a key with a trigger that runs
|
|
-- once per deleted measurement and looks the referencing rows up by that
|
|
-- column. Without an index every lookup is a sequential scan of the whole
|
|
-- referencing table, so deleting N readings costs N scans.
|
|
--
|
|
-- On a seeded account of 1.89 million readings with 300 personal records, the
|
|
-- restore's `DELETE FROM measurements WHERE user_id = $1` spent 1.5 s deleting
|
|
-- and 38 s in these two triggers. On a slower host that crossed the 60 s
|
|
-- statement timeout every connection carries (`src/lib/db.ts`), the restore
|
|
-- was cancelled in its clearing step and rolled back (#1031). The tombstone
|
|
-- purge and account deletion delete readings the same way and paid the same
|
|
-- cost per row.
|
|
--
|
|
-- Additive only: `CREATE INDEX IF NOT EXISTS`, no data change. Not
|
|
-- CONCURRENTLY, because Prisma Migrate runs a migration in a transaction; both
|
|
-- tables are small next to `measurements`, and `migrate deploy` runs at boot
|
|
-- before the app serves traffic.
|
|
|
|
CREATE INDEX IF NOT EXISTS "personal_records_source_measurement_id_idx"
|
|
ON "personal_records" ("source_measurement_id");
|
|
|
|
CREATE INDEX IF NOT EXISTS "ecg_recordings_measurement_id_idx"
|
|
ON "ecg_recordings" ("measurement_id");
|