This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-03-25
Channels
- # aleph (9)
- # announcements (2)
- # babashka (32)
- # babashka-sci-dev (72)
- # beginners (77)
- # calva (1)
- # cider (3)
- # clj-kondo (19)
- # clojure (61)
- # clojure-brasil (13)
- # clojure-europe (25)
- # clojure-italy (1)
- # clojure-nl (2)
- # clojure-norway (27)
- # clojure-uk (23)
- # clojuredesign-podcast (6)
- # clojurescript (12)
- # conjure (2)
- # core-typed (3)
- # cursive (6)
- # datalevin (2)
- # datomic (13)
- # emacs (9)
- # events (19)
- # fulcro (6)
- # graphql (11)
- # gratitude (2)
- # helix (3)
- # honeysql (16)
- # jobs (1)
- # lsp (89)
- # malli (33)
- # meander (14)
- # off-topic (87)
- # pathom (4)
- # polylith (7)
- # portal (4)
- # practicalli (1)
- # rdf (6)
- # reagent (2)
- # releases (8)
- # remote-jobs (1)
- # shadow-cljs (59)
- # sql (8)
- # tools-deps (14)
- # xtdb (18)
Maybe an easy question, new to datomic,
Let’s say i’m implementing an idempotent API, ontop of datomic
PUT /component/{name}
In the case where there already exists a component by the name name
, we update the record, e.g.
(d/transact! conn [{:db/id [:c/name name], ...}])
In the case where it doesn’t exist we create a new record, with new UUID, e.g.
(d/transact! conn [{:c/myid (d/squuid), :c/name name, ...}])
So how do I write the logic that determines whether to (1) create a new record, or (2) update an existing one as above, if I put an if/else branch that checks whether ahead of time whether the record already exists, that’s using a stale snapshot of the database, so by the time a branch is selected, I may be on the wrong branch.Just insert the update/record/datom/fact and provide the unique idenitfier. If it exists, it will be updated, if it doesn't it will add that and so now it's created. Or at least that's how it looks from my couch without consulting the docs.
if it already exists i'll get an illegal state because d/squuid will generate a different value than what's already in the database
You could take a look at https://docs.datomic.com/cloud/transactions/transaction-functions.html maybe that will help?
One more thing. If the name is a unique then you should also model this in the schema by using :db/unique :db.unique/identity
then when you have that it will either create a new record or update existing. In this case you won't have to use transaction functions, which come with their own tradeoffs.
@U8A5NMMGD Sorry I still don’t see it, the :db.unique/identity is exactly why you would reach an illegal state exception, because you’d be trying to transact a newly generated (d/squuid) with an already existing :c/name and existing :c/myid
the ‘if else branch’ is because when create a record you generate a new UUID under :c/myid, when you update an existing record, you omit that as part of the transact! because it’s already generated. but now we’re back to a race condition.
Here is the docs > If a transaction specifies a unique identity for a temporary id, and that unique identity already exists in the database, then that temporary id will resolve to the existing entity in the system. This upsert behavior makes it possible for transactions to work with domain identities, without ever having to specify Datomic entity ids.
It should resolve and then it says "upsert behavior" which feels like what you want.