Last active
May 6, 2021 06:42
-
-
Save lgessler/7ad8b38e6860e75d6935fde4a8a1336c to your computer and use it in GitHub Desktop.
Crux transaction function-backed mutations to avoid race conditions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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