CAS already supported for a single item, but dependent updates after success not yet implemented. For this, need
- a log
- a replay job
Can likely share infrastructure with multi-item transactions below.
- minimize contention: only conflict / retry if actual entries conflict
- ideally, allow transactions across an arbitrary set of entities within a domain
- prepare transaction by inserting using timeuuid into per-domain transaction
table
- limited validity period (1s?), can be extended by another second or so during transaction ('heartbeat'); goal: speedy failure detection while allowing slowish transaction execution during high traffic (up to 10s or so)
- transactional read & planned writes atomically update transactionid column in data table if null
- if not null
- if other transaction timed out (plus clock uncertainty)
- if not marked as committed: atomically replace timeuuid, clean up other transaction members
- if marked as committed, but not marked as finished: replay transaction updates
- abort other transaction if other timeuuid newer (atomic update on 'abort' column)
- retry aquiring the lock (wound-wait) for a limited time or abort / retry current transaction if other timeuuid is older
- if other transaction timed out (plus clock uncertainty)
- once everything is properly locked, atomically mark transaction as
committed by inserting now() tid with CAS
- perform all updates using commit tid
- reset transactionids to null
- finally delete transaction
- background job looks for outdated transactions & cleans up (by tid)
- if committed, as above
- else, clean up transactiontids
- delete transaction
- Read latest (or at timestamp in past)
- No locking at any time
- CAS on both tid & transactiontid = null
- if both aren't equal:
- check if transaction is still alive
- run clean-up if it isn't
- else: transaction is in progress; return mismatch
- check if transaction is still alive
- static transactiontid column in all participating tables
- tid attribute uniformly named, so that it can be added on commit
- maybe ignored when a table is not versioned?
- Transaction table per domain. Draft:
{
comment: 'Per-domain transaction table',
name: 'domain.transactions',
attributes: {
tid: 'timeuuid',
aborted: 'boolean', // false initially
commitTimestamp: 'timeuuid', // null initially, non-null on commit
members: 'set<json>', // cells locked in the transaction
body: 'json' // original transaction request, including potential
// post-transaction HTTP requests to be performed
// through restface
},
index: {
hash: 'tid'
}
}