From 03b62fa9eecfd1eb63728032ec6f8f6991f5ed3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Dalga=CC=8Ard?= Date: Mon, 23 Feb 2015 17:52:34 +0100 Subject: [PATCH 1/3] Patch for issue #41 --- synced-cron-server.js | 48 ++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/synced-cron-server.js b/synced-cron-server.js index 26a311f..15ee434 100644 --- a/synced-cron-server.js +++ b/synced-cron-server.js @@ -56,7 +56,7 @@ var log = { var scheduleEntry = function(entry) { var schedule = entry.schedule(Later.parse); entry._timer = - SyncedCron._laterSetInterval(SyncedCron._entryWrapper(entry), schedule); + SyncedCron._laterSetInterval(SyncedCron._entryWrapper(entry), schedule, entry.name); log.info('SyncedCron: scheduled "' + entry.name + '" next run @' + Later.schedule(schedule).next(1)); @@ -193,9 +193,9 @@ SyncedCron._reset = function() { // between multiple, potentially laggy and unsynced machines // From: https://github.com/bunkat/later/blob/master/src/core/setinterval.js -SyncedCron._laterSetInterval = function(fn, sched) { +SyncedCron._laterSetInterval = function(fn, sched, name) { - var t = SyncedCron._laterSetTimeout(scheduleTimeout, sched), + var t = SyncedCron._laterSetTimeout(scheduleTimeout, sched, name), done = false; /** @@ -203,10 +203,12 @@ SyncedCron._laterSetInterval = function(fn, sched) { * interval. */ function scheduleTimeout(intendedAt) { - if(!done) { + if (!done) fn(intendedAt); - t = SyncedCron._laterSetTimeout(scheduleTimeout, sched); - } + + // Check for done again, in case entry is removed inside job function body + if (!done) + t = SyncedCron._laterSetTimeout(scheduleTimeout, sched, name); } return { @@ -224,7 +226,7 @@ SyncedCron._laterSetInterval = function(fn, sched) { }; // From: https://github.com/bunkat/later/blob/master/src/core/settimeout.js -SyncedCron._laterSetTimeout = function(fn, sched) { +SyncedCron._laterSetTimeout = function(fn, sched, name) { var s = Later.schedule(sched), t; scheduleTimeout(); @@ -236,21 +238,29 @@ SyncedCron._laterSetTimeout = function(fn, sched) { */ function scheduleTimeout() { var now = Date.now(), - next = s.next(2, now), - diff = next[0].getTime() - now, - intendedAt = next[0]; - - // minimum time to fire is one second, use next occurrence instead - if(diff < 1000) { - diff = next[1].getTime() - now; - intendedAt = next[1]; - } + next = s.next(2, now); - if(diff < 2147483647) { - t = Meteor.setTimeout(function() { fn(intendedAt); }, diff); + if (next) { + var diff = next[0].getTime() - now, + intendedAt = next[0]; + + // minimum time to fire is one second, use next occurrence instead + if(next[1] && diff < 1000) { + diff = next[1].getTime() - now; + intendedAt = next[1]; + } + + if(diff < 2147483647) { + t = Meteor.setTimeout(function() { fn(intendedAt); }, diff); + } + else { + t = Meteor.setTimeout(scheduleTimeout, 2147483647); + } } else { - t = Meteor.setTimeout(scheduleTimeout, 2147483647); + log.info('SyncedCron: No more future events for "' + name + '".'); + + SyncedCron.remove(name); } } From a113b0e13609fbe42064466b1d0d54b278973375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Dalga=CC=8Ard?= Date: Mon, 23 Feb 2015 18:57:20 +0100 Subject: [PATCH 2/3] =?UTF-8?q?Test=20stubs=20for=20issue=20#41=20(pull=20?= =?UTF-8?q?request=20#42)=20=E2=80=93=20sorry,=20don't=20know=20anything?= =?UTF-8?q?=20about=20Tinytest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- synced-cron-tests.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/synced-cron-tests.js b/synced-cron-tests.js index 805e6e4..a72414a 100644 --- a/synced-cron-tests.js +++ b/synced-cron-tests.js @@ -131,3 +131,41 @@ Tinytest.add('SyncedCron.add starts by it self when running', function(test) { test.equal(SyncedCron.running, false); test.equal(_.keys(SyncedCron._entries).length, 0); }); + + +Tinytest.add('Removing current entry inside job prevents the next event from running', function(test) { + //! Preparation + + SyncedCron.add({ + name: "testRemove", + schedule: function (parser) { + return parser.recur().every(1).second(); + }, + job: function () { + console.log("TEST"); + SyncedCron.remove("testRemove"); + } + }); + + //! Should test that "TEST" is only output to log once +}); + +Tinytest.add('Automatically remove entry after last event in schedule has been triggered', function(test) { + //! Preparation + + SyncedCron.add({ + name: "testRemove", + schedule: function (parser) { + var date = new Date; + date.setSeconds(date.getSeconds() + 2); + + return parser.recur().on(date).fullDate(); + }, + job: function () { + console.log("TEST"); + } + }); + + //! Should test that "TEST" is only output to log once + // and that "testRemove" is removed after 2 seconds +}); From 9502319924b032772ea23a9dd14d1970d50296cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Dalga=CC=8Ard?= Date: Mon, 23 Feb 2015 18:57:42 +0100 Subject: [PATCH 3/3] Documentation for patch (running job only once) --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index 13f9f7c..c3bfb99 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,29 @@ To start processing your jobs, somewhere in your project add: SyncedCron.start(); ``` +If you want a job to run only once or a limited number of times, you can either remove it inside the job function or create a limited schedule: + +```javascript +SyncedCron.add({ + name: 'myJob', + schedule: function(parser) { + // EITHER set a limited schedule + var date = new Date; + date.setHours(date.getHours() + 12); + + // (this Later.js object only contains a single event) + return parser.recur().on(date).fullDate(); + }, + job: function() { + // (do something) + + // ... OR remove the job inside function + SyncedCron.remove('myJob'); + } +}); +``` + + ### Advanced SyncedCron uses a collection called `cronHistory` to syncronize between processes. This also serves as a useful log of when jobs ran along with their output or error. A sample item looks like: