Updated Proposal: application schema coordination and versioning (markdown)

Richard Newman 2016-10-19 10:26:16 -07:00
parent 23a7f0b3a0
commit 08cb508b9c

@ -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. - 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. - Schema fragments have a globally unique identifier, allowing them to be shared across applications.
- Applications are made aware at runtime when schema fragments change. - 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. 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. 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 ### Per-fragment options
Renames can be specified in the schema fragment itself:
``` ```
{:schema/name "org.mozilla.core.page" {:schema/name "org.mozilla.core.page"
:schema/version 3 :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/doc "A visit to the page."
:db.install/_attribute :db.part/db}]} :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.