Fork me on GitHub
#datomic
<
2022-03-25
>
jasonjckn23:03:42

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.

Drew Verlee02:03:39

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.

jasonjckn03:03:06

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

jacekschae09:03:49

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.

👍 1
jasonjckn16:03:44

@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

jasonjckn16:03:34

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.

jasonjckn16:03:42

in SQL , the equivalent would be an INSERT with an ON CONFLICT DO UPDATE SET …

jasonjckn16:03:59

or even just grouping a series of statements in a transaction.

jasonjckn17:03:47

seems like transaction functions would solve this , thanks

Drew Verlee18:03:58

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.

Drew Verlee18:03:44

It should resolve and then it says "upsert behavior" which feels like what you want.

jacekschae18:03:15

@U0J3J79FE seems like I didn't understand what you were trying to do clearly.

🙏 1