Files

103 lines
5.8 KiB
Markdown

# Row 126 (#1620): bus schema 3h → 3i, for Mos
Round 4, under Mos's go (mos-0835, relayed in sage-0972) and Sage's scope (sage-0970). This step
lands on refactor only. It is not in the 8e7ea89a1 window. It goes live later through
`stack-release.sh activate ID BUSINESS --migrate` with Jason present, on Mos's go (decision 96,
conditions 1 and 2).
## What the step does
3h reserved `usage_records.account` as `account TEXT CHECK (account IS NULL)`
(`packages/bus/src/migrations/3g-3h.sql:10`). Row 126 writes the run's account name there, so the
CHECK has to go. SQLite cannot drop a CHECK in place, so `packages/bus/src/migrations/3h-3i.sql`
rebuilds the table:
1. drops the three append-only triggers: `usage_records_no_update`, `_no_delete` and `_no_replace`.
Mos checked that nothing else references the table: no index, view or foreign key;
2. renames `usage_records` to `usage_records_3h`;
3. creates `usage_records` with text byte-identical to `schema.sql`. The only change from 3h is
`account TEXT,`;
4. copies every row in seq order, `seq` included;
5. sets the table's `sqlite_sequence` value to the old table's value. It is not rebuilt from
`max(seq)`, so a seq that was handed out and then rolled back is never reused;
6. drops `usage_records_3h`, then recreates the three triggers with their 3h text.
Why it renames first: `ALTER TABLE … RENAME` stores the new name quoted in the table's SQL. The
open-time digest compares `sqlite_master` text, so renaming the new table into place would leave
`"usage_records"` in its text and fail `migration-target-mismatch` at import.
The Store runs each step inside one `BEGIN IMMEDIATE` transaction (`store.mjs`
`#migrateSchema`): the step's SQL, the meta version and digest rewrite, and the
`schema-mismatch` digest check before commit. Any failure rolls the whole step back, and the
store stays at 3h.
## The checks behind it
`packages/bus/tests/account-migration.test.mjs` works on a 3h store built from
`tests/fixtures/schema-3h.sql`. That file is pinned from 81f26551b's `schema.sql`, sha256 `bb2ba581…6950`.
No live store is read or copied.
- **Test 1.** The plain open refuses with `schema-mismatch`, and `migrateSchema` then reaches 3i.
Every table's rows are unchanged, seq 1-3 included, and the sequence stays at 50.
`sqlite_master` equals 3h's except the account line of usage_records; the three triggers are
back. quick_check and foreign_key_check are clean. The next insert gets seq 51 and takes an
account. UPDATE, DELETE, INSERT OR REPLACE and a reused seq all abort with
`usage_records is append-only`.
- **Test 2.** A step that fails partway, after the triggers are dropped and the copy is done,
leaves version 3h, `sqlite_master` and every row as they were. The real step then migrates the
same store.
- **Test 3.** A 3h engine (3h schema and SHA, no 3h→3i step) refuses a 3i store with
`schema-mismatch`, with or without `migrateSchema`. The store is left at 3i, unchanged.
## Running it
The only supported path is `scripts/stack-release.sh activate ID BUSINESS --migrate`, run by Jason
or Mos on Mos's go, with Jason present. Run `--plan` first: it prints
`migration steps: 3h to 3i` from the target copy's `store.mjs`, plus `needs: --migrate` and
`needs: Jason present`.
The activate path takes the pre-migrate backup itself, so nothing extra is needed. In
`run_steps` (`stack-release.sh:663-669`), with both units stopped and the store clean:
1. `in_chain` checks that 3h is in the target's chain, or the run rolls back `store-newer`;
2. without `--migrate`, the run rolls back `migration-needs-flag`;
3. `backup_store` copies `bus.sqlite` to `<dataRoot>/backups/stack/<ACTIVATION>/`, together with
`SOURCE.sha256`, `SHA256SUMS`, `MODES.txt`, `LISTING.txt`, `DATA_ROOT`, `STAMP`, `SCHEMA` (3h)
and `ROLLBACK_SET`, and checks that the copy's sha equals the source's. It sets TAKEN;
4. only then does `migrate-schema.mjs` run from the target copy.
## The way back
There is no 3i → 3h step, and no down-migration will be written. 3h code meeting a 3i store
refuses:
- the 3h Store throws `schema-mismatch` (test 3);
- `scripts/land-work/read.mjs` in a 3h tree refuses `bus-schema`;
- `stack-release.sh rollback` to a 3h copy refuses `schema-differs` unless `--restore` is given.
The way back is restoring the pre-migrate backup:
- **Inside the activate run.** If `migrate-schema.mjs` fails, or a later step or the gate fails,
`rollback_in_run` restores the backup this run took (TAKEN), puts back the previous units and
wrapper, and gates the old stack. A failed migration also rolls back its own transaction, so the
store is still 3h before the restore.
- **After a successful activate.**
`stack-release.sh rollback BUSINESS --restore <ACTIVATION> [--events N]`, where `<ACTIVATION>`
is the activate run's id (the backup directory's name). The backup must be of this data root at
the target's schema (3h), or the run refuses `restore-mismatch`. If events were written after
the backup's STAMP, the run needs `--events N` with that count and waits for Jason (decision 96,
condition 3). The store from before the restore moves aside to the run's post-attempt
directory; nothing is deleted.
Restoring drops whatever the 3i bus wrote after the backup, including any usage records with an
account. `--events N` counts events only. Usage records written in the same period are lost too.
## Between landing and the window
`scripts/land-work/read.mjs` imports `SCHEMA_VERSION` from the checkout's
`packages/bus/src/store.mjs` and refuses `bus-schema` when the store's meta differs. After 3i
lands on refactor, `scripts/land-work.sh` run from the canonical checkout against the live 3h store
will refuse `bus-schema` until the 3i window has run. This is the same gap 3g→3h had. Sage decides
whether land-work runs from a 3h tree in that period, or whether this row lands just before the
window.