I am trying to find a solution to prevent race conditions in my application logic (specifically when renewing an OAuth access token) and my back-end database happens to be mongodb.
Coming from a MySQL background, I'm used to using GET_LOCK
and it's related functions to handle blocking in PHP. Does Mongo have any analog to MySQL's GET_LOCK
function, or will I have to use PHP's file locking or something similar?
Is flock()
a good (or proper) alternative for this situation, or is that meant only for use when reading and writing to files?
Edit:
The race condition I am trying to prevent is the following:
Instance A notices OAuth access token nearing expiration
Instance B notices OAuth access token nearing expiration
Instance A requests refreshed OAuth access token from remote server and obtains one
Instance B requests refreshed OAuth access token from the same server and is rejected (server potentially invalidates access token from step 3 as security precaution)
Instance A saves result back to database
Instance B saves result back to database
If you want to simulate a named mutex or lock using MongoDB, I would suggest using findAndModify by creating a special collection for it and having a document, you can even call it db.my_lock.
db.my_lock.save({"IMREFRESHINGAUTHTOKEN":false});
Now, between steps 2 and 3 add a findAndModify to grab the "lock":
db.my_lock.findAndModify(
query: {"IMREFRESHINGAUTHTOKEN":false},
update: {$set: {"IMREFRESHINGAUTHTOKEN": true}, ...}
);
If you get to the "lock" first, you will get back this object (and you will get to set the first field to true - I recommend setting a second field with timestamp or connection number or process ID or some other identifier which will allow cleaning up after a crashed process so it won't hold a lock forever).
If you "lose" the race you will get back nothing that matches "IMREFRESHINGAUTHTOKEN":false and you'll know you lost the race and give up (or check the timestamp of the lock and maybe try to see if it's old and stale).
This describes a stand-alone single lock on the whole "collection" - of course you can implement this as an extra field on the stored OAuth token and have as many of them being refreshed at a time as there are threads noticing they are expiring.
Hope this helps.