I'm trying to prevent concurrent requests to a specific record, see the following example:
function addMoney(orderID,orderID){
const status = Database.getOrder(orderID);
if (status === 1){
return "Money Already Added";
}
Database.udateOrder(orderID, {status: 1});
Database.addMoney(userID, 300);
return true;
}
Assume someone made this request exactly at the same time, therefore the "status" check passed, they'd be able to get Database.addMoney
run twice.
Using MySQL, I'd start a transction to lock the row but not sure how to do so using MongoDB.
You can do the transactions in mongodb like MySQL. Consider having an order
document with id:123
and status:0
. Then you can check for status in a transaction and return if it's already paid or fall through in order to add money document and update order status.
If you face any issue like Transaction numbers are only allowed on a replica set member or mongos
this link might help.
In order to use transactions, you need a MongoDB replica set, and starting a replica set locally for development is an involved process. The new run-rs npm module makes starting replica sets easy.
const uri = 'mongodb://localhost:27017';
const dbName = 'myDB';
const MongoClient = require('mongodb').MongoClient;
async function main() {
const client = new MongoClient(uri);
await client.connect();
const session = client.startSession();
try {
await session.withTransaction(async () => {
const orders = client.db(dbName).collection('orders');
const money = client.db(dbName).collection('money');
let doc = await orders.findOne({orderID: 123});
if (doc && doc.status === 1) {
console.log("Money Already Added");
return
}
await orders.updateOne({orderID: 123}, {'$set': {status: 1}});
await money.insertOne({orderID: 123, userID: 100, amount: 300}, {session});
console.log("Money added");
});
await session.commitTransaction();
} catch (e) {
console.log(e);
} finally {
await session.endSession();
await client.close();
}
}
main()
The code above may need improvement because I couldn't test it on MongoDB with replica set.