datomic 2026-08-27

Datomic Pro Feature Request: (datomic.api/origin-t speculative-db) => t returns the original basis-t of a db value prior to any d/with calls that advanced the tx-basis due to speculative txes. Why: d/with advances monotonic db-basis for uncommitted txes. There is no easy way to track when a speculative DB with uncommitted txes diverged from committed DB transactions, without doing costly tx-range gymnastics. This would assist libraries like https://github.com/theronic/eacl track divergent monotonic bases T to maintain cache coherence on reads without cache poisoning, while enabling cache reuse. This function could also be named d/committed-t. Details in thread.

Proposed d/origin-t simple mechansim: 1. Add originT: long to https://docs.datomic.com/javadoc/datomic/Database.html and internal long field. For non-speculative DBs, (d/origin-t db) will equal (d/basis-t db). 2. Initial call to d/with assoc's :origin-t (d/basis-t db) to the internal Database object, such that (d/basis-t db) > (d/origin-t db'). 3. Subsequent d/with carries over :origin-t, i.e. origin-t' := (or (:origin-t db) (d/basis-t db)), so it remains stable when chaining multiple d/with calls. 4. Alternatively, origin-t could be nil if no d/with used. 5. Optional d/is-speculative? could check the above. Rationale: • Given a (d/db conn) with basis T=100, then ◦ (d/with (d/db conn) tx-data) advances monotonic basis T to T=101, without populating the tx-log. ◦ Transacting against live DB via (d/transact conn) advances the committed DB basis (d/db conn) to T=101, ◦ Both the speculative DB and the committed DB values have the same basis T=101. ◦ What changed? It's hard to tell when they diverged - you have to do expensive d/log & d/tx-range gymnastics to figure out when they diverged. • In Datomic Pro, we can detect filtered DBs with https://docs.datomic.com/clojure/index.html#datomic.api/is-filtered. • Historic DBs can be detected with Java .isHistory(). • But we can't easily detect speculative DBs. EACL populates cache when querying which is essential far fast cyclic paths & cursor continuations. The trick EACL uses for this is to bump :eacl.relation/version "datomic.tx" for the affected Relation when writing a (Relationship subject relation resource). Queries will discard stale cache segments older than :eacl.relation/version for any dependent computations. Speculative db values poison the EACL cache, because EACL can't tell the difference between (d/db conn) and (d/with db ...). To mitigate this, I have to prevent library consumers from using db directly and take snapshots instead as well as add eacl/with helpers that makes the cache speculative since the fork. Library consumers may forgot to do this, or can reach into internals and poison the cache unwittingly. It would be convenient to let callers pass around DBs instead of EACL snapshots that wrap db with metadata & cache instructions :).