简体   繁体   中英

Concurrent request can not handle properly with mongoDB and nodeJs

I'm working on my project, which is based on socket.io room. I used socket with nodejs and manage room data in mongoDB.

Here is my code, Only two players can join a room and then after I turn IsGameOn flag false to true.
This code is working fine when I send request to server one by one.
Problem occurs when many request comes at a time. And problem is more than 2 players join the room (Room players' data store in aPlayers Array).

I upload an Image of the database also. So, you can see what happen actually in the database.

const joinRoom = async (sData, callback) => {

if(sData.iPlayerId && sData.eRoomCount)
{
    try {

        let body = _.pick(sData, ['eRoomCount', 'iPlayerId']);

        console.log(body);

        await roomsModel.aggregate([
            {
                $match: {
                    eRoomCount: body.eRoomCount,
                    IsGameOn: { $eq: false }
                }
            },
            { $unwind: "$aPlayers" },
            {
                $group: {
                    _id: "$_id",
                    eRoomCount: { $first: "$eRoomCount" },
                    aPlayers: { $push: "$aPlayers" },
                    size: { $sum: 1 }
                }
            },
            {
                $match: {
                    size: { '$lt': body.eRoomCount }
                }
            },
            { $sort: { size: -1 } }
        ]).exec((error, data) => {
            if (data.length < 1) {

                let params = {
                    eRoomCount: body.eRoomCount,
                    aPlayers: [{
                        iPlayerId: body.iPlayerId
                    }]
                }
                let newRoom = new roomsModel(params);
                console.log(JSON.stringify(newRoom));
                newRoom.save().then((room) => {
                    console.log("succ", room);
                    callback(null,room);
                }).catch((e) => {
                    callback(e,null);
                });
            } else {
                roomsModel.findOne({ _id: data[0]._id }, (error, room) => {

                    if (error) {
                        callback(error,null);
                    }

                    if (!room) {
                        console.log("No room found");
                        callback("No room found",null);
                    }

                    room.aPlayers.push({ iPlayerId: body.iPlayerId });
                    if (room.aPlayers.length === room.eRoomCount) {
                        room.IsGameOn = true;
                    }

                    room.save().then((room) => {
                        callback(null,room);
                    }).catch((e) => {
                        callback(e,null);
                    });

                })
            }
        });

    } catch (e) {
        console.log(`Error :: ${e}`);
        let err = `Error :: ${e}`;
        callback(e,null);
    }
    }
}

This is happens when request comes one by one. 当请求逐个出现时会发生这种情况。

This is happens when many request comes at a time. 在此输入图像描述

The right way would be use mongoose's findOneAndUpdate instead of findOne . The operation findOneAndUpdate is atomic. If you do the right query, you can make your code thread safe.

// This makes sure, that only rooms with one or no player gets selected.
query = {
    // Like before
    _id: data[0]._id,
    // This is a bit inelegant and can be improved (but would work fast)
    $or: {
        { aPlayers: { $size: 0 } },
        { aPlayers: { $size: 1 } }
    }
}

// $addToSet only adds a value to a set if it not present.
// This prevents a user playing vs. him/herself
update = { $addToSet: { aPlayers: { iPlayerId: body.iPlayerId } } }

// This returns the updated document, not the old one
options = { new: true }

// Execute the query
// You can pass in a callback function
db.rooms.findOneAndUpdate(query, update, options, callback)

for this problem you can use semaphore module, in your case Node Server accessing the concurrent request in a single thread environment but OS does the multitasking job. semaphore module will help you to overcome from this hell.

  • npm i semaphore (install module)
  • const sem = require('semaphore')(1); (require and specify concurrent request limit heres its 1 )
  • sem.take(function()); (wrap your function in it)
  • sem.leave(); (call it when your processing is done then the next request will access the function)

     const joinRoom = (sData, callback) => { sem.take(function () { if (sData.iPlayerId && sData.eRoomCount) { try { let body = _.pick(sData, ['eRoomCount', 'iPlayerId']); roomsModel.aggregate([ { $match: { eRoomCount: body.eRoomCount, IsGameOn: { $eq: false } } }, { $unwind: "$aPlayers" }, { $group: { _id: "$_id", eRoomCount: { $first: "$eRoomCount" }, aPlayers: { $push: "$aPlayers" }, size: { $sum: 1 } } }, { $match: { size: { '$lt': body.eRoomCount } } }, { $sort: { size: -1 } } ]).exec((error, data) => { if (data.length < 1) { let params = { eRoomCount: body.eRoomCount, aPlayers: [{ iPlayerId: body.iPlayerId }] } let newRoom = new roomsModel(params); console.log(JSON.stringify(newRoom)); newRoom.save().then((room) => { console.log("succ", room); sem.leave(); callback(null, room); }).catch((e) => { sem.leave(); callback(e, null); }); } else { roomsModel.findOne({ _id: data[0]._id }, (error, room) => { if (error) { sem.leave(); callback(error, null); } if (!room) { console.log("No room found"); sem.leave(); callback("No room found", null); } room.aPlayers.push({ iPlayerId: body.iPlayerId }); if (room.aPlayers.length === room.eRoomCount) { room.IsGameOn = true; } room.save().then((room) => { sem.leave(); callback(null, room); }).catch((e) => { sem.leave(); callback(e, null); }); }); } }); } catch (e) { console.log(`Error :: ${e}`); let err = `Error :: ${e}`; callback(e, null); } } }); } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM