feat: adds new jobs.shouldAutoRun property (#11092)

Adds a `shouldAutoRun` property to the `jobs` config to be able to have
fine-grained control over if jobs should be run. This is helpful in
cases where you may have many horizontally scaled compute instances, and
only one instance should be responsible for running jobs.
This commit is contained in:
James Mikrut
2025-02-10 16:43:20 -05:00
committed by GitHub
parent d2fe9b0807
commit 5dadccea39
4 changed files with 53 additions and 21 deletions

View File

@@ -29,7 +29,7 @@ As mentioned above, you can queue jobs, but the jobs won't run unless a worker p
#### Cron jobs
You can use the jobs.autoRun property to configure cron jobs:
You can use the `jobs.autoRun` property to configure cron jobs:
```ts
export default buildConfig({
@@ -47,6 +47,12 @@ export default buildConfig({
},
// add as many cron jobs as you want
],
shouldAutoRun: async (payload) => {
// Tell Payload if it should run jobs or not.
// This function will be invoked each time Payload goes to pick up and run jobs.
// If this function ever returns false, the cron schedule will be stopped.
return true
}
},
})
```

View File

@@ -105,8 +105,9 @@ export const payloadCloudPlugin =
const DEFAULT_CRON_JOB = {
cron: DEFAULT_CRON,
limit: DEFAULT_LIMIT,
queue: 'default (every minute)',
queue: 'default',
}
config.globals = [
...(config.globals || []),
{
@@ -130,9 +131,33 @@ export const payloadCloudPlugin =
const oldAutoRunCopy = config.jobs.autoRun ?? []
const hasExistingAutorun = Boolean(config.jobs.autoRun)
const newShouldAutoRun = async (payload: Payload) => {
if (process.env.PAYLOAD_CLOUD_JOBS_INSTANCE) {
const retrievedGlobal = await payload.findGlobal({
slug: 'payload-cloud-instance',
})
if (retrievedGlobal.instance === process.env.PAYLOAD_CLOUD_JOBS_INSTANCE) {
return true
} else {
process.env.PAYLOAD_CLOUD_JOBS_INSTANCE = ''
}
}
return false
}
if (!config.jobs.shouldAutoRun) {
config.jobs.shouldAutoRun = newShouldAutoRun
}
const newAutoRun = async (payload: Payload) => {
const instance = generateRandomString()
process.env.PAYLOAD_CLOUD_JOBS_INSTANCE = instance
await payload.updateGlobal({
slug: 'payload-cloud-instance',
data: {
@@ -140,16 +165,7 @@ export const payloadCloudPlugin =
},
})
await waitRandomTime()
const cloudInstance = await payload.findGlobal({
slug: 'payload-cloud-instance',
})
if (cloudInstance.instance !== instance) {
return []
}
if (!config.jobs?.autoRun) {
if (!hasExistingAutorun) {
return [DEFAULT_CRON_JOB]
}
@@ -160,11 +176,3 @@ export const payloadCloudPlugin =
return config
}
function waitRandomTime(): Promise<void> {
const min = 1000 // 1 second in milliseconds
const max = 5000 // 5 seconds in milliseconds
const randomTime = Math.floor(Math.random() * (max - min + 1)) + min
return new Promise((resolve) => setTimeout(resolve, randomTime))
}

View File

@@ -729,9 +729,20 @@ export class BasePayload {
typeof this.config.jobs.autoRun === 'function'
? await this.config.jobs.autoRun(this)
: this.config.jobs.autoRun
await Promise.all(
cronJobs.map((cronConfig) => {
new Cron(cronConfig.cron ?? DEFAULT_CRON, async () => {
const job = new Cron(cronConfig.cron ?? DEFAULT_CRON, async () => {
if (typeof this.config.jobs.shouldAutoRun === 'function') {
const shouldAutoRun = await this.config.jobs.shouldAutoRun(this)
if (!shouldAutoRun) {
job.stop()
return false
}
}
await this.jobs.run({
limit: cronConfig.limit ?? DEFAULT_LIMIT,
queue: cronConfig.queue,

View File

@@ -76,6 +76,13 @@ export type JobsConfig = {
* a new collection.
*/
jobsCollectionOverrides?: (args: { defaultJobsCollection: CollectionConfig }) => CollectionConfig
/**
* A function that will be executed before Payload picks up jobs which are configured by the `jobs.autorun` function.
* If this function returns true, jobs will be queried and picked up. If it returns false, jobs will not be run.
* @param payload
* @returns boolean
*/
shouldAutoRun?: (payload: Payload) => boolean | Promise<boolean>
/**
* Define all possible tasks here
*/