Skip to content

Instantly share code, notes, and snippets.

@tgk
Last active October 13, 2015 03:58
Show Gist options
  • Save tgk/4136272 to your computer and use it in GitHub Desktop.
Save tgk/4136272 to your computer and use it in GitHub Desktop.

Example of how rolling deploys might have been easy on Datomic databases, had Datomic supported change of schema in db snapshots. The example will not work, but illustrates how easy it would have been to operate on a new schema on an old database by performing migration in-memory when needed.

(ns datomic-migration.core
(:use [datomic.api :as d]))
;; This example will use an in-memory database, but the observations
;; are equally valid on any other storage engine
(def uri "datomic:mem://datomic-migration")
(d/delete-database uri)
(d/create-database uri)
(def conn (d/connect uri))
;; The basic schema only defines the name property for a person
(def schema-tx
[{:db/id (d/tempid :db.part/db)
:db/ident :person/name
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}])
@(d/transact conn schema-tx)
;; We insert a record in the schema
@(d/transact
conn
[{:db/id #db/id[:db.part/user]
:person/name "Thomas"}])
;; ... and record the transaction time after having done so
(defn latest-transaction-time []
(ffirst
(reverse (sort (d/q '[:find ?when :where [_ :db/txInstant ?when]]
(d/db conn))))))
(def one-person-time (latest-transaction-time))
;; ... before adding another person to the system
@(d/transact
conn
[{:db/id #db/id[:db.part/user]
:person/name "Maximilien"}])
;; We want to add a "debt" property for our persons, so we
;; define a new schema attribute
(defn migrate-schema-tx []
[{:db/id (d/tempid :db.part/db)
:db/ident :person/debt
:db/valueType :db.type/long
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}])
;; and supply a function for generating the transactions nessecary
;; for migrating the data in a database, giving all existing persons
;; a debt of '0'
(defn migrate-data-tx [db]
(let [persons (d/q '[:find ?p :where [?p :person/name _]] db)]
(for [[person-id] persons]
{:db/id person-id
:person/debt 0})))
;; We can now transact these two on the database to perform the
;; migration
@(d/transact conn (migrate-schema-tx))
@(d/transact conn (migrate-data-tx (d/db conn)))
;; print-persons prints all persons with their debts, and will only
;; work on databases with the new schema
(defn print-persons [db]
(doseq [[name debt] (d/q
'[:find ?n ?d
:where
[?p :person/name ?n]
[?p :person/debt ?d]]
db)]
(println ">>" name ":" debt)))
(print-persons (d/db conn))
;; To use the function on a snapshot of an old database, however,
;; is quite easy. We only need to take a snapshot from before,
;; peform our migration steps, and finally call the function
(let [db (d/as-of (d/db conn) one-person-time)
db (d/with db (migrate-schema-tx))
db (d/with db (migrate-data-tx db))]
(print-persons db))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment