Permalink
Browse files

Promise rewrite (#557)

Rewrite Agenda API support promises. This is a breaking change.

No more callbacks! Instead of:

```
function graceful() {
  agenda.stop(function() {
    process.exit(0);
  });
}
```

You need to:
```
async function graceful() {
  await agenda.stop();
  process.exit(0);
}
```

You don't anymore have to listen for `start` event. Instead you can do:
```
await agenda.start();
agenda.every('10 minutes', 'example');
```

However, this will still work:
```
agenda.on('ready', function () {
  agenda.every('10 minutes', 'example');
  agenda.start();
});
```

See the documentation for more!

Drops support for Node.js versions 4, 5, 6

Contains also other small fixes:
- Jobs _emit_ errors instead of throwing them
- Fixes some flaky tests
- Adds docs generator (`npm run docs` to generate `/docs`)
  • Loading branch information...
OmgImAlexis authored and simison committed Jun 9, 2018
1 parent 0a82921 commit b9ecca90916abc88c95d5f229a3fa21c7e356504
Showing with 1,329 additions and 1,229 deletions.
  1. +3 −1 .gitignore
  2. +23 −0 .jsdoc.json
  3. +49 −45 README.md
  4. +12 −14 lib/agenda/cancel.js
  5. +3 −2 lib/agenda/create.js
  6. +2 −0 lib/agenda/database.js
  7. +17 −13 lib/agenda/db-init.js
  8. +2 −0 lib/agenda/default-concurrency.js
  9. +3 −1 lib/agenda/default-lock-lifetime.js
  10. +3 −1 lib/agenda/default-lock-limit.js
  11. +2 −0 lib/agenda/define.js
  12. +31 −48 lib/agenda/every.js
  13. +7 −5 lib/agenda/find-and-lock-next-job.js
  14. +26 −8 lib/agenda/index.js
  15. +8 −14 lib/agenda/jobs.js
  16. +3 −1 lib/agenda/locklimit.js
  17. +2 −0 lib/agenda/max-concurrency.js
  18. +2 −0 lib/agenda/mongo.js
  19. +2 −0 lib/agenda/name.js
  20. +9 −7 lib/agenda/now.js
  21. +2 −0 lib/agenda/process-every.js
  22. +7 −6 lib/agenda/purge.js
  23. +115 −122 lib/agenda/save-job.js
  24. +24 −42 lib/agenda/schedule.js
  25. +2 −0 lib/agenda/sort.js
  26. +10 −6 lib/agenda/start.js
  27. +22 −21 lib/agenda/stop.js
  28. +2 −0 lib/job/compute-next-run-at.js
  29. +2 −0 lib/job/disable.js
  30. +3 −1 lib/job/enable.js
  31. +2 −0 lib/job/fail.js
  32. +6 −0 lib/job/index.js
  33. +3 −1 lib/job/is-running.js
  34. +2 −0 lib/job/priority.js
  35. +5 −4 lib/job/remove.js
  36. +2 −0 lib/job/repeat-at.js
  37. +4 −2 lib/job/repeat-every.js
  38. +55 −50 lib/job/run.js
  39. +7 −5 lib/job/save.js
  40. +2 −0 lib/job/schedule.js
  41. +2 −0 lib/job/to-json.js
  42. +2 −0 lib/job/touch.js
  43. +2 −0 lib/job/unique.js
  44. +14 −0 lib/no-callback.js
  45. +1 −1 lib/utils/create-job.js
  46. +6 −4 lib/utils/process-jobs.js
  47. +9 −1 package.json
  48. +190 −273 test/agenda.js
  49. +2 −2 test/fixtures/agenda-instance.js
  50. +428 −501 test/job.js
  51. +23 −24 test/retry.js
  52. +164 −3 yarn.lock
@@ -1,3 +1,5 @@
node_modules
coverage.html
.idea
.idea
.DS_Store
docs
@@ -0,0 +1,23 @@
{
"tags": {
"allowUnknownTags": true,
"dictionaries": ["jsdoc"]
},
"source": {
"include": ["lib", "package.json", "README.md"],
"includePattern": ".js$",
"excludePattern": "(node_modules/|docs)"
},
"plugins": [
"plugins/markdown"
],
"templates": {
"referenceTitle": "Agenda"
},
"opts": {
"destination": "./docs/",
"encoding": "utf8",
"recurse": true,
"template": "./node_modules/jsdoc-template"
}
}
@@ -56,14 +56,14 @@ agenda.define('delete old users', function(job, done) {
User.remove({lastLogIn: { $lt: twoDaysAgo }}, done);
});
agenda.on('ready', function() {
agenda.every('3 minutes', 'delete old users');
(async function() { // IIFE to give access to async/await
await agenda.start();
// Alternatively, you could also do:
agenda.every('*/3 * * * *', 'delete old users');
await agenda.every('3 minutes', 'delete old users');
agenda.start();
});
// Alternatively, you could also do:
await agenda.every('*/3 * * * *', 'delete old users');
})();
```
@@ -78,18 +78,18 @@ agenda.define('send email report', {priority: 'high', concurrency: 10}, function
}, done);
});
agenda.on('ready', function() {
agenda.schedule('in 20 minutes', 'send email report', {to: 'admin@example.com'});
agenda.start();
});
(async function() {
await agenda.start();
await agenda.schedule('in 20 minutes', 'send email report', {to: 'admin@example.com'});
})();
```
```js
agenda.on('ready', function() {
(async function() {
var weeklyReport = agenda.create('send email report', {to: 'another-guy@example.com'})
weeklyReport.repeatEvery('1 week').save();
agenda.start();
});
await agenda.start();
await weeklyReport.repeatEvery('1 week').save();
})();
```
# Full documentation
@@ -153,7 +153,9 @@ You can also specify it during instantiation.
var agenda = new Agenda({db: { address: 'localhost:27017/agenda-test', collection: 'agendaJobs' }});
```
Agenda will emit a `ready` event (see [Agenda Events](#agenda-events)) when properly connected to the database and it is safe to start using Agenda.
Agenda will emit a `ready` event (see [Agenda Events](#agenda-events)) when properly connected to the database.
It is safe to call `agenda.start()` without waiting for this event, as this is handled internally.
If you're using the `db` options, or call `database`, then you may still need to listen for `ready` before saving jobs.
### mongo(mongoClientInstance)
@@ -165,24 +167,29 @@ Please note that this must be a *collection*. Also, you will want to run the fol
afterwards to ensure the database has the proper indexes:
```js
agenda.on('ready', () => {
agenda._collection.createIndex({
disabled: 1,
lockedAt: 1,
name: 1,
nextRunAt: 1,
priority: -1
}, {
name: 'findAndLockNextJobIndex'
}, (err) => {
if (err) {
console.log('Failed to create Agenda index!');
console.error(err);
} else {
console.log('Agenda index created.');
}
});
});
(async () => {
await agenda._ready;
try {
agenda._collection.createIndex({
disabled: 1,
lockedAt: 1,
name: 1,
nextRunAt: 1,
priority: -1
}, {
name: 'findAndLockNextJobIndex'
});
} catch (err) {
console.log('Failed to create Agenda index!');
console.error(err);
throw err;
}
console.log('Agenda index created.');
})();
```
You can also specify it during instantiation.
@@ -320,13 +327,13 @@ By default it is `{ nextRunAt: 1, priority: -1 }`, which obeys a first in first
An instance of an agenda will emit the following events:
- `ready` - called when Agenda mongo connection is successfully opened
- `ready` - called when Agenda mongo connection is successfully opened and indeces created.
If you're passing agenda an existing connection, ou shouldn't need to listen for this, as `agenda.start()` will not resolve until indeces have been created.
If you're using the `db` options, or call `database`, then you may still need to listen for `ready` before saving jobs. `agenda.start()` will still wait for the connection to be opened.
- `error` - called when Agenda mongo connection process has thrown an error
```js
agenda.on('ready', function() {
agenda.start();
});
await agenda.start();
```
## Defining Job Processors
@@ -535,10 +542,9 @@ job queues can grab them / they are unlocked should the job queue start again. H
shutdown.
```js
function graceful() {
agenda.stop(function() {
process.exit(0);
});
async function graceful() {
await agenda.stop();
process.exit(0);
}
process.on('SIGTERM', graceful);
@@ -948,10 +954,8 @@ jobTypes.forEach(function(type) {
require('./lib/jobs/' + type)(agenda);
})
if(jobTypes.length) {
agenda.on('ready', function() {
agenda.start();
});
if (jobTypes.length) {
agenda.start(); // Returns a promise, which should be handled appropriately
}
module.exports = agenda;
@@ -3,21 +3,19 @@ const debug = require('debug')('agenda:cancel');
/**
* Cancels any jobs matching the passed MongoDB query, and removes them from the database.
* @param {Object} query MongoDB query to use when cancelling
* @param {Function} cb callback(error, numRemoved) when cancellation fails or passes
* @caller client code, Agenda.purge(), Job.remove()
* @returns {undefined}
* @name Agenda#cancel
* @function
* @param {Object} query MongoDB query to use when cancelling
* @caller client code, Agenda.purge(), Job.remove()
* @returns {Promise<Number>} A promise that contains the number of removed documents when fulfilled.
*/
module.exports = function(query, cb) {
module.exports = function(query) {
debug('attempting to cancel all Agenda jobs', query);
this._collection.deleteMany(query, (error, result) => {
if (cb) {
if (error) {
debug('error trying to delete jobs from MongoDB');
} else {
debug('jobs cancelled');
}
cb(error, result && result.result ? result.result.n : undefined);
}
return this._collection.deleteMany(query).then(({result}) => {
debug('%s jobs cancelled', result.n);
return result.n;
}).catch(err => {
debug('error trying to delete jobs from MongoDB');
throw err;
});
};
@@ -4,10 +4,11 @@ const Job = require('../job');
/**
* Given a name and some data, create a new job
* @name Agenda#create
* @function
* @param {String} name name of job
* @param {Object} data data to set for job
* @access protected
* @returns {module.Job} instance of new job
* @returns {Job} instance of new job
*/
module.exports = function(name, data) {
debug('Agenda.create(%s, [Object])', name);
@@ -4,6 +4,8 @@ const debug = require('debug')('agenda:database');
/**
* Connect to the spec'd MongoDB server and database.
* @name Agenda#database
* @function
* @param {String} url MongoDB server URI
* @param {String} collection name of collection to use
* @param {Object} options options for connecting
@@ -3,6 +3,8 @@ const debug = require('debug')('agenda:db_init');
/**
* Setup and initialize the collection used to manage Jobs.
* @name Agenda#dbInit
* @function
* @param {String} collection name or undefined for default 'agendaJobs'
* @param {Function} cb called when the db is initialized
* @returns {undefined}
@@ -12,19 +14,21 @@ module.exports = function(collection, cb) {
debug('init database collection using name [%s]', collection);
this._collection = this._mdb.collection(collection || 'agendaJobs');
debug('attempting index creation');
this._collection.createIndex(this._indices, {
name: 'findAndLockNextJobIndex'
}, (err, result) => {
if (err) {
debug('index creation failed');
self.emit('error', err);
} else {
debug('index creation success');
self.emit('ready');
}
this._collection.createIndex(
this._indices,
{name: 'findAndLockNextJobIndex'},
err => {
if (err) {
debug('index creation failed');
self.emit('error', err);
} else {
debug('index creation success');
self.emit('ready');
}
if (cb) {
cb(err, self._collection);
if (cb) {
cb(err, self._collection);
}
}
});
);
};
@@ -3,6 +3,8 @@ const debug = require('debug')('agenda:defaultConcurrency');
/**
* Set the default concurrency for each job
* @name Agenda#defaultConcurrency
* @function
* @param {Number} num default concurrency
* @returns {exports} agenda instance
*/
@@ -4,8 +4,10 @@ const debug = require('debug')('agenda:defaultLockLifetime');
/**
* Set the default lock time (in ms)
* Default is 10 * 60 * 1000 ms (10 minutes)
* @name Agenda#defaultLockLifetime
* @function
* @param {Number} ms time in ms to set default lock
* @returns {exports} agenda instance
* @returns {Agenda} agenda instance
*/
module.exports = function(ms) {
debug('Agenda.defaultLockLifetime(%d)', ms);
@@ -3,8 +3,10 @@ const debug = require('debug')('agenda:defaultLockLimit');
/**
* Set default lock limit per job type
* @name Agenda#defaultLockLimit
* @function
* @param {Number} num Lock limit per job
* @returns {exports} agenda instance
* @returns {Agenda} agenda instance
*/
module.exports = function(num) {
debug('Agenda.defaultLockLimit(%d)', num);
@@ -4,6 +4,8 @@ const debug = require('debug')('agenda:define');
/**
* Setup definition for job
* Method is used by consumers of lib to setup their functions
* @name Agenda#define
* @function
* @param {String} name name of job
* @param {Object} options options for job to run
* @param {Function} processor function to be called to run actual job
Oops, something went wrong.

0 comments on commit b9ecca9

Please sign in to comment.