I am using node-cron to send operational emails (currently searching for new emails that need to be sent every minute).
My function (operationalEmails) looks for mongodb records that have a sent flag = false, sends the email, then changes the sent flag = true.
If an iteration of the cron has a large payload of records to send, it could take more than a minute.
How do I ensure the last iteration of the cron is complete before starting a new one?
//Run every Min
cron.schedule("* * * * *", () => {
operationalEmails();
});
you would need to create a simple lock
const running = false;
function operationalEmails() {
if (running) {
return
}
running = true;
// do stuff
running = false;
}
//Run every Min
cron.schedule("* * * * *", () => {
operationalEmails();
});
Related
I need to call a function every given period of time on a route for example my function:
function hello(){
console.log("Hi")
}
app.post("/", (req,res) => {
res.send("Hi")
hello()
}
Well, I didn't put all the code in my application, just the part that matters, I need to call the hello function every 5 seconds. I need to use this method of calling a given function every given period, in my application.
you can use node-schedule !
for example: Execute a cron job every 5 Minutes = */5 * * * *
var schedule = require('node-schedule');
var j = schedule.scheduleJob('*/5 * * * *', function(){
console.log('The answer to life, the universe, and everything!');
});
Another solution:
setInterval
setInterval(() => {
// do something every 5 seconds
}, 5000);
I have built a script that looks at fields within a saved search to process, when the script processes each line of the saved search . The script has to process a lot of lines of data and I'm running into issue where I get an SSS Usage Limit exceeded message just due to the nature of the amount of information that I'm processing. Therefore I'm wondering if on each run of the script if I can limit the amount of orders processed and then consecutively run the script until there aren't any more records that need to be processed
Previously I have just restricted the number of records that the script processes at a time and then just manually trigger the script until there aren't anymore lines left to process. I see that you can trigger the script to run every 15 minutes or so but would like it to run each day and then run in a 15 minute cadence after that until the saved search has been exhausted. Then the script would be triggered to run again the following day etc for every 15 min until all records are processed. From research don't know if this type of scheduling is possible.
code itself is working the scheduling of it is what I need guidance on
The other alternative (and one that I've used successfully) is to exit the scheduled script whent the usage reaches a certain point, but before you exit, trigger the scheduled script again. I've used this successfully to process thousands of records (such as mass deletions).
/**
* #NApiVersion 2.x
* #NScriptType ScheduledScript
* #NModuleScope SameAccount
*/
define(['N/record', 'N/runtime', 'N/search', 'N/task'],
/**
* #param {record} record
* #param {search} search
*/
function(record, runtime, search, task) {
const governanceCap = 9950;
function getAllResults(s) {
var results = s.run();
var searchResults = [];
var searchid = 0;
do {
var resultslice = results.getRange({start:searchid,end:searchid+1000});
resultslice.forEach(function(slice) {
searchResults.push(slice);
searchid++;
}
);
} while (resultslice.length >=1000);
return searchResults;
}
/**
* Definition of the Scheduled script trigger point.
*
* #param {Object} scriptContext
* #param {string} scriptContext.type - The context in which the script is executed. It is one of the values from the scriptContext.InvocationType enum.
* #Since 2015.2
*/
function execute(scriptContext) {
function rescheduleCurrentScript() {
var scheduledScriptTask = task.create({
taskType: task.TaskType.SCHEDULED_SCRIPT
});
scheduledScriptTask.scriptId = runtime.getCurrentScript().id;
scheduledScriptTask.deploymentId = runtime.getCurrentScript().deploymentId;
return scheduledScriptTask.submit();
}
try {
var script = runtime.getCurrentScript();
// GET YOUR SEARCH HERE
var mySearch = getAllResults(
search.create({
type: "transaction",
filters:
[
["mainline","is","T"],
],
columns:
[
"name",
"tranid",
"type",
search.createColumn({
name:"datecreated",
sort: search.Sort.DESC
}),
]
})
);
var recCount = mySearch.length;
for (each in mySearch) {
try {
record.delete({
type: transSearch[each].getValue({name:'type'}),
id: transSearch[each].id
});
} catch (err) {log.error(err.name,err.message) }
var govPointsUsed = 10000-script.getRemainingUsage();
script.percentComplete = (govPointsUsed/governanceCap*100).toFixed(1);
if (govPointsUsed >= governanceCap) {
var taskId = rescheduleCurrentScript();
log.audit('Rescheduling status: ','Task ID:' + taskId);
return;
}
}
} catch (err) { log.error(err.name,err.message + '; Stack: '+err.stack ) };
}
return {
execute: execute
};
});
Worked like a charm!!
I would encourage you to use a Map/Reduce script. That is the correct script type to use when dealing with a lot of data and can handle governance issues much better.
I had this problem some time ago. The map/reduce script can be the solution if you run 2.0 scripts. In my case, I had 'legacy' scripts in versions 1.X and 2.X. And all of them throw from time to time 'SSS_TIME_LIMIT_EXCEEDED'.
My solution :)
I created a script, kind of 'listener'. I removed link to the script because the rep. is not active anymore
// *add the script listener
// [it can be used for both script versions 1.X and 2.X]
/** you have to calculate the 'process usage' by 1 loop cycle.
use script.getRemainingUsage() */
var recallValue = 200;
var stopValue = 200;
var recallIsRequired = true;// use false to stop the script without recall
var script = setScriptListener(recallValue, stopValue, recallIsRequired);
for (var data in data_contaniner){
if(script.canContinue()){
//run you processing
// !important : mark the data as 'processed'.
//otherwise you will have a lot of duplicates after script recall
}else{
//stop the loop
break;
}
}
I have divided a day on 6 parts and each part of a day has its own interval. My function must run in this intervals.
for example 12:00pm - 15:00pm interval is 10 min, so function must be called every 10 min, but 15:00pm - 20:00pm interval is 2 min so from 15:01pm interval must be changed from 10min to 2min.
Need a suggestion how to build this kind of timer.
I can get intervals from mongoDB or local json file.
I guess I have to check what time is it and get an interval (from mongoDB or json file) for that time, then pass it to setInterval() or Cron job scheduler.
Tryed this way but every time im passing new interval last intervals are still working: If interval is 2 min and im changing it to 5 min, function is called twice: every 2 min and every 5 min in both setInterval() and Cron
const test = (min) => {
console.log(min);
var intervale = setInterval(() => {
console.log('firing', min, new Date());
}, min);
}
const test = (min) => {
cron.schedule(`0 */${min} * * * *`, () => {
console.log('firing cron', new Date())
});
}
thank you
Can you do something like this? This uses the EventEmitter model and fires an event based on the custom Interval.
const eventemitter_cls = require("events").EventEmitter;
let eventemitter = new eventemitter_cls();
intervalName = "randomintervals"
let sumOfTwoNumbers = () => {
console.log(`hello world ${(new Date()).getMinutes()} ${(new Date()).getSeconds()}` );
// After you are done with your function, again you need to emit the event based on the time interval
setTimeout(() => {
eventemitter.emit(intervalName)
}, getTimeInterval());
}
let getTimeInterval = () => {
let currentTime = new Date();
// put your complicated interval schedule here
if(currentTime.getHours() >= 10 && currentTime.getHours() < 11){
return 5000;
}
else if (currentTime.getHours() > 12 && currentTime.getHours() < 15) {
return 2000;
}
else{
return 1000;
}
}
// give your function name here. the function will be called whenever someone emits intervalName
eventemitter.on(intervalName, sumOfTwoNumbers);
eventemitter.emit(intervalName);
I think It's still firing because there is no clearInterval telling it to stop.
Suppose I have this code running on 3 different servers and every server is using a single database.
setInterval(function(){
if(userArray) {
var report = mongoose.connection.db.collection('report');
report.insert({datenow:new Date(),userlist:userArray},function(err,doc) {
if(err) throw err;
});
}
},600000);
So, this piece of code is running every 10 minutes on every server but I want only one of them to insert the data into the database. Since the data is same it is getting inserted 3 times.
How do I check if the data is already inserted into the database by any one of the servers.
I tried making an incrementing count variable and insert it into the database and use it as a unique ID to check if it exists in the database. If it exists then I won't insert the data. But what if I have to restart the server for some reason, then the count will be reset to its initial value and this doesn't seem a viable solution.
So, how do I approach this problem? I am guessing I have to compare time somehow?
IMO, you should use a Cron expression instead of interval and use the execution time as primary key of your report when you perform the insertion in the database.
Explanation
Cron expression can garantee that the execution of your script will occur at an accurate time. If you use this Cron expression : 00 */10 * * * * (every 10 minutes), your script will execute at exactly 11:00:00, 11:10:00, 11:20:00, so on.. for every server you have.
So you can use this execution time as key for your reports and it will prevent multiple insertion of the same report.
Libs
You can use this lib to use Cron with Node.js : node-cron
Example
var CronJob = require('cron').CronJob;
new CronJob('* * * * * *', function() {
console.log('You will see this message every second');
}, null, true, 'America/Los_Angeles');
I hope this will help you.
So I had to end up comparing timestamps and checking it anything is inserted in the last 10 minutes. This is the solution I came up with.
setInterval(function(){
var currDate = new Date().getTime();
if(keyPairNameArray) {
var overallreport = mongoose.connection.db.collection('overallreport');
overallreport.find({}).sort({_id:-1}).limit(1).toArray(function(err, res){
if(res.length > 0){
var dbDate = new Date(res[0].datenow).getTime();
var diffDate = currDate - dbDate;
if(diffDate < 600000){
} else {
overallreport.insert({datenow:new Date(),userlist:keyPairNameArray},function(err,doc) {
if(err) throw err;
});
}
} else {
overallreport.insert({datenow:new Date(),userlist:keyPairNameArray},function(err,doc) {
if(err) throw err;
});
}
});
}
},610000);
In my app I use node-cache to hold some data that is needed very frequently. This data can change so I am using node-cron to periodically kick off a child process to pull refresh data from Mongo and pass it back to the node-cache to overwrite the key/value pairs.
The data is needed in a middleware. If I run the Cron process in the main server.js file everything works fine, but the node-cache data obviously isn't accessible in the middleware. If I run the Cron process in the middleware....it runs the cron + cache process every time the middleware is used (i.e. triggers the child process +mongo calls, etc defeating the purpose of the cache + cron approach).
How does one go about solving this issue? Thx.
The Cron code that kicks of the Cache (via Child Process) looks like this :
var companyCache = companyCache || new cache();
new CronJob('0 */15 * * * *', function () {
var child = childProcess.fork(process.cwd() + "/lib/cacheCronWorker.js");//,[],{execArgv: ['--debug-brk=55555']});
console.log("initialized child process for cacheCron");
child.send('refresh_company_cache');
child.on('message', function (m) {
if (m !== 'done') {
// Receive results from child process
companyCache.set(m.key, m.currObj, function (err, success) {
if (err) {
console.log("node-cache hit an error: " + err)
}
if (!err && success) {
}
});
}
else if (m === 'done') {
console.log("company cache load is " + m + " disconnecting child process");
child.disconnect();
}
});
console.log('You will see this message every 15 min when we refresh the cache');
}, null, true, 'America/Chicago', null, true);