How do I undo or reverse a transaction in datomic?
The basic procedure:
- Retrieve the datoms created in the transaction you want to undo. Use the transaction log to find them.
- Remove datoms related to the transaction entity itself: we don't want to retract transaction metadata.
- Invert the "added" state of all remaining datoms, i.e., if a datom was added, retract it, and if it was retracted, add it.
- Reverse the order of the inverted datoms so the bad-new value is retracted before the old-good value is re-asserted.
- Commit a new transaction.
In Clojure, your code would look like this:
(defn rollback
"Reassert retracted datoms and retract asserted datoms in a transaction,
effectively \"undoing\" the transaction.
WARNING: *very* naive function!"
[conn tx]
(let [tx-log (-> conn d/log (d/tx-range tx nil) first) ; find the transaction
txid (-> tx-log :t d/t->tx) ; get the transaction entity id
newdata (->> (:data tx-log) ; get the datoms from the transaction
(remove #(= (:e %) txid)) ; remove transaction-metadata datoms
; invert the datoms add/retract state.
(map #(do [(if (:added %) :db/retract :db/add) (:e %) (:a %) (:v %)]))
reverse)] ; reverse order of inverted datoms.
@(d/transact conn newdata))) ; commit new datoms.