Last active
May 24, 2019 00:46
-
-
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
This file contains 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
(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