From 8f206fba633cd38a8cfb4cc3d56648735d789f99 Mon Sep 17 00:00:00 2001 From: Manuel Astudillo Date: Thu, 3 Oct 2024 21:41:38 +0000 Subject: [PATCH] GITBOOK-203: change request with no subject merged in GitBook --- docs/gitbook/SUMMARY.md | 6 +- .../jobs/job-scheduler/repeat-strategies.md | 62 -------- .../README.md | 10 +- .../job-schedulers/manage-job-schedulers.md | 10 ++ .../jobs/job-schedulers/repeat-options.md | 68 ++++++++ .../jobs/job-schedulers/repeat-strategies.md | 148 ++++++++++++++++++ docs/gitbook/guide/jobs/repeatable.md | 4 + 7 files changed, 239 insertions(+), 69 deletions(-) delete mode 100644 docs/gitbook/guide/jobs/job-scheduler/repeat-strategies.md rename docs/gitbook/guide/jobs/{job-scheduler => job-schedulers}/README.md (87%) create mode 100644 docs/gitbook/guide/jobs/job-schedulers/manage-job-schedulers.md create mode 100644 docs/gitbook/guide/jobs/job-schedulers/repeat-options.md create mode 100644 docs/gitbook/guide/jobs/job-schedulers/repeat-strategies.md diff --git a/docs/gitbook/SUMMARY.md b/docs/gitbook/SUMMARY.md index 1cd9c83039..b351bb38e9 100644 --- a/docs/gitbook/SUMMARY.md +++ b/docs/gitbook/SUMMARY.md @@ -33,8 +33,10 @@ * [Debouncing](guide/jobs/debouncing.md) * [Delayed](guide/jobs/delayed.md) * [Repeatable](guide/jobs/repeatable.md) - * [Job Scheduler](guide/jobs/job-scheduler/README.md) - * [Repeat Strategies](guide/jobs/job-scheduler/repeat-strategies.md) + * [Job Schedulers](guide/jobs/job-schedulers/README.md) + * [Repeat Strategies](guide/jobs/job-schedulers/repeat-strategies.md) + * [Repeat options](guide/jobs/job-schedulers/repeat-options.md) + * [Manage Job Schedulers](guide/jobs/job-schedulers/manage-job-schedulers.md) * [Prioritized](guide/jobs/prioritized.md) * [Removing jobs](guide/jobs/removing-job.md) * [Stalled](guide/jobs/stalled.md) diff --git a/docs/gitbook/guide/jobs/job-scheduler/repeat-strategies.md b/docs/gitbook/guide/jobs/job-scheduler/repeat-strategies.md deleted file mode 100644 index 868b78d030..0000000000 --- a/docs/gitbook/guide/jobs/job-scheduler/repeat-strategies.md +++ /dev/null @@ -1,62 +0,0 @@ -# Repeat Strategies - -By default, we are using [cron-parser](https://www.npmjs.com/package/cron-parser) as the default repeat strategy for cron expressions. - -It is possible to define a different strategy to schedule repeatable jobs. The idea is that the repeat strategy, based on a pattern and the latest job's milliseconds, return the next desired timestamp. Although not used in the following example, you could have different behaviours on your repeat strategies based on the current job's name if you want to. However not that **only** **one** repeatStrategy can be defined for a given queue. - -For example we can create a custom one for [RRULE](https://jkbrzt.github.io/rrule/) like this: - -```typescript -import { Queue, QueueScheduler, Worker } from 'bullmq'; -import { rrulestr } from 'rrule'; - -const settings = { - repeatStrategy: (millis: number, opts: RepeatOptions, _jobName: string) => { - const currentDate = - opts.startDate && new Date(opts.startDate) > new Date(millis) - ? new Date(opts.startDate) - : new Date(millis); - - const rrule = rrulestr(opts.pattern); - - if (rrule.origOptions.count && !rrule.origOptions.dtstart) { - throw new Error('DTSTART must be defined to use COUNT with rrule'); - } - - const next_occurrence = rrule.after(currentDate, false); - return next_occurrence?.getTime(); - }, -}; - -const myQueue = new Queue('Paint', { settings }); - -// Repeat job every 10 seconds -await myQueue.upsertJobScheduler( - 'collibris', - { - pattern: 'RRULE:FREQ=SECONDLY;INTERVAL=10;WKST=MO', - }, - { - data: { color: 'green' }, - }, -); - -// Repeat job every 20 seconds -await myQueue.upsertJobScheduler( - 'pingeons', - { - pattern: 'RRULE:FREQ=SECONDLY;INTERVAL=20;WKST=MO', - }, - { - data: { color: 'gray' } - }, -); - -const worker = new Worker( - 'Paint', - async () => { - doSomething(); - }, - { settings }, -); -``` diff --git a/docs/gitbook/guide/jobs/job-scheduler/README.md b/docs/gitbook/guide/jobs/job-schedulers/README.md similarity index 87% rename from docs/gitbook/guide/jobs/job-scheduler/README.md rename to docs/gitbook/guide/jobs/job-schedulers/README.md index 8788f986d0..5f61c3cf90 100644 --- a/docs/gitbook/guide/jobs/job-scheduler/README.md +++ b/docs/gitbook/guide/jobs/job-schedulers/README.md @@ -1,10 +1,10 @@ --- description: >- - Job Schedulers replace "repeatable jobs", and are available in v5.5.5 and - above. + Job Schedulers replace "repeatable jobs", and are available in v5.16.0 and + onwards --- -# Job Scheduler +# Job Schedulers A Job Scheduler acts as a factory , producing jobs based on specified "repeat" settings. The Job Scheduler is highly flexible, accommodating various scenarios, including jobs produced at fixed intervals, according to cron expressions, or based on custom requirements. For historical reasons, jobs produced by the Job Scheduler are often referred to as ‘Repeatable Jobs’. @@ -19,7 +19,7 @@ This example will create a new Job Scheduler that will produce a new job every s Now there are also a few important considerations that need to be explained here.: -* **Upsert vs. Add:** 'upsert' is used instead of 'add' to simplify management of recurring jobs, especially in production deployments. It ensures the scheduler is updated or created without duplications. +* **Upsert vs. Add:** the 'upsert' is used instead of 'add' to simplify management of recurring jobs, especially in production deployments. It ensures the scheduler is updated or created without duplications. * **Job Production Rate:** The scheduler will only generate new jobs when the last job begins processing. Therefore, if your queue is very busy, or if you do not have enough workers or concurrency, it is possible that you will get the jobs less frequently than the specified repetition interval. * **Job Status:** As long as a Job Scheduler is producing jobs, there will be always one job associated to the scheduler in the "Delayed" status. @@ -44,7 +44,7 @@ const firstJob = await queue.upsertJobScheduler( ``` -All jobs produced by this scheduler will use the given settings. Note that in the future you could call "upsertJobScheduler" again with the given "my-scheduler-id" in order to update any settings, both the repeat options or/and the job's template settings. +All jobs produced by this scheduler will use the given settings. Note that in the future you could call "upsertJobScheduler" again with the given "my-scheduler-id" in order to update any settings of this particular job scheduler, such as the repeat options or/and the job's template settings. {% hint style="info" %} Since jobs produced by the Job Scheduler will get a special job ID in order to guarantee that jobs will never be created more often than the given repeat settings, you cannot choose a custom job id. However you can use the job's name if you need to discriminate these jobs from other jobs. diff --git a/docs/gitbook/guide/jobs/job-schedulers/manage-job-schedulers.md b/docs/gitbook/guide/jobs/job-schedulers/manage-job-schedulers.md new file mode 100644 index 0000000000..52c2322250 --- /dev/null +++ b/docs/gitbook/guide/jobs/job-schedulers/manage-job-schedulers.md @@ -0,0 +1,10 @@ +# Manage Job Schedulers + +There are a couple of methods that are useful for managing job schedulers. We have already talked about "upsertJobScheduler" which allows us to both add and update existing job schedulers. However sometimes we also need to have the hability to remove or to get all existing schedulers. + +#### Remove job scheduler + +Get job schedulers + + + diff --git a/docs/gitbook/guide/jobs/job-schedulers/repeat-options.md b/docs/gitbook/guide/jobs/job-schedulers/repeat-options.md new file mode 100644 index 0000000000..95ed397069 --- /dev/null +++ b/docs/gitbook/guide/jobs/job-schedulers/repeat-options.md @@ -0,0 +1,68 @@ +# Repeat options + +There are some options that can be used on all Job Schedulers, to control some aspects of the repetitions. Lets review them one by one: + +#### Start date + +This option sets a future date from which the job will start being scheduled. This can be useful for setting up jobs that should begin repeating on a specific day. + +```typescript +const { Queue } = require('bullmq'); +const connection = { host: 'localhost', port: 6379 }; +const myQueue = new Queue('my-dated-jobs', { connection }); + +await myQueue.upsertJobScheduler('start-later-job', { + every: 60000, // every minute + startDate: new Date('2024-10-15T00:00:00Z') // start on October 15, 2024 +}, { + name: 'timed-start-job', + data: { message: 'Starting later' } +}); +``` + +#### End Date + +Use this to specify when the job should stop being scheduled, effectively setting an expiration date for the job repetitions. + +```typescript +await myQueue.upsertJobScheduler('end-soon-job', { + every: 60000, // every minute + endDate: new Date('2024-11-01T00:00:00Z') // end on November 1, 2024 +}, { + name: 'timed-end-job', + data: { message: 'Ending soon' } +}); +``` + +#### Limit + +This setting is used to limit the number of times a job will be repeated. When the count reaches this limit, no more jobs will be produced for the given job scheculer. + +```typescript +await myQueue.upsertJobScheduler('limited-job', { + every: 10000, // every 10 seconds + limit: 10 // limit to 10 executions +}, { + name: 'limited-execution-job', + data: { message: 'Limited runs' } +}); +``` + +#### immediately + +This setting forces the job to execute as soon as it is added, regardless of the schedule. This can help in situations where an immediate action is required before entering a regular cycle. + +When you use the every option in BullMQ, it schedules jobs based on fixed time intervals, which might seem a bit counterintuitive initially. For instance, if you set an interval of 2000ms, jobs will be triggered at every even second—such as 0, 2, 4, 6, 8 seconds, and so on. This means the scheduling aligns with the clock, regardless of when you actually added the job. + +If you need a job to begin processing immediately after you add a job scheduler, regardless of the interval’s alignment with the clock, you can use the immediately setting. This is especially crucial for long intervals. For example, if you set the job to repeat monthly, normally it would wait to start until the first second of the next month. If you add the job mid-month, it would not start until the beginning of the following month. Using immediately ensures the first instance of the job runs as soon as it’s added, bypassing the wait until the scheduled interval begins. + +```typescript +await myQueue.upsertJobScheduler('immediate-job', { + every: 86400000, // once a day + immediately: true // execute the first one immediately +}, { + name: 'instant-job', + data: { message: 'Immediate start' } +}); +``` + diff --git a/docs/gitbook/guide/jobs/job-schedulers/repeat-strategies.md b/docs/gitbook/guide/jobs/job-schedulers/repeat-strategies.md new file mode 100644 index 0000000000..9d324e890e --- /dev/null +++ b/docs/gitbook/guide/jobs/job-schedulers/repeat-strategies.md @@ -0,0 +1,148 @@ +# Repeat Strategies + +BullMQ comes with two predefined strategies for creating repeatable jobs. The ‘every’ strategy is straightforward, allowing you to schedule jobs to repeat at specific intervals, measured in seconds. The more complex ‘cron’ strategy uses cron expressions, as defined by the [cron-parser](https://www.npmjs.com/package/cron-parser) to schedule jobs in intricate patterns. Additionally, BullMQ lets you create custom strategies, giving you the flexibility to define your own logic for setting job intervals. + +### "Every" strategy + +The every strategy is used when we simply want to produce repeatable jobs at specific intervals: + +```typescript +const { Queue, Worker } = require('bullmq'); + +const connection = { + host: 'localhost', + port: 6379 +}; + +const myQueue = new Queue('my-repeatable-jobs', { connection }); + +// Upserting a repeatable job in the queue +await myQueue.upsertJobScheduler('repeat-every-10s', { + every: 10000 // Job will repeat every 10000 milliseconds (10 seconds) +}, { + name: 'every-job', + data: { jobData: 'data' }, + opts: {} // Optional additional job options +}); + +// Worker to process the jobs +const worker = new Worker('my-repeatable-jobs', async job => { + console.log(`Processing job ${job.id} with data: ${job.data.jobData}`); +}, { connection }); +``` + +### "Cron" strategy + +The “cron” strategy in BullMQ leverages the [cron-parser](https://www.npmjs.com/package/cron-parser) library to use cron expressions for scheduling jobs with greater specificity. This approach is ideal for jobs requiring execution at precise times or intervals, such as automated reports or maintenance tasks. + +Below is the supported format for cron expressions in cron-parser: + +``` +* * * * * * +┬ ┬ ┬ ┬ ┬ ┬ +│ │ │ │ │ │ +│ │ │ │ │ └ day of week (0 - 7, 1L - 7L, where 0 or 7 is Sunday) +│ │ │ │ └───── month (1 - 12) +│ │ │ └────────── day of month (1 - 31, L for the last day of the month) +│ │ └─────────────── hour (0 - 23) +│ └──────────────────── minute (0 - 59) +└───────────────────────── second (0 - 59, optional) +``` + +This format includes the optional second field, which is not typically available in standard cron schedules, allowing for even more precise scheduling. + +Cron expressions are quite powerful as in they support seemless handling timezone differences and daylight saving time transitions, crucial for tasks that depend on local times. And also because of the use of special characters to denote specific days or things like the last day of the month, providing flexibility for monthly and weekly tasks. + +If you are new to Cron expressions, [Wikipedia](https://en.wikipedia.org/wiki/Cron) is an excelent starting point to learn how to use them. + +Here follows an example that sets up a job to execute at 9:00 AM from Monday to Friday: + +```typescript +const { Queue, Worker } = require('bullmq'); + +const connection = { + host: 'localhost', + port: 6379 +}; + +const myQueue = new Queue('my-cron-jobs', { connection }); + +// Upserting a job with a cron expression +await myQueue.upsertJobScheduler('weekday-morning-job', { + cron: '0 0 9 * * 1-5' // Runs at 9:00 AM every Monday to Friday +}, { + name: 'cron-job', + data: { jobData: 'morning data' }, + opts: {} // Optional additional job options +}); + +// Worker to process the jobs +const worker = new Worker('my-cron-jobs', async job => { + console.log(`Processing job ${job.id} at ${new Date()} with data: ${job.data.jobData}`); +}, { connection }); +``` + +### Custom Strategy + +It is possible to define a different strategy to schedule repeatable jobs. The idea is that the repeat strategy, based on a pattern and the latest job's milliseconds, return the next desired timestamp. Although not used in the following example, you could have different behaviours on your repeat strategies based on the current job's name if you want to. However not that **only** **one** repeatStrategy can be defined for a given queue. + +For example we can create a custom one for [RRULE](https://jkbrzt.github.io/rrule/) like this: + +```typescript +import { Queue, Worker } from 'bullmq'; +import { rrulestr } from 'rrule'; + +const settings = { + repeatStrategy: (millis: number, opts: RepeatOptions, _jobName: string) => { + const currentDate = + opts.startDate && new Date(opts.startDate) > new Date(millis) + ? new Date(opts.startDate) + : new Date(millis); + + const rrule = rrulestr(opts.pattern); + + if (rrule.origOptions.count && !rrule.origOptions.dtstart) { + throw new Error('DTSTART must be defined to use COUNT with rrule'); + } + + const next_occurrence = rrule.after(currentDate, false); + return next_occurrence?.getTime(); + }, +}; + +const myQueue = new Queue('Paint', { settings }); + +// Repeat job every 10 seconds +await myQueue.upsertJobScheduler( + 'collibris', + { + pattern: 'RRULE:FREQ=SECONDLY;INTERVAL=10;WKST=MO', + }, + { + data: { color: 'green' }, + }, +); + +// Repeat job every 20 seconds +await myQueue.upsertJobScheduler( + 'pingeons', + { + pattern: 'RRULE:FREQ=SECONDLY;INTERVAL=20;WKST=MO', + }, + { + data: { color: 'gray' } + }, +); + +const worker = new Worker( + 'Paint', + async () => { + doSomething(); + }, + { settings }, +); +``` + +{% hint style="danger" %} +As you may have noticed, the repeat strategy setting should be provided in **both** the Queue and Worker classes. The reason we need it in both places is that when we first add the job to the Queue, we need to calculate when the next iteration will occur. After that, the Worker takes over, and we use the settings configured in the Worker. +{% endhint %} diff --git a/docs/gitbook/guide/jobs/repeatable.md b/docs/gitbook/guide/jobs/repeatable.md index 8c0c3af497..e73ec60762 100644 --- a/docs/gitbook/guide/jobs/repeatable.md +++ b/docs/gitbook/guide/jobs/repeatable.md @@ -1,5 +1,9 @@ # Repeatable +{% hint style="danger" %} +Note: from BullMQ version 5.16.0 and onwards, we have deprecated these APIs in favor of ["Job Schedulers"](job-schedulers/), which provide a more cohesive and more robust API for handling repeatable jobs. +{% endhint %} + There is a special type of _meta_ job called **repeatable**. These jobs are special in the sense that even though you only add one job to the queue, they will keep repeating according to a predefined schedule. Adding a job with the `repeat` option set will actually do two things immediately: create a Repeatable Job configuration, and schedule a regular delayed job for the job's first run. This first run will be scheduled "on the hour", that is if you create a job that repeats every 15 minutes at 4:07, the job will first run at 4:15, then 4:30, and so on.