Jonathon Blok
Jonathon Blok

Reputation: 749

Express: run middleware once per second

I have an express server that is running as an API, and I have a middleware loaded as such:

app.js

const lastActivity = require('./middleware/lastActivity');
app.use(lastActivity);`

middleware/lastActivity.js

module.exports = function(req, res, next) {
    if (req.user && isDifferentDay(req.user.last_activity) {
        User.findById(req.user.id, (err, user) => {
            user.last_activity = Date.now();
            user.save((err) => {
                // also do another async call to an external service, then
                return next();
            }
        });
    }
});

So it checks whether the last_activity date saved on the user is a different day than today, and if so updates the user (I only care about the date, not the specific timestamp). It also does an API call to an external service to manage email marketing campaigns.

The problem however is that my web app requests two resources on page load at the same time. This means the isDifferentDay returns true for both of them, and the user model updates twice and more importantly I do two API calls to the external service which is rate limited.

One obvious solution is to only do one request on my client at a time, but I don't really want to limit myself to that. What I want is a sort of express 'lock' which will only run the middleware once per second? Or some other solution that I can't see.

What is the best way to handle this in a express/node manner?

Thank you.

Upvotes: 0

Views: 387

Answers (1)

zlumer
zlumer

Reputation: 7004

This problem is a typical race condition.
Since you only care about user's last_activity once per day, instead of using findById you can add an additional filter to DB request AND update it in one go. E.g.

var query = { 
    _id: req.user.id,
    last_activity: req.user.last_activity
};
User.findOneAndUpdate(query, { last_activity: Date.now() }, (err, user) => {
    if (!user)
        return; // user was changed between requests, do nothing
    // ... the rest of your code
});

That way, you'll only update the user if it's last_activity was left unchanged between your actions.

Upvotes: 1

Related Questions