Database Reference
In-Depth Information
Note that the retire_transaction is idempotent : it can be called any number of times with
the same txn_id with the same effect as calling it once. This means that if we have a crash at
any point before removing the transaction object, a subsequent cleanup process can still retire
the transaction by simply calling retire_transaction again.
We now need to take care of transactions that have timed out, or where the commit or rollback
process has crashed in a periodic cleanup task:
def
def cleanup_transactions ( txn , max_txn_time ):
# Find & commit partially-committed transactions
for
for txn iin db . transaction . find ({ 'state' : 'commit' }, { '_id' : 1 }):
retire_transaction ( txn [ '_id' ])
# Move expired transactions to 'rollback' status:
cutoff = now - max_txn_time
db . transaction . update (
{ '_id' : txnid , 'state' : 'new' , 'ts' : { '$lt' : cutoff } },
{ '$set' : { 'state' : 'rollback' } })
# Actually rollback transactions
for
for txn iin db . transaction . find ({ 'state' : 'rollback' }):
rollback_transfer ()
Finally, in the case where we want to roll back a transfer, we must update the transaction ob-
ject and undo the effects of the transfer:
def
def rollback_transfer ( txn ):
db . accounts . update (
{ '_id' : txn [ 'src' ], 'txns._id' : txn [ '_id' ] },
{ '$inc' : { 'balance' : txn [ 'amt' ] },
'$pull' : { 'txns' : { '_id' : txn [ '_id' ] } } })
db . accounts . update (
{ '_id' : txn [ 'dst' ], 'txns._id' : txn [ '_id' ] },
{ '$inc' : { 'balance' : - txn [ 'amt' ] },
'$pull' : { 'txns' : { '_id' : txn [ '_id' ] } } })
db . transaction . remove ({ '_id' : txn [ '_id' ]})
Note in particular that the preceding code will only undo a transaction in an account if the
transaction is still stored in that account's txns array. This makes the rollback of the transac-
tion idempotent just like retiring a transaction via a commit.
Search WWH ::




Custom Search