Skip to content

Instantly share code, notes, and snippets.

@favila
Last active May 24, 2019 00:46
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 favila/62276cdb479060b782158e808e1113aa to your computer and use it in GitHub Desktop.
Save favila/62276cdb479060b782158e808e1113aa to your computer and use it in GitHub Desktop.
Lazy tx-ids for datomic that works outside query and does not rely on a connection or the log
(require '[datomic.api :as d])
(def conn-uri "datomic:dev://localhost:4334/my-db")
;; This is normally how you would get a list of tx-ids outside a query.
;; However, there is some concern that this is not lazy. (I am pretty sure,
;; but not certain, that reading the tx-log is lazy.)
(-> conn-uri d/connect d/log (d/tx-range nil nil)
(->> (map :t) (take 10)))
;;=> You should get something like this:
;;=> (1000 1001 1005 1006 1007 1336 1337 1338 1467 1468)
;;; Here is an alternative `tx-ids` that uses only a db, not the log.
;;; Note you will not see transactions older than the
;;; basis-t of the DB!
;;; It also assumes you aren't filtering out transaction
;;; datoms, and that every transaction entity has
;;; :db/txInstant on it (which I think is a safe assumption?)
(defn tx-ids
"Return a list of transaction ids visible in the given database.
This is a work-alike to the tx-ids special function inside a
query and to tx-range, except it uses a database instead of
a log."
[db]
(let [tx-part (d/entid db :db.part/tx)
;; NOTE: there are magic "bootstrap" transactions below
;; t=1000, but tx-log never shows them. We skip them too
;; just to be compatible.
start-t (d/entid-at db tx-part 1000)]
(sequence
(comp
;; This is just paranoia. Conceivably it is possible to
;; assert :db/txRange on an arbitrary tx-partitioned
;; entity number, but that entity is not actually
;; a transaction t?
(filter (fn [{:keys [e tx]}] (= e tx)))
(map :e)
(take-while #(= (d/part %) tx-part)))
(d/seek-datoms db :aevt :db/txInstant start-t))))
;; You should get the same answer, with the caveat that I think
;; (d/tx-range log nil nil) might keep giving you txs if more
;; occured after the moment tx-range was called?
;; This tx-ids function will definitely not do that.
(->> conn-uri d/connect d/db tx-ids (map d/tx->t) (take 10))
;;=> (1000 1001 1005 1006 1007 1336 1337 1338 1467 1468)
;;; Now verify we get the same results with both methods
(->> (map (fn [a b] (when-not (= a b) [a b]))
(-> conn-uri d/connect d/db tx-ids)
(-> conn-uri d/connect d/log (d/tx-range nil nil) (->> (map :t) (map d/t->tx))))
(remove nil?)
(take 5))
;; For me, on a large non-trivial db, I get empty-seq
;; If you do not get empty-seq, tx-ids is broken...
;;=> ()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment