Files
HealthLog/prisma/migrations/0336_offhost_backup_ledger/migration.sql
T
Marc-André Bombeck 25f0310596 feat(backups): tell an empty ledger apart from an empty bucket
On the release that adds the ledger the table is empty, so on a host that has
been uploading nightly for months every account read Never, under a sentence
saying no copy had ever reached the bucket. That sentence is untrue on exactly
the morning an operator opens the new card for the first time, and it is a
claim the host cannot make: what is empty is the record, not the bucket.

The run now records every account it walks, not only the ones an object came
of. A walk that produced nothing moves the attempt instant and leaves the
success columns alone, so the two cases separate: no row at all is no record
yet, a row without a success is never. Never now means what the word says —
a run looked at this account and has never put anything there.

The card names the last check on the never row, so the state is a fact with a
timestamp rather than an assertion. Runbook and all seven bundles carry the
same two sentences. The integration file proves both arms against real
Postgres, including that a failed run leaves the previous good copy on the
row.
2026-09-10 10:15:31 +02:00

25 lines
1.1 KiB
SQL

-- One row per account, written by the nightly off-host backup job when an
-- object lands in the bucket. The admin console reads it to say, per account,
-- whether the newest off-host copy is as fresh as the schedule promises,
-- without the page ever talking to the bucket.
CREATE TABLE "offhost_backup_state" (
"user_id" TEXT NOT NULL,
-- Written on every account the run walks, success or not. A row's mere
-- existence is what lets the console tell "no run has reached this
-- account yet" from "a run reached it and nothing landed".
"last_attempt_at" TIMESTAMP(3) NOT NULL,
-- NULL until an object lands for this account.
"last_success_at" TIMESTAMP(3),
-- BIGINT, not INTEGER: a single object may occupy 8 MiB x 10 000 multipart
-- parts, so the uploader permits 80 GB and a 32-bit column would refuse
-- every account past 2.1 GB.
"size_bytes" BIGINT,
CONSTRAINT "offhost_backup_state_pkey" PRIMARY KEY ("user_id")
);
ALTER TABLE "offhost_backup_state"
ADD CONSTRAINT "offhost_backup_state_user_id_fkey"
FOREIGN KEY ("user_id") REFERENCES "users"("id")
ON DELETE CASCADE ON UPDATE CASCADE;