Running a node.js script every 10 seconds

15,976

Solution 1

Put your db query in a function with callback, and make the callback fire the function again 10sec later:

function mydbquery(callback) {
    db.query("SELECT * FROM queue WHERE cooldown > UNIX_TIMESTAMP(NOW()) AND simulated=0 ORDER BY cooldown DESC LIMIT 1", function(err, rows){
    if(err != null){
        die("Query error: " + err);
    }

    if(rows < 1){
        die("No rows");
    }

    //Set the vars from the query
    var name = rows[0]['name'];
    var ip = rows[0]['ip'];
    var iterations = rows[0]['runs'];

    var bin = "/home/hoar/sum/run"
    var args = ['arg='+name, 'arg2='+iterations, 'path=/var/www/upload/'+name+'.html', 'output=log.log'];
    var proc = spawn(bin, args);
    var time = "/.*/";
    var pct = "/^\d/";
    var name = rows[0]['name'];
    var ip = rows[0]['ip'];
    var iterations = rows[0]['runs'];

    proc.stdout.setEncoding('utf8');
    proc.stdout.on('data', function(data) {
        var str = data.toString();
        var s = str.split("|");
        var p = s[0].split("/");
        var t = (s[1] == null) ? "" : s[1];

        if(p != null && s[0] != "@"){ //Needed to check for @ because the program prints this as first line, which is good then we can do the query further done only once.
            //Check the return numbers from simc to see how many sims it has done
            if(parseInt(p[0]) < parseInt(p[1])){ 
                //Check if the 6th match is a number and the 7th only contains letters
                if(t != null){ 
                    var time = t.replace(/(\r\n|\n|\r)/gm,""); //Remove any line disturbers for db

                    //Update the database with the amount of time left on the simulation
                    db.query("UPDATE `queue` SET `status`=" + db.escape(time) + " WHERE (`name`=" + name + ")");
                    //console.log(p[0]+"/"+p[1] + " - " + t + " left");
                }
                //console.log(p[0]+"/"+p[1] + " iterations done");
            }
        }else{
            //If the stdout is null run this query since we don't want to run this more than once.
            db.query("UPDATE `queue` SET `simulated`='2' WHERE (`name`=" + name + " AND simulated!='2')");
            //console.log("Updated db to 2");
        }
    });

    proc.stderr.on('data', function (data) {
        var str = data.toString();
        //If the program returns stderr we want to make sure it stops and we update the database to let the user know.
        if(str.indexOf("ERROR! Setup failure...")){ 

            //Update the database with the amount of time left on the simulation
            db.query("UPDATE `queue` SET `simulated`='3' WHERE (`name`=" + name + ")");

            //Kill the DB connection
            db.destroy(); 
            die("There was an error: " + data);
        }
    });

    proc.on('exit', function (code) {
        //Setup the ftp connection
        var ftp = new JSFtp({
          host: "ftp",
          port: 21,
          user: "ftp",
          pass: "ftp"
        });

        //Simulation ended with success update the database and kill.
        db.query("UPDATE `queue` SET `simulated`='1' WHERE (`name`=" + name + " AND simulated='2')");

        ftp.put('/var/www/upload/'+rows[0]['name']+'.html', 'public_html/mysite/'+ip2long(rows[0]['ip'])+'/'+rows[0]['name']+'.html', function(hadError) {
          if (!hadError)
            console.log("FTP error");

            ftp.raw.quit();
        });
        db.destroy();

        //die("Simulation is done");
//NEW CODE!!! 
        callback();
//END OF NEW CODE
    });

});//end sql
}

//NEW CODE!!!
function wait10sec(){
    setTimeout(function(){
        mydbquery(wait10sec);
    }, 10000);
}

mydbquery(wait10sec);
//END OF NEW CODE

So it will do your query, then wait 10sec before firing another.

Solution 2

Just have your program run continuously and use setTimeout to re-execute the main logic on a timer. There is also setInterval which is tempting but you risk starting a run before the prior run completes. Here's the basic pattern.

function doMainStuff() {
  //do all your stuff
  lastAsyncThing(function (error) {
    //When your final async thing is done, start the timer
    if (error) {
        //log error. Maybe exit if it's irrecoverable.
    }
    setTimeout(doMainStuff, 10 * 1000);
  });
}

//when your program starts, do stuff right away.
doMainStuff();
Share:
15,976
user3246446
Author by

user3246446

Updated on June 05, 2022

Comments

  • user3246446
    user3246446 almost 2 years

    I just started using Node.js and I'm now trying to make my script run in the background every 10 seconds like a daemon waiting for something to do, when there is something to run from the database It reads the output from the program and does certain tasks depending on the output.

    This is what I've been able to do so far, It works just as I intended but can only run once even in the background. How can I make it run like a daemon every 10 seconds?

    Code:

    var spawn = require('child_process').spawn;
    var mysql = require('mysql');
    var JSFtp = require('jsftp');
    var check = require('node-validator').check;
    var sanitize = require('node-validator').sanitize;
    
    //Setup the db connection
    var db = mysql.createConnection({
      host : 'db',
      port : 3306,
      database: 'db',
      user : 'db',
      password : 'db'
    });
    
    //Make the connection
    db.connect(function(err){
        if(err != null) {
            res.end('Error connecting to mysql:' + err+'\n');
        }
    });
    
    var die = function(msg){
        console.error(msg);
        process.exit(1);
    }
    
    function ip2long ( ip_address ) {
        var output = false;
        var parts = [];
        if (ip_address.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)) {
            parts  = ip_address.split('.');
            output = ( parts[0] * 16777216 +
            ( parts[1] * 65536 ) +
            ( parts[2] * 256 ) +
            ( parts[3] * 1 ) );
        }
        return output;
    }
    
    
    db.query("SELECT * FROM queue WHERE cooldown > UNIX_TIMESTAMP(NOW()) AND simulated=0 ORDER BY cooldown DESC LIMIT 1", function(err, rows){
        if(err != null){
            die("Query error: " + err);
        }
    
        if(rows < 1){
            die("No rows");
        }
    
        //Set the vars from the query
        var name = rows[0]['name'];
        var ip = rows[0]['ip'];
        var iterations = rows[0]['runs'];
    
        var bin = "/home/hoar/sum/run"
        var args = ['arg='+name, 'arg2='+iterations, 'path=/var/www/upload/'+name+'.html', 'output=log.log'];
        var proc = spawn(bin, args);
        var time = "/.*/";
        var pct = "/^\d/";
        var name = rows[0]['name'];
        var ip = rows[0]['ip'];
        var iterations = rows[0]['runs'];
    
        proc.stdout.setEncoding('utf8');
        proc.stdout.on('data', function(data) {
            var str = data.toString();
            var s = str.split("|");
            var p = s[0].split("/");
            var t = (s[1] == null) ? "" : s[1];
    
            if(p != null && s[0] != "@"){ //Needed to check for @ because the program prints this as first line, which is good then we can do the query further done only once.
                //Check the return numbers from simc to see how many sims it has done
                if(parseInt(p[0]) < parseInt(p[1])){ 
                    //Check if the 6th match is a number and the 7th only contains letters
                    if(t != null){ 
                        var time = t.replace(/(\r\n|\n|\r)/gm,""); //Remove any line disturbers for db
    
                        //Update the database with the amount of time left on the simulation
                        db.query("UPDATE `queue` SET `status`=" + db.escape(time) + " WHERE (`name`=" + name + ")");
                        //console.log(p[0]+"/"+p[1] + " - " + t + " left");
                    }
                    //console.log(p[0]+"/"+p[1] + " iterations done");
                }
            }else{
                //If the stdout is null run this query since we don't want to run this more than once.
                db.query("UPDATE `queue` SET `simulated`='2' WHERE (`name`=" + name + " AND simulated!='2')");
                //console.log("Updated db to 2");
            }
        });
    
        proc.stderr.on('data', function (data) {
            var str = data.toString();
            //If the program returns stderr we want to make sure it stops and we update the database to let the user know.
            if(str.indexOf("ERROR! Setup failure...")){ 
    
                //Update the database with the amount of time left on the simulation
                db.query("UPDATE `queue` SET `simulated`='3' WHERE (`name`=" + name + ")");
    
                //Kill the DB connection
                db.destroy(); 
                die("There was an error: " + data);
            }
        });
    
        proc.on('exit', function (code) {
        //Setup the ftp connection
        var ftp = new JSFtp({
          host: "ftp",
          port: 21,
          user: "ftp",
          pass: "ftp"
        });
    
            //Simulation ended with success update the database and kill.
            db.query("UPDATE `queue` SET `simulated`='1' WHERE (`name`=" + name + " AND simulated='2')");
    
            ftp.put('/var/www/upload/'+rows[0]['name']+'.html', 'public_html/mysite/'+ip2long(rows[0]['ip'])+'/'+rows[0]['name']+'.html', function(hadError) {
              if (!hadError)
                console.log("FTP error");
    
                ftp.raw.quit();
            });
            db.destroy(); 
            //die("Simulation is done");
        });
    
    });//end sql
    
  • t.niese
    t.niese over 10 years
    setInterval is a bad idea, as the current task could still be running for some reason and then you would trigger it another time. It is better to wither set a timeout of 10 seconds in the success (which indeed would not result in a execution every 10sec but 10sec + elapsed time) or measure the elapsed time form the start of the task to the complete of the task and use the difference for the timeout.
  • user3246446
    user3246446 over 10 years
    So I can wrap the code in a function run it where //do all your stuff is check the return and then time it out?
  • user3246446
    user3246446 over 10 years
    I'm not entirely sure on what you mean with lastAsyncThing, since the script ends in different places depending on if the program succeeds or not. Where exactly would I put the timeout?
  • vodolaz095
    vodolaz095 over 10 years
    probably we need some variable to be the lock to eliminate the race conditions?
  • nehem
    nehem about 4 years
    @peter Do you have a fiddle for this?