Skip to content

Instantly share code, notes, and snippets.

@mszajna
Last active April 23, 2020 09:57
Show Gist options
  • Save mszajna/94708c97d6f2d33238ce35cd421d0771 to your computer and use it in GitHub Desktop.
Save mszajna/94708c97d6f2d33238ce35cd421d0771 to your computer and use it in GitHub Desktop.
(require '[datomic.api :as d])
; Datomic transaction are ACID which is achieved processing only one at a time.
; For certain workloads it's a good enough model. The built in transaction functions
; offer enforcing uniqueness and a compare-and-swap operation. They work great but
; are often too little for expressing more complex business rules.
; For those, datomic offers transaction functions, that you can install storing the
; source code in the DB.
(def conn (let [url "datomic:mem:call"] (d/create-database url) (d/connect url)))
(def tx-println
(d/function
'{:lang "clojure"
:params [db s]
:code (println s)})) ; this one returns nil - empty transaction. NB. side effects in tx are to be avoided!
(d/transact conn [{:db/ident :tx-println :db/fn tx-println}])
(d/transact conn [[:tx-println "look ma, I'm in a tx!"]])
; This is pretty cool, although maintaining the parity between the source code
; of this function and the value stored in Datomic is often painful. Datomic also
; advocates acretion, so ideally a function wouldn't change once transacted. This
; gets in the way of fast-paced development.
; Introducing 'call' - the last transaction function you ever have to install.
; It allows running a transaction function without installing it, compiling the
; code the first time it's run.
(def call
(d/function
'{:lang "clojure"
:params [db f & args]
:code (do (when-not (resolve 'user/datomic-function-mem)
(intern 'user 'datomic-function-mem (memoize datomic.api/function)))
(apply (user/datomic-function-mem f) db args))}))
(d/transact conn [{:db/ident :call :db/fn call}]) ; install the function
(d/transact conn [[:call tx-println "look ma, I'm in a tx!"]]) ; run another, referring its source, not :db/ident
(d/transact conn [[:call '{:language "clojure" :params [db] :code (pringln "risky bussiness")}]])
; Transaction functions tend to be small and caching forever shouldn't be too much strain
; on the memory, as long as you avoid generating source code at runtime, which could also
; be dangerous - think eval. The call below empties the cache.
(d/transact conn [[:call '{:language "clojure" :params [db] :code (ns-unmap 'user 'datomic-function-mem)}]])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment