How MVCC works here

If I concurrently run 100 times this. What will I get key1 == 100 or key1 = 1? If 100, why?

db.transaction(trx => {
 k1 = trx.read('key1) || 0
 trx.write('key1',k1++)
})

Did you mean ++k1 instead of k1++ ?

Assuming this is using the default retry loop, after all 100 transactions commit you will see key1 >= 100. This is because the default retry loop assumes transactions are idempotent, so it retries commit_unknown_result. It could be that there was a network fault after attempting to commit one of these transactions and before getting a reply, but the attempt actually succeeded and the retry would increment a second time.

You would also see a lot of retries because of not_committed errors, since by default transactions don’t commit if any of the reads they performed might have given a different result at commit time. Each of the 100 transactions will commit one at a time and see the writes from all previously committed transactions.

1 Like

Thanks Andrew,

It could be that there was a network fault after attempting to commit one of these transactions and before getting a reply, but the attempt actually succeeded and the retry would increment a second time.

What is best practice to mitigate such effects (except indempotence)?

There’s some advice here: Developer Guide — FoundationDB 6.3. There’s also some discussion on handling this automatically here: Automatically providing transaction idempotency, but I don’t think anyone is actively working on this.