How nubank operates without traditional transaction features like commit/rollback?

Since the last news https://www.cognitect.com/blog/2020/07/23/Cognitect-Joins-Nubank I believe it can be asked here:

Transactions with ability to commit/rollback are something like “must have” for financial systems(at least my expierence says it), how nubank operates without such feature wich is not provided in datomic due to its nature?

4 Likes

Datomic provides a set of capabilities for ensuring data integrity that far surpasses basic commit/rollback:

  1. Datomic has ACID transactions.
  2. Datomic automatically enforces uniqueness and cardinality constraints.
  3. Datomic allows you to specify arbitrary predicates of both attributes and entities.
  4. Datomic allows you to write arbitrary code, running inside a transaction, to perform arbitrarily complex validations.

Do you have an example of something you are trying to do?

2 Likes

Actually I came up to the same case described here .

My current solution is the same, transact statements using with each time accumulating statements, each next call is done to the db returned from with. In the case of success all collected changes are pushed to real database in the single call, but some questions arise:

  1. What if some statements(previously executed step by step on memory db returned from with) call transaction functions which might depend on result from the previously transacted statements. In such case collected statements can’t be called at once on real database
  2. If some statement creates entity and the next one uses id of that entity then such statements can’t be executed at once.
  3. If first statement creates a entity and the next one retracts the entity

P.S. Maybe the most right solution in this case is to develop in a way which do not need rollback but maximally use features which are provided by datomic to keep data constraints

P.P.S Why I asked about nubank because usually logic of bank operations is not so simple and can be described as:

try {
0. Open transaction
1. do this
2. put some data here
3. change data there
4. Commit
catch (Exception e) {
  rollback transaction
}

I suppose the fundamental difference here is that a transaction is an atomic thing already - it either makes sense as a whole against a value of db, or it doesn’t. Consequently, the job of the “do this”, “put some data here”, “put some data there” is to accumulate speculative changes in the form of data for a transaction.

Your with approach is arguably redundant here, as even if your with calls have made sense against that value of the db locally, there’s no guarantee they will when you really transact them. It feels like a postgres-ism (or similar) that doesn’t really make sense in a datomic world.

For example, and as I guess you know, postgres has things like isolation levels, distributed locks etc to allow imperative sequences of calls to be treated as a reversible transaction (with a cost!), but that just doesn’t equate here.

About your point 2, creating and referencing entities in a transaction, this is why tempids exist, no?

On point 3, wouldn’t that throw due to conflicts in the transaction? That said, having to worry about asserting and retracting the same entity in the same transaction feels like something isn’t quite right in your data flow, and could be elided before submitting a transaction, or be an invariant, for example.

1 Like