Franken
Franken

Reputation: 439

How to prevent race condition in node.js?

Can someone explain me how to prevent race conditions in node.js with Express?

If have for example this two methods:

router.get('/addUser/:department', function(req, res) { ...})

router.get('/deleteUser/:department', function(req, res) { ...})

Both functions are using a non blocking I/O Operation ( like writing to a file or a database).

Now someone calls 'addUser' with Department 'A' and someone tries to delete all users with department 'A'. How can I solve this (or other similar) race conditions?

  1. How can I solve the problem if every user has its own file/database-record?

  2. How can I solve the problem if I have a single user (filesystem) file that I have to read alter and write again?

Note: This is just an example for understanding. No optimization tipps needed here.

Upvotes: 2

Views: 2590

Answers (2)

Manuel Spigolon
Manuel Spigolon

Reputation: 12940

To archive this goal, you need to implement a communication within the two services.

This can be done with a simple queue of operations to process each request in order. The counter effect is that the request waiting for the queue will have a delayed response (and may occur timeout).

A simple "meta" implementation is:

const operationQueue = new Map();

const eventEmitter = new events.EventEmitter();

router.get('/addUser/:department', function(req, res) {
    const customEvent = `addUser-${new Date().getTime()}`;
    const done = () => { 
        res.send('done'); 
        operationQueue.delete(customEvent);
    };
    eventEmitter.once(customEvent, done);
    operationQueue.set(customEvent, () => addUser(customEvent, req));
})

router.get('/deleteUser/:department', function(req, res) {
    const customEvent = `deleteUser-${new Date().getTime()}`;
    const done = () => { 
        res.send('done'); 
        operationQueue.delete(customEvent);
    };
    eventEmitter.once(customEvent, done);
    operationQueue.set(customEvent, () => deleteUser(customEvent, req));
})

function addUser(customEvent, req){
    // do the logic
    eventEmitter.emit(customEvent, {done: true});
}

function deleteUser(customEvent, req){
    // do the logic
    eventEmitter.emit(customEvent, {done: true});
}

// not the best performance
setInterval(()=>{
    const process = operationQueue.shift();
    if(process) {
        process();
    }
}, 1);

Of course, if you'll use tools like a DB or a Redis queue it could fit better than this solution in terms of robustness and failover.

Upvotes: 1

Code
Code

Reputation: 6251

(This is a very broad question.)

Typically, one would use a database (instead of regular text files) and make use its in-built locking mechanisms.

Example of locking mechanisms in the Postgres database management system: https://www.postgresql.org/docs/10/static/explicit-locking.html

Upvotes: 0

Related Questions