diff --git a/Proposal:-application-schema-coordination-and-versioning.md b/Proposal:-application-schema-coordination-and-versioning.md index 9aae206..ee779d9 100644 --- a/Proposal:-application-schema-coordination-and-versioning.md +++ b/Proposal:-application-schema-coordination-and-versioning.md @@ -82,7 +82,7 @@ This is similar to the 'user version' functionality in SQLite, with important di - The datom store's schema consists of `{name, version}` pairs, not a single version. - Schema fragments have a globally unique identifier, allowing them to be shared across applications. - Applications are made aware at runtime when schema fragments change. -- Many schema changes -- adding attributes, altering indexing choices, or weakening constraints -- can be performed automatically with no need to supply migration code. +- Many schema changes — adding attributes, altering indexing choices, or weakening constraints — can be performed automatically with no need to supply migration code. Under this proposal different applications can each ship shared schema fragments, coordinate upgrades, avoid conflicts in a large majority of cases, and safely detect real conflicts when they arise. @@ -196,8 +196,48 @@ The schema handling code performs a sequence of operations: Most migrations will be very simple — some per-fragment pre- or post-upgrade code, if that — but this sequence exists if more complicated changes are required. Note that each fragment has pre/post/rename stages, as does the application itself. This should allow for simpler code sharing, avoiding the need for schema fragments to accommodate application logic. +For example, an imaginary upgrade sequence for developers who made a bunch of errors in a previous release might be: + +> From: page=2, save=1 +> To: page=3, save=2 +> - App 'pre': clean up old history so we don't have to fix it. +> - Page 'pre': fix duplicate visits, prior to imposing a uniqueness constraint. +> - Save 'pre': delete saves for pages that no longer exist, prior to imposing a component constraint. +> - Page renames: none. +> - Save renames: rename `:save/instant` to `:save/savedAt` to fix a copy-paste error. +> - Page schema upgrade, 2->3: apply uniqueness constraint to `:page/visit`. +> - Save schema upgrade, 1->2: set `isComponent` to `true` to avoid orphans when history is deleted. +> - Page 'post': add inter-visit relationships to fixed pages using new vocabulary. +> - Save 'post': none. +> - App 'post': none. + +Note that the page and save pre/rename/upgrade/post sequences are independent of each other and of the app sequence. Another application might share the page logic and perform this sequence first: + +> From: page=2 +> To: page=3 +> - App 'pre': clean up old history so we don't have to fix it. +> - Page 'pre': fix duplicate visits, prior to imposing a uniqueness constraint. +> - Page renames: none. +> - Page schema upgrade, 2->3: apply uniqueness constraint to `:page/visit`. +> - Page 'post': add inter-visit relationships to fixed pages using new vocabulary. +> - App 'post': none. + +leaving our first application to only upgrade the save schema fragment: + +> From: save=1 +> To: save=2 +> - Save 'pre': delete saves for pages that no longer exist, prior to imposing a component constraint. +> - Save renames: rename `:save/instant` to `:save/savedAt` to fix a copy-paste error. +> - Save schema upgrade, 1->2: set `isComponent` to `true` to avoid orphans when history is deleted. +> - Save 'post': none. +> - App 'post': none. + +After commit, the app updates its caches and other metadata, secure in the knowledge that the entire migration happened atomically and was persisted to disk. + ### Per-fragment options +Renames can be specified in the schema fragment itself: + ``` {:schema/name "org.mozilla.core.page" :schema/version 3 @@ -216,3 +256,5 @@ Most migrations will be very simple — some per-fragment pre- or post-upgrade c :db/doc "A visit to the page." :db.install/_attribute :db.part/db}]} ``` + +Pre- and post-upgrade functions, of course, can't easily be specified declaratively. They should instead be functions `(f conn from to)` that only have side effects on the provided connection, and raise on error. \ No newline at end of file