Trax
Trax

Reputation: 1558

Node.js mongodb Multiple Scheduled Tasks

I have the following .js file, part of a node.js APP, and 2 questions.

var User = require('../app/models/user');   
var Agenda = require('agenda');

var mongoConnectionString = "mongodb://localhost/agenda";
var agenda = new Agenda({db: {address: mongoConnectionString}});


agenda.define('handle energy', function(job, done) {
    let lastUpdateRun = new Date(Date.now() - 50*1000); // 50 seconds ago for some buffer room


    /****** Energy Loss Per Minute ******/
    User.update( // Over 85 -> ~ 40 loss / hour
        {'local.energy' : { "$gt" : 85}, "local.energyUpdatedAt": {"$lt": lastUpdateRun}}, // query
        {"$inc": { "local.energy": -0.66 }, "local.energyUpdatedAt": new Date() } , // update
        {"multi": true}, // Options
        function(err, result) { // Callback
            if(err) {
                console.log(err)
            }
        }
    );
    User.update( // Over Under 80 -> ~ 25 loss / hour
        {'local.energy' : { "$gt": 50, "$lte" : 85 }, "local.energyUpdatedAt": {"$lt": lastUpdateRun}}, // query
        {"$inc": { "local.energy": -0.42 }, "local.energyUpdatedAt": new Date() } , // update
        {"multi": true}, // Options
        function(err, result) { // Callback
            if(err) {
                console.log(err)
            }
        }
    );
    User.update( // Under 50 -> ~ 15 loss / hour
        {'local.energy' : { "$gt": 25, "$lte" : 50}, "local.energyUpdatedAt": {"$lt": lastUpdateRun}}, // query
        {"$inc": { "local.energy": -0.25 }, "local.energyUpdatedAt": new Date() }, // update
        {"multi": true}, // Options
        function(err, result) { // Callback
            if(err) {
                console.log(err)
            }
        }
    );
    User.update( // Under 25 -> ~ 10 loss / hour
        {'local.energy' : { "$gt": 10, "$lte" : 25}, "$or" :[{'local.estate.owned.movedIn' : false}, {'local.estate.rented.movedIn' : false}], "local.energyUpdatedAt": {"$lt": lastUpdateRun} }, // query
        {"$inc": { "local.energy": -0.167 }, "local.energyUpdatedAt": new Date()} , // update
        {"multi": true}, // Options
        function(err, result) { // Callback
            if(err) {
                console.log(err)
            }
        }
    );  


    /******* Energy Gain Per Minute, No Office *******/

    User.update( // Set Energy to Ten if over 9.5 and under 10
        {'local.energy' : { "$lt": 10, "$gte" : 9.9}, "$or" : [{'local.estate.movedIn' : false}, {'local.estate.rented.movedIn' : false}], "local.energyUpdatedAt": {"$lt": lastUpdateRun}   }, // query
        {"$set": { "local.energy": 10 }, "local.energyUpdatedAt": new Date() } , // update
        {"multi": true}, // Options
        function(err, result) { // Callback
            if(err) {
                console.log(err)
            }
        }
    );  
    User.update( // Under 10 -> ~ 5 gain / hour
        {'local.energy' : { "$lt": 9.9}, "$or" : [{'local.estate.movedIn' : false}, {'local.estate.rented.movedIn' : false}], "local.energyUpdatedAt": {"$lt": lastUpdateRun}  }, // query
        {"$inc": { "local.energy": 0.085 }, "local.energyUpdatedAt": new Date() } , // update
        {"multi": true}, // Options
        function(err, result) { // Callback
            if(err) {
                console.log(err)
            }
        }
    );  


    /******* Energy Gain Per Minute, With Office *******/

    User.update( // Set Energy to 25 if over 24.5 and under 10
        {'local.energy' : { "$lt": 10, "$gte" : 24.5}, "$or" : [{'local.estate.movedIn' : true}, {'local.estate.rented.movedIn' : true}], "local.energyUpdatedAt": {"$lt": lastUpdateRun}   }, // query
        {"$set": { "local.energy": 10 }, "local.energyUpdatedAt": new Date() } , // update
        {"multi": true}, // Options
        function(err, result) { // Callback
            if(err) {
                console.log(err)
            }
        }
    );  
    User.update( // Under 25 -> ~ 15 gain / hour
        {'local.energy' : { "$lt": 24.5 } ,  "$or" : [{'local.estate.movedIn' : true}, {'local.estate.rented.movedIn' : true}], "local.energyUpdatedAt": {"$lt": lastUpdateRun}}, // query      
        {"$inc": { "local.energy": 0.250 }, "local.energyUpdatedAt": new Date() } , // update
        {"multi": true}, // Options
        function(err, result) { // Callback
            if(err) {
                console.log(err)
            }
        }
    );      

    done()
});


agenda.on('ready', function() {
    agenda.every('one minute', 'handle energy');
    agenda.start();
});



   // Handle Office Rent Expiration Every Hour
   //Handle A Few other things at different times

Question #1: Is there a better way to handle all those updates, I have quite a few updates in a short interval and I'm repeating 90% of the code over and over again. Also performance wise is this the way to go or I'm doing a big mistake ?

Question #2: If I need more scheduled tasks 1 every hour and one every 25 minutes should I just create a setInterval for each of them, because iirc javascript doesn't handle multiple setIntervals. So how should I handle it ?

P.S. I'm still learning node/js so don't hate me if these are dumb questions. Thanks for you patience.

Upvotes: 0

Views: 364

Answers (1)

chrisbajorin
chrisbajorin

Reputation: 6153

The updates are fine. If they need to be done, they need to be done. Given that they are running repetitively, just make sure you add a few compound indexes between local.energy and each of other fields you are querying by:

UserSchema.index({"local.energy": 1});
UserSchema.index({"local.energy": 1, "local.estate.owned.movedIn": 1});
UserSchema.index({"local.energy": 1, "local.estate.rented.movedIn": 1});
//etc.

The only thing I see that concerns me about the logic is that your updates are running in parallel with no restriction on duplicate updates. Imagine you have a document:

DocA = {
    "local.energy": 9.85
}

Depending on order of execution within the two "Energy Gain/No Office" queries, you can have different results.

The update querying by {"local.energy": {$lt: 9.9}} could increment DocA to 9.935. Now it falls into the update querying by {"local.energy": {$gte: 9.9, $lt: 10}}, which will set it to 10. I'd recommend adding a "lastUpdatedAt": Date field to the schema (and indexes), and restricting your queries with it:

let lastUpdateRun = new Date(Date.now() - 50*1000); // 50 seconds ago for some buffer room

User.update( // Over 85 -> ~ 40 loss / hour
    {'local.energy' : { "$gt" : 85}, "lastUpdatedAt": {"$lt": lastUpdateRun}}, // query
    {"$inc": { "local.energy": -0.66 }, "lastUpdatedAt": new Date()}, // update
    {"multi": true}, // Options
    function(err, result) { // Callback
        if(err) {
            console.log(err)
        }
    }
);

This way you'll only update ones that haven't been updated in this most recent interval.

Upvotes: 2

Related Questions