npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

job-stash

v1.1.6

Published

Date based scheduler for Node.js that can persist jobs

Downloads

248

Readme

Job Stash

Job Stash is a date based scheduler for Node.js. It allows you to schedule jobs (callback functions) for execution at specific dates. It not only run's on memory but persists metadata of the job in MongoDB.

  • Allows you to schedule a task on a specific date

  • Persists task metadata in DB .Reschedule all the jobs using this on your app start.

  • Abstracts DB Implementation - Give it a DB Address(URI) and it forms a connection, Give it a MongoDBClient and it reuses the connection.

  • It's Atomic - If you have multiple machines running , only one of them will execute your job if you enable useLock while initialising.

  • Has Inbuilt Retry Mechanism, if something went wrong while executing the job, they are retried after a retry window(configurable).

Example Usage

Installation

npm i job-stash

Initialise the Scheduler


import { Scheduler } from 'job-stash';
const mongoConnectionString = 'mongodb://127.0.0.1/job_stash';

await Scheduler.init({ db: { address: mongoConnectionString } });

// Or override the default collection name:
// await Scheduler.init({db: {address: mongoConnectionString, collection: 'jobCollectionName'}});

// or pass additional connection options:
// await Scheduler.init({db: {address: mongoConnectionString, collection: 'jobCollectionName', options: {ssl: true}}});

// or pass in an existing mongodb-native MongoClient instance
// await Scheduler.init({mongo: myMongoClient});

If you use Mongoose and want to use it's MongoClient Instance

// After you do  await mongoose.connect(mongoUrl);
const mongoClient = mongoose.connection.getClient();
const db = mongoClient.db();

await Scheduler.init({ mongo: db });

Options

useLock

Takes a boolean which specifies whether to acquire a lock before executing a job.

If you are running a scheduler on multiple nodes, this has to be true. This makes sure your job is executed only on one of the nodes.

By default, it is set to false.

await Scheduler.init({ mongo: db }, { useLock: true });

retryCount

Takes a number which specifies the number of times a job must be retried . By default it is 3 .

await Scheduler.init({ mongo: db }, { retryCount: 3 });

retryWindowInSeconds

Takes a number representing the number of seconds to wait before retrying a failed job. By default, it is set to 3600 seconds (1 hour).

await Scheduler.init({ mongo: db }, { retryWindowInSeconds: 3600 });

Schedule a Job

const exampleCallback = () => console.log("starting");
const dateToRunOn = new Date("2023-10-04T17:43:28.798Z")
// or you can do new Date(Date.now() + 60 * 1000); // will run once after 1 minute
const jobId = v4(); // or use any unique identifier.
const job = await Scheduler.scheduleJob(exampleCallback, dateToRunOn, jobId);

You can even store any metadata related to the job.

For example, let's say based on the operation type I want to decide which function to call in my callback.

const jobMetaData = { operation: "gradeZero", activityType:"quiz" };
const job = await Scheduler.scheduleJob(
  exampleCallback,
  dateToRunOn,
  jobId,
  jobMetaData
);

You can make the callback function call do things based on some condition like a field called operation.

const scheduledJobCallBack = async () => {
  if (operation === OperationTypes.GRADE_ZERO) {
    await publishReport(jobId, activityType);
  }
};

Here we are calling the function publishReport if the operation type is GRADE_ZERO and you can see I need an activityType too to call that function.

Sending these fields as metadata to the Scheduler helps you recreate callback functions like this when my app restarts.

Example Code to Handle App Restarts.

It’s important to reschedule jobs from the disk to your memory whenever your server restarts.

To do this,

  1. First Initialise the Scheduler using Scheduler.init and then

  2. pass your callback function to Scheduler.rescheduleJobs .

Here is an example

// after doing mongoose.connect();
const mongoClient = mongoose.connection.getClient();
const db = mongoClient.db();

await Scheduler.init({mongo: db}); 
// initialse scheduler
await Scheduler.rescheduleJobs(decideCallback) 
// pass the callback that has to be called for the jobs

decideCallback callback function will have context the job’s metadata that was passed while creating the job. Here is my callback Function’s definition for reference.

(I can even reuse the same function while scheduling the job)

async function decideCallback(job) {
  const { jobId, operation, activityType } = job;
  if (operation === "gradeZero" && activityType === "quiz") {
    await gradeZero(jobId);
    await createQuizReport(jobId);
  }
  if (operation === "gradeZero") {
    await gradeZero(jobId);
  }
  if (operation === "publishReport") {
    await publishReport(jobId);
  }
}
/* job has context to operation because, 
while scheduling I had passed operation
and activityType as jobMetadata. You can send any metadata and add
conditions like this in your callbacks
*/

Common Errors Thrown by the package

  • JobId must be unique - When you schedule a job with a jobId that's already present.

  • Scheduler not Initialised - When you try to use Scheduler Methods before Scheduler.init()

  • callback is not a function - When your callback function is not a function