2

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.

2
  • Whatever you are using to write to the database should be using promises. Also when you add a user, you should ensure that the department actually exists prior to adding them. Commented Sep 19, 2018 at 16:05
  • Well,literally no two requests come in same time. so one function will be called sooner at last. Inside the functions, using promise should be considered. By that, the code will act synchronously and wait for one request to handle before processing another one. Commented Sep 19, 2018 at 16:33

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

Comments

0

(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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.