Repository navigation
docs(f4): an older backup is brought forward, not refused - #103
Merged
Merged
Conversation
F4-R23 refused a backup in either direction. Refusing the newer one is the only honest answer — migrations do not roll back, so there is no path from that database to a shape this build reads. Refusing the older one was not: it is the shape this build's own migrations were written to convert, and the desktop was already converting it, by accident, on the next request through a gate the phone does not have. The cost of refusing it was stranding most backups a reader holds more than a few weeks, and on mobile nearly all of them — the restore screen is reached from a fresh install of whatever the store is serving, and the reader cannot choose an older version. So F4-R26 brings it forward on every shell rather than as a consequence of one shell's startup gate, and says so to the reader first: a restore is now a schema-changing operation for that case, which is a change in what it promises. F4-R27 is where it runs. A migration that fails part way is not recoverable on this store — the statements before it are applied and committed, the failing one is not recorded, and re-running meets the half of its own work that already landed. Measured: a forward run whose last step throws leaves the table that step created and no row saying it ran. So the run happens on the copy the restore already staged, the swap reads that copy only once every migration has run, and a failure refuses with the live database never opened. That fixes the ordering too. The forward run comes before the pre-restore snapshot, so a refused restore still leaves nothing at all, and a restore that migrates and then swaps takes its snapshot like any other. F4-R24 is reworded: one direction is refused for being ahead, the other only for failing to come forward, and those ask different things. Signed-off-by: Wessel Verheij <info@nightworks.io>
…ites Two things a restore does to a backup rewrite it. Lifting the carried keyring out drops the table that carried it, leaving a backup that restores the ledger once and the keys never again. Bringing an older schema forward writes a whole schema into it, leaving one no earlier build can read. Both were already meant to happen on a copy, and the page said so about the forward run only — as a property of that run rather than of a restore. A reader whose restore was refused has to be able to hand the same file to the next attempt, and it is often the only copy they have. F4-R28 states it as the unconditional one it has to be: not "when the backup carries a keyring", not "when something needs migrating". A guarantee that holds while a check agrees with the code it guards is not a guarantee. Signed-off-by: Wessel Verheij <info@nightworks.io>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



An older backup is brought forward, not refused
Amends
F4-R23/F4-R24and addsF4-R26/F4-R27, from spec#96 (merged) and the flag raised on beatrax#715.What changes, and why only half of it
F4-R23refused a backup in either direction. Refusing the newer one is the only honest answer and that half stands: migrations do not roll back, so there is no path from that database to a shape this build reads, and a pending-migration check cannot even see the mismatch.Refusing the older one was not honest. It is precisely the shape this build's own migrations were written to convert — and the desktop was already converting it, by accident, on the next request through
EnsureDatabaseReady, a gate the phone does not have. Refusing it stranded most backups a reader holds more than a few weeks, and on mobile nearly all of them: the restore screen is reached from a fresh install, a fresh install is whatever version the store is serving, and the reader cannot choose an older one.F4-R26therefore brings it forward as part of the restore, on every shell rather than as a consequence of one shell's startup gate — and requires the reader to be told first, because a restore that changes the schema is a change in what the command promises.F4-R27is the load-bearing half, and it is measuredA migration that fails part way through is not recoverable on this store.
SQLiteGrammarinherits$transactions = false, so Laravel does not wrap migrations in transactions on SQLite; the statements before the failure are applied and committed and the failing one is never recorded. The product repo already carries an incident of exactly this: a first-launch run died on migration eighteen of two hundred and nine and the app opened on thirteen tables of a hundred and two, with reinstall the only exit.Measured directly against this build before writing the requirement, on a staged copy taken to migration 200 of 262 and then run forward with a failing step appended:
That is survivable on a copy and unsurvivable on the reader's ledger. So the run happens on the copy the restore has already staged, the swap reads that copy only once every migration has run, and a failure refuses with the live database never opened.
Also measured, because the requirement is worthless if the conversion does not converge: a database stopped at 200 migrations and then brought forward is byte-for-byte the same schema as a fresh install — 369 objects each, identical names, identical normalised SQL.
The ordering it settles
The forward run comes before the pre-restore snapshot. A refused restore still leaves nothing at all — no snapshot, no trace — which is the property
F4-R25and the existing refusal ordering already promise. A restore that migrates and then swaps is not a refusal, and takes its snapshot like any other. That was an open question in the previous PR and is now stated rather than inferred.Ids
F4-R23andF4-R24are edited, not renumbered.F4-R26andF4-R27are new;F4-R25is untouched. Nothing is renumbered and no vacated number is reused.python3 scripts/integrity.pyclean;markdownlint-cli2clean.F4-R28 added after review of the implementation. The page said the forward run happens on a copy, as a property of that run. It has to be a property of the restore: lifting a carried keyring out of a backup drops the table that carried it, and migrating it forward writes a schema into it — both consume the reader's file, and a refused restore has to leave them holding the file they started with. Stated unconditionally, because a guarantee that holds while a check agrees with the code it guards is not a guarantee. The implementation in beatrax#719 stages that copy on every restore rather than on the ones that need it.