Skip to content

Instantly share code, notes, and snippets.

@lgessler
Last active May 6, 2021 06:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lgessler/7ad8b38e6860e75d6935fde4a8a1336c to your computer and use it in GitHub Desktop.
Save lgessler/7ad8b38e6860e75d6935fde4a8a1336c to your computer and use it in GitHub Desktop.
Crux transaction function-backed mutations to avoid race conditions
(ns foo (:require [crux.api :as crux]))
(defmacro deftx [name bindings & body]
"Defines a function used for mutations that uses a Crux transaction function under the hood.
body must return a valid Crux transaction vector, with any non-clojure.core variables fully
qualified. `install-tx-fns` must be called on namespaces where deftx is used for the function
to work."
(let [kwd-name (keyword (str *ns*) (str name))
symbol-name (symbol (str name))]
`(def
~(vary-meta
symbol-name
assoc
:crux-tx-fn
`(fn [node#]
(crux/submit-tx node# [[:crux.tx/put {:crux.db/id ~kwd-name
:crux.db/fn (quote (fn ~bindings
~@body))}]])))
(fn ~symbol-name [node# & ~'args]
(crux/await-tx
node#
(crux/submit-tx node# (log/spy [(into [:crux.tx/fn ~kwd-name] ~'args)])))))))
(defn evict-async [node eid] (crux/submit-tx node [[:crux.tx/evict eid]]))
(defn evict [node eid] (crux/await-tx node (evict-async node eid)))
(defn install-tx-fns [node namespaces]
"Given a node and a seq of namespace symbols, scan all public vars
and use any :crux-tx-fn in their metadata to install the tx-fn on
the node"
(doseq [ns-symbol namespaces]
(when-let [ns (the-ns ns-symbol)]
(doseq [[vname v] (ns-publics ns)]
(when-let [tx-install-fn (some-> v meta :crux-tx-fn)]
;; evict any already-existing entities with the tx-fn's id
(evict node (keyword (str ns) (str vname)))
(tx-install-fn node))))))
;; example usage
(ns foo.user (:require [crux.api :as crux]))
(foo/deftx set-name [node eid name]
(let [entity (crux.api/entity (crux.api/db node) eid)]
[[:crux.tx/put (assoc entity :name name)]]))
(ns foo.crux-node)
(def crux-node ...)
(foo/install-tx-fns crux-node '[foo.user])
(ns foo.bar)
(foo.user/set-name foo.crux-node/crux-node :my-user "new name")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment