2

I'm creating a rochambo game with Socket.io. Players add their bets to a waiting list, node checks in mondogDB sessions if the player has enough money to do so and then creates the bet before sending it to other players.

My code works but if I ask for creating multiple bets very fast, node doesn't have time to update the player balance in mongoDB before the next bet arrives, despite my waiting list. So a player can create bets even if he doesn't have enough money, and I'd like to avoid that.

I can't find a way to prevent this from happening. Certainly with callbacks or recursive function, but I didn't manage to make it work.

Sorry for my poor English, I'm french :)

Here is a simplified version of the code :

socket.on('placerunpari', function (screenName, amount, sign) {
newBetWaitingList.push({
    screenName: screenName,
    amount: amount,
    sign: sign
});
for (var k in newBetWaitingList) {

    screenName = newBetWaitingList[k].screenName;
    amount = newBetWaitingList[k].amount;
    sign = newBetWaitingList[k].sign;

    playerModel.findOne({
        screenName: screenName
    }).exec(function (err, player) {
        if (player != null) {
            if (checkAmounts(amount) && (sign == "scissors" || sign == "rock" || sign == "paper")) {
                if (amount <= player.balance) {

                    //DB writing
                    var newBet = new betModel({
                        screenName: screenName,
                        amount: amount,
                        sign: sign
                    });
                    newBet.save(function (err, res) {
                        io.sockets.socket(connected[screenName]).emit('mybet', newBet.id, amount, sign);
                        socket.broadcast.emit('newbet', newBet.id, screenName, amount);
                        creatorUpdateBalance(screenName, -amount);
                    })
                } else {
                    io.sockets.socket(connected[screenName]).emit('showalert', 'You don\'t have enough money for this bet', 'red');
                }
            } else {
                console.log('Unauthorized access');
            }
        }
    });
    newBetWaitingList.shift();
}});
atmys
  • 68
  • 6
  • 1
    you may push all bets into a queue, and after on timer (say 100ms) process that queue. So you have always a storage for new bets, and have a time to process them in sequence they've arrived. http://stackoverflow.com/questions/4700935/what-are-good-message-queue-options-for-nodejs – Tigran Mar 14 '14 at 13:02
  • you can try Redis cache and list data structure, it would be faster with small timers to coordinate everything. – Gntem Mar 14 '14 at 13:04
  • How are you planning on handling when you site needs to run on more than one machine? – Hector Correa Mar 14 '14 at 13:06
  • Have you considered using a mutex? Maybe this would help: https://github.com/jupiter/node-synchronized – azurelogic Mar 14 '14 at 13:06
  • 1
    [`async.queue`](https://github.com/caolan/async#queue) with `concurrency = 1` may help here. – TimWolla Mar 14 '14 at 13:17

1 Answers1

1

Thanks for your answers and help, it works :

   function CreateBet(task, callback) {

    screenName = newBetWaitingList[k].screenName;
    amount = newBetWaitingList[k].amount;
    sign = newBetWaitingList[k].sign;

    playerModel.findOne({
        screenName: screenName
    }).exec(function (err, player) {
        if (player != null) {
            if (checkAmounts(amount) && (sign == "scissors" || sign == "rock" || sign == "paper")) {
                if (amount <= player.balance) {

                    //DB writing
                    var newBet = new betModel({
                        screenName: screenName,
                        amount: amount,
                        sign: sign
                    });
                    newBet.save(function (err, res) {
                        io.sockets.socket(connected[screenName]).emit('mybet', newBet.id, amount, sign);
                        socket.broadcast.emit('newbet', newBet.id, screenName, amount);
                        creatorUpdateBalance(screenName, -amount, function () {
                            callback();
                        });
                    })
                } else {
                    io.sockets.socket(connected[screenName]).emit('showalert', 'You don\'t have enough money for this bet', 'red');
                    callback();
                }
            } else {
                console.log('Unauthorized access');
                callback();
            }
        }
    });
}

var newBetWaitingList = async.queue(CreateBet, 1);

socket.on('placerunpari', function (screenName, amount, sign) {
    newBetWaitingList.push({
        screenName: screenName,
        amount: amount,
        sign: sign
    });
});
atmys
  • 68
  • 6