egg-schedule

NPM version build status Test coverage David deps Known Vulnerabilities npm download

A schedule plugin for egg. It supports two scheduler types, worker and all, and can be extended by other plugins.

egg-schedule has been built-in for egg. It is enabled by default.

# Usage

Just add you job file to {app_root}/app/schedule.

// {app_root}/app/schedule/cleandb.js
const Subscription = require('egg').Subscription;

class CleanDB extends Subscription {
/**
* @property {Object} schedule
* - {String} type - schedule type, `worker` or `all`
* - {String} [cron] - cron expression, see [below](#cron-style-scheduling)
* - {Object} [cronOptions] - cron options, see [cron-parser#options](https://github.com/harrisiirak/cron-parser#options)
* - {String | Number} [interval] - interval expression in millisecond or express explicitly like '1h'. see [below](#interval-style-scheduling)
* - {Boolean} [immediate] - To run a scheduler at startup
* - {Boolean} [disable] - whether to disable a scheduler, usually use in dynamic schedule
*/
static get schedule() {
return {
type: 'worker',
cron: '0 0 3 * * *',
// interval: '1h',
// immediate: true,
};
}

async subscribe() {
await this.ctx.service.db.cleandb();
}
}

module.exports = CleanDB;

You can also use function simply.

exports.schedule = {
type: 'worker',
cron: '0 0 3 * * *',
// interval: '1h',
// immediate: true,
};

exports.task = async function (ctx) {
await ctx.service.db.cleandb();
};

# Overview

egg-schedule supports both time-based scheduling and interval-based scheduling.

Schedule decision is being made by agent process. agent triggers a task and sends message to worker process. Then, one or all worker process(es) execute the task based on schedule type.

To setup a schedule task, simply create a job file in {app_root}/app/schedule. A file contains one job and export schedule and task properties.

The rule of thumbs is one job per file.

# Task

Task is a class which will be instantiated every schedule, and subscribe method will be invoked.

You can get anonymous context with this.ctx.

  • ctx.method: SCHEDULE
  • ctx.path: /__schedule?path=${schedulePath}&${schedule}.

To create a task, subscribe can be generator function or async function. For example:

// A simple logger example
const Subscription = require('egg').Subscription;
class LoggerExample extends Subscription {
* subscribe() {
this.ctx.logger.info('Info about your task');
}
}
// A real world example: wipe out your database.
// Use it with caution. :)
const Subscription = require('egg').Subscription;
class CleanDB extends Subscription {
async subscribe() {
await this.ctx.service.db.cleandb();
}
}

# Scheduling

schedule is an object that contains one required property, type, and optional properties, { cron, cronOptions, interval, immediate, disable }.

# Cron-style Scheduling

Use cron-parser.

Note: cron-parser support second as optional that is not supported by linux crontab.

@hourly / @daily / @weekly / @monthly / @yearly is also supported.

*    *    *    *    *    *
┬ ┬ ┬ ┬ ┬ ┬
│ │ │ │ │ |
│ │ │ │ │ └ day of week (0 - 7) (0 or 7 is Sun)
│ │ │ │ └───── month (1 - 12)
│ │ │ └────────── day of month (1 - 31)
│ │ └─────────────── hour (0 - 23)
│ └──────────────────── minute (0 - 59)
└───────────────────────── second (0 - 59, optional)

Example:

// To execute task every 3 hours
exports.schedule = {
type: 'worker',
cron: '0 0 */3 * * *',
cronOptions: {
// tz: 'Europe/Athens',
}
};

# Interval-style Scheduling

To use setInterval, and support ms conversion style

Example:

// To execute task every 3 hours
exports.schedule = {
type: 'worker',
interval: '3h',
};

# Schedule Type

Build-in support is:

  • worker: will be executed in one random worker when schedule run.
  • all: will be executed in all workers when schedule run.

Custom schedule

To create a custom schedule, simply extend agent.ScheduleStrategy and register it by agent.schedule.use(type, clz). You can schedule the task to be executed by one random worker or all workers with the built-in method this.sendOne() or this.sendAll().

// {app_root}/agent.js
module.exports = function(agent) {
class CustomStrategy extends agent.ScheduleStrategy {
start() {
this.interval = setInterval(() => {
this.sendOne();
}, this.schedule.interval);
}
}
agent.schedule.use('custsom', CustomStrategy);
};

Then you could use it to defined your job:

// {app_root}/app/schedule/other.js
const Subscription = require('egg').Subscription;
class ClusterTask extends Subscription {
static get schedule() {
return {
type: 'custom',
};
}
async subscribe() {
await this.ctx.service.someTask.run();
}
}

# Dynamic schedule

// {app_root}/app/schedule/sync.js
module.exports = app => {
class SyncTask extends app.Subscription {
static get schedule() {
return {
interval: 10000,
type: 'worker',
// only start task when hostname match
disable: require('os').hostname() !== app.config.sync.hostname
};
}
async subscribe() {
await ctx.sync();
}
}
return SyncTask;
}

# Testing

app.runSchedule(scheduleName) is provided by egg-schedule plugin only for test purpose.

Example:

it('test a schedule task', async function () {
// get app instance
await app.runSchedule('clean_cache');
});

# Questions & Suggestions

Please open an issue here.

# License

MIT