Let's say I have some sort of game. I have a buyItem function like this:
buyItem: function (req, res) {
// query the users balance
// deduct user balance
// buy the item
}
If I spam that route until the user balance is deducted (the 2nd query) the user's balance is still positive.
What I have tried:
buyItem: function (req, res) {
if(req.session.user.busy) return false;
req.session.user.busy = true;
// query the users balance
// deduct user balance
// buy the item
}
The problem is req.session.user.busy will be undefined for the first ~5 requests. So that doesn't work either.
How do we handle such situations? I'm using the Sails.JS framework if that is important.
It sounds like what you need is a transaction. Sails doesn't support transactions at the framework level yet (it's on the roadmap) but if you're using a database that supports them (like Postgres or MySQL), you can use the .query() method of your model to access the underlying adapter and run native commands. Here's an example:
buyItem: function(req, res) {
try {
// Start the transaction
User.query("BEGIN", function(err) {
if (err) {throw new Error(err);}
// Find the user
User.findOne(req.param("userId").exec(function(err, user) {
if (err) {throw new Error(err);}
// Update the user balance
user.balance = user.balance - req.param("itemCost");
// Save the user
user.save(function(err) {
if (err) {throw new Error(err);}
// Commit the transaction
User.query("COMMIT", function(err) {
if (err) {throw new Error(err);}
// Display the updated user
res.json(user);
});
});
});
});
}
// If there are any problems, roll back the transaction
catch(e) {
User.query("ROLLBACK", function(err) {
// The rollback failed--Catastrophic error!
if (err) {return res.serverError(err);}
// Return the error that resulted in the rollback
return res.serverError(e);
});
}
}
I haven't tested this out. But as long as your not using multiple instances or clusters, you should just be able to store the status in memory. Because node is single threaded there shouldn't be any problems with atomicity.
var inProgress = {};
function buyItem(req, res) {
if (inProgress[req.session.user.id]) {
// send error response
return;
}
inProgress[req.session.user.id] = true;
// or whatever the function is..
req.session.user.subtractBalance(10.00, function(err, success) {
delete inProgress[req.session.user.id];
// send success response
});
}