This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-04-12
Channels
- # announcements (76)
- # babashka (10)
- # beginners (39)
- # biff (2)
- # calva (51)
- # chlorine-clover (8)
- # cider (6)
- # clj-kondo (15)
- # clj-on-windows (13)
- # cljdoc (26)
- # cljs-dev (8)
- # clojure (196)
- # clojure-austin (17)
- # clojure-europe (36)
- # clojure-nl (4)
- # clojure-spec (8)
- # clojure-uk (2)
- # clojurescript (18)
- # community-development (4)
- # conjure (1)
- # core-typed (38)
- # cursive (5)
- # datalevin (17)
- # datomic (25)
- # docker (1)
- # events (2)
- # interop (2)
- # jobs (4)
- # kaocha (28)
- # lsp (22)
- # nyc (1)
- # off-topic (10)
- # pedestal (1)
- # portal (22)
- # re-frame (22)
- # react (10)
- # shadow-cljs (19)
- # spacemacs (7)
- # tools-deps (11)
- # vim (14)
- # xtdb (7)
I had a bit of a weird one while developing an on-prem transactor function - perhaps it’s my lack of experience with them, but it was surprising. The function has a set as part of an argument map, and all worked fine on a forked local connection, or on an in memory db. However, when deployed to the transactor we started getting odd errors about trying to treat a set as a string. It was a classic error (below), but we couldn’t spot the error in our logic, and the tests were green.
Execution error (ClassCastException) at dev/eval118048$fn (form-init6861183615777452247.clj:1).
class java.util.HashSet cannot be cast to class java.lang.CharSequence (java.util.HashSet and java.lang.CharSequence are in module java.base of loader 'bootstrap')
The missing piece was figuring out that the types changed (perhaps due to de/serialization), such that coll?
for example, returned false on something that originally had been a clojure.lang.PersistentHashSet
- it had become a java.util.HashSet
.
Adding a section to the tx function docs explaining what happens to data provided to the function would have helped. It was quite some debugging to figure it out, although the d/cancel
api helped to bisect exactly which bit of code was blowing up.And this is indeed because of fressian serialization. It doesn’t have to be this way but these are the handlers they chose (maybe for performance)
I've also been bitten by this
I'm always processing arguments like this when writing transaction functions:
...
(:import (java.util HashSet List)
...
(defn to-clojure-types [m]
(walk/prewalk
(fn [e]
(cond (instance? HashSet e)
(into #{} e)
(and (instance? List e) (not (vector? e)))
(vec e)
:else e))
m))
It’s surprising but I’ve rarely found that it matters (which is why it’s annoying when it bites!)
I guess the big thing was that it works differently on the transactor, compared to in memory, which was really surprising.. hard to write tests that cover this without quite some gymnastics..
Running a full fledged container with datomic would catch this, right? I should look into clj-test-containers: https://github.com/javahippie/clj-test-containers
A month has passed already... I "solved" this problem in the following way using https://github.com/clojure/data.fressian:
(ns com.github.ivarref.add-fressian
(:require [clojure.data.fressian :as fress]
[datomic.api :as d]))
(defn transact [org-transact]
(fn [conn tx-data]
(org-transact conn (fress/read (fress/write tx-data)))))
(defn with-fressian [f]
(with-redefs [d/transact (transact d/transact)]
(f)))
and then inside my tests I have something like:
(test/use-fixtures :each add-fressian/with-fressian)
While this works the problem is still how Datomic works (and differs in networked database vs. local)Thanks for pointing this out @U3ZUC5M0R! You saved me a lot of frustrating debugging time 🙏
PS: You can test remote transactor funtcions' behavior if you want using https://github.com/sikt-no/datomic-testcontainers/. There is also https://github.com/ivarref/gen-fn for writing datomic functions that automatically de-mangles fressian types into regular Clojure ones. I've documented the differences https://github.com/ivarref/gen-fn/blob/main/test/com/github/ivarref/remote_vs_local_test.clj#L79-L93, as part of gen-fn. Disclaimer (?): I wrote both these libraries.
I also just got bitten by this, due to using vector?
in a db-fn (though sequential?
would also cause issues).
I was wondering if there is a reason they don't use the same serialization with in-memory transactors to patch this deviation of behavior?
I wonder how many have been bitten by this, and how many hours have been wasted... They could e.g. add a function transact2 which has identical types on both in-mem and remote transactor
would there be a need for a new name in this case, given it would be bringing parity to the two versions?
You could break a production (remote transactor) function which is lacking (in memory) tests? But this is a known issue, or no guarantees for over the wire format, for many years (I checked the git logs here and stumbled upon this error in 2018). I'm not the one you need to convince though :-/
Hi guys! A question about @(d/transact conn ..)
and subsequent (d/db conn)
: is the d/db
guaranteed to see the effects of the transaction (T value equal or greater that the transaction?) I am aware d/transact
returns the db value immediately after transaction completion, and I use it 99% of the time, but in this particular instance it's particularly inconvenient.
The question is really this: is it possible at all, through the JVM instruction reordering or any other kind of magic, that (do @(d/transact conn ..) (d/db conn))
might return a db
value representing state before the transaction?
Edit: it's been answered before https://stackoverflow.com/questions/47693495/datomic-on-a-peer-does-connection-db-read-your-writes-after-connection-trans
Oh, it's been answered before https://stackoverflow.com/questions/47693495/datomic-on-a-peer-does-connection-db-read-your-writes-after-connection-trans The answer is: yes, the guarantee is there
https://yyhh.org/blog/2021/11/t-wand-beat-lucene-in-less-than-600-lines-of-code/ This isn't Datomic, but it is an example of adding full text search to a different Datalog based DB.
You could break a production (remote transactor) function which is lacking (in memory) tests? But this is a known issue, or no guarantees for over the wire format, for many years (I checked the git logs here and stumbled upon this error in 2018). I'm not the one you need to convince though :-/