Skip to content

Instantly share code, notes, and snippets.

@vvvvalvalval
Last active September 19, 2018 06:09
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save vvvvalvalval/9330ac436a8cc1424da1 to your computer and use it in GitHub Desktop.
Save vvvvalvalval/9330ac436a8cc1424da1 to your computer and use it in GitHub Desktop.
Mocking datomic.Connection for fast in-memory testing
(ns bs.utils.mock-connection
"Utilities for using Datomic"
(:require [datomic.api :as d])
(:use clojure.repl clojure.pprint)
(:import (java.util.concurrent BlockingQueue LinkedBlockingDeque)
(datomic Connection)))
(defrecord MockConnection
[dbAtom, ^BlockingQueue txQueue]
Connection
(db [this] @(.dbAtom this))
(transact [this tx-data] (doto (datomic.promise/settable-future)
(deliver (let [tx-res
(loop []
(let [db-atom (.dbAtom this)
old-val @db-atom
tx-res (d/with old-val tx-data)
new-val (:db-after tx-res)]
(if (compare-and-set! db-atom old-val new-val)
tx-res
(recur))
))]
(.add ^BlockingQueue (.txQueue this) tx-res)
tx-res))
))
(transactAsync [this tx-data] (.transact this tx-data))
(gcStorage [this olderThan])
(requestIndex [this])
(release [this])
(sync [this] (doto (datomic.promise/settable-future)
(deliver (.db this))))
(syncExcise [this t] (.sync this))
(syncIndex [this t] (.sync this))
(syncSchema [this t] (.sync this))
(sync [this t] (.sync this))
(txReportQueue [this] (.txQueue this)))
(defn ^Connection mock-conn
"Creates a mocked version of datomic.Connection which uses db/with internally.
Only supports datomic.api/db, datomic.api/transact and datomic.api/transact-async operations.
Sync and housekeeping methods are implemented as noops. #log() is not supported."
[db]
(MockConnection. (atom db) (LinkedBlockingDeque.)))
(comment ;; How fast is it ?
(def mem-db (bs.dev/local-db)) ;; creates a mem db, loads the schema and some fixture data in it. Takes about 20ms on my dev laptop.
(time (dotimes [i 1000]
(let [conn (mock-conn mem-db)]
@(d/transact conn [{:db/id (d/tempid :db.part/user)
:bouser/firstName (str "Valentinnnnn_" i)
:bouser/lastName (str "Valentinnnnn_" i)
:bouser/level "bandsquare"
:bouser/email (str "val.vvalval" i "@gmail.com")
}]))))
;; "Elapsed time: 2027.881 msecs". So this is a x10 improvement.
;; the schema and fixture data are quite small (35 attributes, 103 datoms), so I expect this will get more compelling as the application grows.
)
Copy link

ghost commented Oct 21, 2015

Thanks a lot. Also,

@vvvvalvalval
Copy link
Author

Note that with this implementation Exceptions will not be wrapped, unlike what is described in the Datomic documentation. So be careful of that if you're using this implementation for development.

@vvvvalvalval
Copy link
Author

Now available on Clojars: https://github.com/vvvvalvalval/datomock

@bhurlow
Copy link

bhurlow commented Sep 6, 2018

nice!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment