2020-12-30

How can I use mongodb to only allow one entry when multiple servers are accessing the database?

I have multiple "worker" servers processing jobs and accessing the same MongoDB database but I only want one message to be created and never allow two servers running the same job to create the same message.

When a message is sent, its status field is set to "sent" or if it's disabled it's set to "disabled". So first it checks if there are any sent or disabled messages. Then it creates a document with a lockedAt field set to the current time and checks if the same message has already been locked. The reason I use the lockedAt field is so that if the job fails for some reason, it will allow the lock to expire and run again.

This seems to work most of the time but there are a few messages getting through if two "workers" run the same job within a few milliseconds so my logic isn't perfect but I can't figure out how duplicate messages are being created.

How can I use MongoDB to prevent the same job from running at the exact same time and creating the same message twice?

// Check if there is a locked message.
// insert a new message or update if one is found but return old message (or nothing if one didn't' exist)
const messageQuery = {
    listingID: listing._id,
    messageRuleID: messageRule._id,
    reservationID: reservation._id
};

let message = await Message.findOne({
    ...messageQuery,
    ...{status: {$in: ["disabled", "sent"]}}
});
// If message has been sent or is disabled don't continue
if (message) {
    return;
}

message = await Message.findOneAndUpdate(
    messageQuery,
    {
        listingID: listing._id,
        messageRuleID: messageRule._id,
        reservationID: reservation._id,
        lockedAt: moment().toDate() // Check out the message with the current date
    },
    {upsert: true}
);
// If no message is defined, then it's new and not locked, move on.
if (message) {
    // If message has been locked for less than X minutes don't continue
    const cutoff = moment().subtract(
        Config.messageSendLock.amount,
        Config.messageSendLock.unit
    );
    if (message.lockedAt && moment(message.lockedAt).isAfter(cutoff)) {
        // Put the last lock time back
        await Message.findOneAndUpdate(messageQuery, {lockedAt: message.lockedAt});
        return;
    }
}


from Recent Questions - Stack Overflow https://ift.tt/3rFqoBe
https://ift.tt/eA8V8J

No comments:

Post a Comment