datomic

cch1 2026-03-02T20:23:03.754199Z

We're seeing some very strange behavior with speculative databases in Datomic Cloud. Sometimes when retracting an entity speculatively, a query immediately after the speculative transaction shows the entity has not been retracted! Sometimes, it shows the entity has been transacted. Here is a transcript of a REPL session executing the same expression twice in a row:

user.unwind> 
(let [wdb (d/with-db (:datomic/connection @user/sysref))
      pol [:st.purchase-order.line/purchase-order-id+number [15900 720]]
      txd [[:db/retractEntity pol]]
      result (d/pull wdb {:selector ['*] :eid pol})
      {:keys [db-after tx-data]} (d/with wdb {:tx-data txd})]
  [wdb txd (d/pull db-after {:selector ['*] :eid pol})])
[{:database-id "e684b9c0-af66-450f-b29b-da7e6f080575", :db-name "stt", :t 18620961, :next-t 18620962, :next-token "qlG2LYVPqXQTliRMZXwRVFLjxjRXfQroaPNrIVWgKbBgChvbZYBJlXwj33IPKaYsK4WgVVS+g7nIPjhsP1H4hg==", :type :datomic.client/db}
 [[:db/retractEntity [:st.purchase-order.line/purchase-order-id+number [15900 720]]]]
#:db{:id nil}]
user.unwind> 
(let [wdb (d/with-db (:datomic/connection @user/sysref))
      pol [:st.purchase-order.line/purchase-order-id+number [15900 720]]
      txd [[:db/retractEntity pol]]
      result (d/pull wdb {:selector ['*] :eid pol})
      {:keys [db-after tx-data]} (d/with wdb {:tx-data txd})]
  [wdb txd (d/pull db-after {:selector ['*] :eid pol})])
[{:database-id "e684b9c0-af66-450f-b29b-da7e6f080575", :db-name "stt", :t 18620963, :next-t 18620964, :as-of 18620961, :next-token "sb7b3by2hDYTuABH0hcCUa73mnmLxWXj4lgKCyw4LxkVQ7vOWOzMIZcDYKb8VpTkBLLVfE80GpThF6NV57VdKuFQbt3yFzzMFXAW3qlfyIY=", :type :datomic.client/db}
 [[:db/retractEntity [:st.purchase-order.line/purchase-order-id+number [15900 720]]]]
 {:db/id 7938473973803978,
  :st.purchase-order.line/item #:db{:id 5818615551342135},
  :st.purchase-order.line/estimated-receipt-on "#time/date \"2023-01-10\"",
  :st.purchase-order.line/number-v2 720,
  :st.purchase-order.line/purchase-order-id+number [15900 720],
 :st.purchase-order.line/blanket-period "#st/year-month-range \"2022-12/P1M\""}]
user.unwind> 
In the first execution of the speculative transaction, the entity has been retracted and d/pull returns the usual marker for an unresolved lookup ref. But in the second execution of the speculative transaction (executed no more than two seconds after the first) the entity was not retracted! The speculative transaction did not throw nor did the d/pull with the :db-after value of the transaction.

👀 1
cch1 2026-03-02T20:23:32.609539Z

These tests were performed from a local REPL against the https://docs.datomic.com/changes/cloud.html#1217-9399.

cch1 2026-03-02T20:41:48.088329Z

(the binding of result to (d/pull wdb {:selector ['*] :eid pol}) is superfluous... it was part of a debugging effort that did not bear fruit.)

okwori 2026-03-03T00:08:32.185209Z

Curious, what happens if pol isn't a lookup ref(or an attempt at, sorry thinking out loud) but an entity id i.e db/id value (7938473973803978)?

cch1 2026-03-03T00:14:25.176279Z

First attempt using :db/id yields the "happy path" where the retracted entity is indeed removed:

(let [wdb (d/with-db (:datomic/connection @user/sysref))
      pol 7938473973803978
      txd [[:db/retractEntity pol]]
      result (d/pull wdb {:selector ['*] :eid pol})
      {:keys [db-after tx-data]} (d/with wdb {:tx-data txd})]
  [wdb txd (d/pull db-after {:selector ['*] :eid pol})])

[{:database-id "e684b9c0-af66-450f-b29b-da7e6f080575", :db-name "stt", :t 18627683, :next-t 18627684, :next-token "aXsglUw0q7c4q8SqaN26ZP8ONBpxgSlW/B2wUPmWiucwl+bWONBSaayA3/SYcsOSEx3/V2BPa70VIAGro5219g==", :type :datomic.client/db}
 [[:db/retractEntity 7938473973803978]]
#:db{:id 7938473973803978}]
...but keep in mind that the results are not deterministic and the unhappy path seems to be more likely if the client has "quiesced" for a bit. I'll try again in a moment and see if I can get the :db/id version to fail.

cch1 2026-03-03T00:15:20.544049Z

(also, obviously for those familiar with the quirks of pulling a non-existent entity, the return value for d/pull is different but consistent with "no entity")

okwori 2026-03-03T00:43:59.450189Z

Try and if it fails, report, so I could reproduce and experiment the same scenario on my machine.

cch1 2026-03-03T01:33:03.229789Z

Yep, after a "quiesce" period, it failed.

jaret 2026-03-03T13:00:55.701289Z

The 1st run: wdb is a db value at :t 18620961 While the 2nd run: wdb is not the same db value because I see it now includes :as-of 18620961 while :t has advanced to 18620963 However, I dont see where you are calling as-of . Where are you getting your db value from? does this @user/sysref possibly wrap the db in an as-of? Once you are using a lookup ref inside a as-of the results may appear nondeterministic. Resolving the entity id in wdb first and then retracting by eid will result in deterministic behavior as a workaround.

jaret 2026-03-03T13:04:01.224419Z

I think this comes down to expecting as-of as a branch: https://docs.datomic.com/reference/filters.html#as-of-not-branch

cch1 2026-03-03T13:06:41.763409Z

The connection from user/sysref is a "plain" connection -there is no call to as-of.

jaret 2026-03-03T13:07:05.907269Z

huh. Thats interesting then. Let me try to figure out why your return shows an as-of

cch1 2026-03-03T13:07:15.260729Z

That struck us as kinda weird too.

cch1 2026-03-03T13:07:47.405289Z

FWIW, the subject of the :db/retractEntity is also the subject of an isComponent relationship.

cch1 2026-03-03T13:10:44.909399Z

Note also that there is no :as-of attr on the db in the second case where things worked as expected. I just ran the test a couple of times in the REPL (with the same connection as yesterday) and when it works, there is no :as-of and when it fails, there is an :as-of. IIRC, @lwhorton observed the same correlation yesterday.

✔️ 1
cch1 2026-03-03T14:57:30.669379Z

Another observation: I cannot provoke the failure if I use a fresh connection. If I "recycle" a connection that has been used for other activities then the problem sometimes appears.