multithreading - How to ensure two users can atomically confirm transaction has taken place in mongodb -


i have model called transaction has following schema

var transactionschema = new mongoose.schema({     amount: number,     status: string,     _recipient: { type: mongoose.schema.types.objectid, ref: 'user' },     _sender: { type: mongoose.schema.types.objectid, ref: 'user' }, }); 

i want both sender , recipient of transaction able 'confirm' transaction took place. status starts out "initial". when sender has confirmed transaction (but recipient yet not), want update status "senderconfirmed" or something, , when recipient has confirmed (but sender has not), want update status "recipientconfirmed". when have both confirmed it, want update status "complete".

the problem is, how can know when update "complete" in way avoids race conditions? if both sender , recipient go confirm transaction @ same time, both threads think status "initial" , update "senderconfirmed" or "recipientconfirmed", when in actuality ought go "complete".

i read mongodbs 2 phase commit approach here doesn't quite fit need, since don't (in case thread modifying transaction) want prevent second thread making update - want wait until first thread finished before doing update, , making content of update contingent on latest status of transaction.

bottom line need "two" update statement each of sender , recipient respectively. 1 going try , set "partial" status complete, , other set "initial" status match "partial" state.

bulk operations best way implement multiple statements, should use these accessing underlying driver methods. modern api releases have .bulkwrite() method, degrades nicely if server version not support "bulk" protocol, , falls issuing separate updates.

// sender confirmation transaction.collection.bulkwrite(     [        { "updateone": {            "filter": {                "_id": docid,                "_sender": senderid,                "status": "recipientconfirmed"            },            "update": {                "$set": { "status": "complete" }            }        }},        { "updateone": {            "filter": {                "_id": docid,                "_sender": senderid,                "status": "initial"            },            "update": {                "$set": { "status": "senderconfirmed" }            }        }}     ],     { "ordered": false },     function(err,result) {        // result confirm 1 update @ succeeded     } ); 

and of course same applies _recipient except different status check or change. alternately issue $or condition on _sender or _recipient , have generic "partial" status instead of coding different update conditions, same basic "two update" process applies.

of course again "could" use regular methods , issue both updates sever in way, possibly in parallel since conditions remain "atomic", reason { "ordered": false } option since no determined sequence needs respected here.

bulk operations though better separate calls, since send , return one request , response, opposed "two" of each, overhead using bulk operations far less.

but general approach. no single statement possibly leave "status" in "deadlock" or mark "complete" before other party issues confirmation.

there "possibility" , slim 1 status changed "initial" in between first attempt update , second, result in nothing being updated. in case, can "retry" action on "should" update on subsequent attempt.

this should ever need "one" retry @ though. , very rarely.


note: care should taken when using .collection accessor on mongoose models. regular model methods have built in logic "ensure" connection database present before anything, , in fact "queue" operations until connection present.

it's practice wrap application startup in event handler ensure database connection:

mongoose.on("open",function() {     // app startup , init here }) 

so using "on" or "once" events case.

generally though connection present either after event fired, or after "regular" model method has been called in application.

possibly mongoose include methods .bulkwrite() directly on model methods in future releases. presently not, .collection accessor necessary grab underlying collection object core driver.


Comments

Popular posts from this blog

Django REST Framework perform_create: You cannot call `.save()` after accessing `serializer.data` -

Why does Go error when trying to marshal this JSON? -