Agenda - my favorite Node scheduler

Emilia Szymańska bio photo By Emilia Szymańska Comment

In the previous post I showed you how to really quickly get started with MongoDB. That is because today I wanted to show you my favorite node scheduler and it utilizes MongoDB for storing jobs. :) In the Ambient Weather project the scheduler will be responsible for firing the job for updating the weather data. I’ve been using this library for over a year on production on Kickerinho World server for scheduling matches creations (it happens once a day). I also use it in another production server – the one responsible for ad mediation.

Why do I love it? It works perfectly, is easy to set up and works greatly with the database I’m usually already using in the project. It stores jobs definitions in the db, so it’s not lost when server goes down.

So let’s start by adding Agenda library to our project.

Firstly, we have to define which jobs we will run according to the schedule. For now I created a function that will only print something to the console. It doesn’t take any arguments, but it can have some other custom args:

export default class Jobs {
    static get Ids() {
        return {
            UpdateAll: "UpdateAll"
        }
    };

    static UpdateAll(): void {
        console.log("UPDATED!!! ;)");
    }
}

This class also contain Job ids, as it will be needed by the library, but there is no code strictly connected to the scheduler.

The class responsible for wiring up jobs will be called “UpdateScheduler”. It needs the scheduler library imported along with database config and jobs definitions:

import * as Agenda from "agenda";
import WeatherJobs from "./Jobs";
import GlobalParameters from "../config/constants/GlobalParameters";

Firstly, in the class constructor we need to set up a database connection. In my project Mongo Uri is saved in environment variables and class GlobalParameters is responsible for reading the credentials and creating the uri. After receiving the callback about the connection being set up, we are ready to define the jobs. We need to define jobs first – connect the Id with a function:

    this.agenda.define(WeatherJobs.Ids.UpdateAll, WeatherJobs.UpdateAll);

Then we have to define how often to fire the job. Agenda library uses my favorite, human-readable format (I’m sorry, but every time I have to use cron-like data, I need to google it), for example “20 seconds”, “1 year”, etc.

    this.currentJob = this.agenda.every(this.jobInterval, WeatherJobs.Ids.UpdateAll);

This line of code defines that every time the interval passes, the job will be fired. Also, if the scheduled time has passed when the server was not running, it will be fired as soon as server is started again.

Remember – this is not cron, so the server app has to be up and running for the job to get executed.

After setting these things up, we only need to start the agenda:

    this.agenda.start();

So complete class looks like this:

export default class UpdateScheduler {
    readonly JobsCollectionName: string = "Jobs";
    agenda: Agenda;
    currentJob: Agenda.Job;
    jobInterval: string;

    constructor(jobInterval: string = "20 seconds") {
        this.jobInterval = jobInterval;
        this.agenda = new Agenda();
        this.agenda.database(GlobalParameters.MongoUri, this.JobsCollectionName, null, err => this.Init(err));
    }

    Init(err) {
        console.log(err);
        this.DefineJobs();
        this.Set();
        this.agenda.start();
    }

    Set() {
        this.currentJob = this.agenda.every(this.jobInterval, WeatherJobs.Ids.UpdateAll);
    }

    private DefineJobs() {
        this.agenda.define(WeatherJobs.Ids.UpdateAll, WeatherJobs.UpdateAll);
    }
}

The only thing left is creating an instance of this class somewhere in the app. As my will be really simple, I leave it in the main app code (this is basically the only thing that will have to be done here):

app.listen(port, () => {
    console.log("Node app is running at localhost:" + port);
    (new UpdateScheduler());
});

And that’s it. The whole UpdateScheduler class looks like this:

export default class UpdateScheduler {
    readonly JobsCollectionName: string = "Jobs";
    agenda: Agenda;
    currentJob: Agenda.Job;
    jobInterval: string;

    constructor(jobInterval: string = "20 seconds") {
        this.jobInterval = jobInterval;
        this.agenda = new Agenda();
        this.agenda.database(GlobalParameters.MongoUri, this.JobsCollectionName, null, err => this.Init(err));
    }

    Init(err) {
        if (err) {
            console.log(err);
        } else {
            this.DefineJobs();
            this.Set();
            this.agenda.start();
        }
    }

    Set() {
        this.currentJob = this.agenda.every(this.jobInterval, WeatherJobs.Ids.UpdateAll);
    }

    private DefineJobs() {
        this.agenda.define(WeatherJobs.Ids.UpdateAll, WeatherJobs.UpdateAll);
    }
}

The only thing left is creating a real job, which will download the temperature and run external script for updating the LED stripe. I hope that in the next post we will see the first milestone up and running – the LED thermometer. :)

And as always - like my fan page or follow me on Twitter to get notified when future posts appear. (。◕‿◕。)

comments powered by Disqus