datascript

zeitstein 2022-03-29T07:51:28.066519Z

Any advice on whether to use transaction functions or regular functions to build transaction data for a sizeable API? Some of the functions will need access to the current db value. Any reason not to go all in on transaction functions?

Linus Ericsson 2022-03-29T11:41:34.288399Z

I would recommend using transaction functions (but only as a last resort). The development story for using transaction functions is terrible. The flexibility is also not great. You will want to use :db.fn/cas most of the time. You will build complex update logic (use rules engines if you want).

Adam Lewis 2022-03-29T15:18:19.799289Z

I'll second everything @oscarlinusericsson just said, great points about development ergonomics! Using regular functions to check consistency and then using :db.fn/cas in the transaction to guarantee the underlying assumptions of those checks didn't change is a great way to go. We use very few installed transaction functions, and they all look like generic databasey utility things, not business logic things. Some examples of txn functions we've built: • uniqueRandomcasNewE (works like :db.fn/cas but takes a temp-id in the new-value position) • atomicAddretractOrFailensureRetract (works like regular retract but for cardinality-many attributes and doesn't fail on non-existent entities)

zeitstein 2022-03-29T15:31:33.706389Z

Thank you both for those tips, much appreciated! So what would the "last resort" point be here?

zeitstein 2022-03-29T17:52:40.610139Z

I guess I'm failing to see why the dev story is terrible? Except for the need to install them. It seems to me you can write them and test them like you would regular tx-data-producing functions?

Adam Lewis 2022-03-29T17:52:55.449859Z

I think it is best to think of transaction functions as primitives. Just like you need hardware support to build a semaphore in software, you need transaction support to get ACID semantics. Transaction functions need some special attention, they probably need to be fast, they better be correct, and they are kind of a pain to maintain, but they are also essential.

Adam Lewis 2022-03-29T17:53:39.880039Z

The dev story falls apart a bit when you try to do change management, versioning, etc...you're better off not trying to revise them

Adam Lewis 2022-03-29T17:56:16.852159Z

To look at it a different way, think of all the ways that databases are cumbersome as giant blobs of global state. e.g. you might have two different versions of the same software accessing the same database instance simultaneously.

zeitstein 2022-03-29T18:21:01.954099Z

Thank you! I guess I'm too inexperienced for this to be obvious. Parsing your example, the problem seems to be that tx functions are stored in the db, so one needs to simultaneously change the db and the software accessing it (which might not be feasible)?

zeitstein 2022-03-29T18:31:17.517349Z

Clearly, I had misunderstood the purpose of tx functions – they are not meant to encode (most) business logic.

Niki 2022-03-30T00:31:14.684259Z

In datomic, installing fns is tiresome. In datascript, though, due to its local nature, experience might be much better. E.g. you can pass function pointer directly in tx data, no need to install anything:

(let [inc-age #(update % :age inc)]
  (d/transact! conn [[:db.fn/call inc-age "Bob"]]))

👍 1
zeitstein 2022-03-30T11:56:37.890289Z

Perfect! Thank you 🎉

Adam Lewis 2022-03-30T13:36:21.227999Z

Thanks @tonsky -- I only realized after I replied I was in the #datascript channel not #datomic 🤦‍♂️