user5239066
user5239066

Reputation:

Datomic: How to synchronize datoms between a connection and (datomic.api/with db)?

I want to operate on a Datomic database without triggering side effects. The goal is to work on a "alternative" version of the current database, and to commit the datoms if the creation of the alternative version was successful. Otherwise, the database connection should stay in its current state.

I found that datomic.api/with is a good fit for my problem, but I don't know what would be the best way to add the datoms created in the alternative version to the Datomic connection.

Here is my current procedure:

(defn operation [conn]
  (let [db (d/db conn)
        current-t (d/basic-t db)
        new-db (create-alternative-version db)]
    (d/transact conn (d/datoms (d/since new-db current-t) :eavt))))

At the moment, I get the following error:

ClassCastException datomic.db$datoms$reify__1559
cannot be cast to   java.util.List  datomic.api/transact

Do you think this approach make sense ? If there a better solution ?

I also looked at the sync api, but it does not seem fit for this purpose.

Thank you for your help

Upvotes: 1

Views: 248

Answers (1)

Leon Grapenthin
Leon Grapenthin

Reputation: 9276

No synchronization between a with-db and a db on a transactor is in effect. A with-db is an alternative reality and does not hold any locks on the transactor connection or the current database on the transactor.

For an example consider that you do a transaction per d/with and in return get a with-db, a speculative database state with the transaction applied. Then you check whether the resulting database status is "successful" (you haven't clarified what successful means, but I assume that it requires checking parts of the whole resulting db state). There is no guarantee that the same transaction would result in the same state of the real database since another transaction could happen in between.

The typical solution to this synchronization problem is to identify the minimal check of "successful" and implement that in a transaction function. Within a transaction function you can access a consistent database state before the transaction. Transaction functions run in isolation during a transaction and can be used to let the transaction fail if it would not result in a "successful" database state. An alternative to letting transactions fail is to inject data from the database state before the transaction. If necessary, you can use d/with from within a transaction function.

Here is the documentation of transaction functions: http://docs.datomic.com/database-functions.html


Your example does not work because d/datoms returns eavt tuples, while a transaction would require assertion tuples beginning with :db/add, :db/retract or the ident of a transaction function, or maps for upsertion. You could of course transform the eavt tuples to such assertion tuples but you would miss retractions (could work with d/history, IDK). It would be easier to just apply the same transaction that you applied to d/with to d/transact. Nevertheless this would not give you any synchronization guarantees as explained above.

Upvotes: 1

Related Questions