Skip to main content

Priority

Some work should be done sooner than others, so the priority of each backgrounded service is customizable to one of the following: urgent, default, not_urgent, last. If not overridden, the priority is default.

class IntercomSync extends ApplicationBackgroundedService {
public static get backgroundJobConfig(): BackgroundJobConfig<ApplicationBackgroundedService> {
return { priority: 'not_urgent' }
}
}

Each level maps to a BullMQ numeric priority — lower numbers run first:

type BackgroundQueuePriority = 'urgent' | 'default' | 'not_urgent' | 'last'
// Maps to BullMQ numeric priority: 1 (urgent), 2 (default), 3 (not_urgent), 4 (last)

Priority and named workstreams compose

When a backgroundJobConfig sets a workstream (or a groupId), Psychic still writes the priority number to BullMQ's top-level priority — the only priority open-source BullMQ reads — so a service gets workstream isolation and priority ordering together. There is nothing to trade off, and nothing to license.

BullMQ Pro adds a group.priority alongside that top-level priority. It answers a different question: which group runs next among the queue's other groups, rather than which job runs next within one. Running Pro means passing QueuePro and WorkerPro as the Queue and Worker providers in the workers initializer.

Bulk work belongs under not_urgent/last

Keep any large-volume, lower-importance run off default priority, so it only fills otherwise-idle worker slots rather than competing with routine application work. The large-record-set fan-out pattern builds on this, using the gap between not_urgent and last to bound how much bulk work is queued at once.

One use case for last is sending check-in events to a service such as https://deadmanssnitch.com so that you get notified when your background services stop working (uses scheduled jobs to schedule calling the check-in every 5 minutes). By working these jobs off last, you can be confident that all of your other jobs are being worked off.

// app/services/ScheduledJobs.ts
export default class ScheduledJobs extends ApplicationScheduledService {
public static async scheduleAllJobs() {
await this.schedule('*/5 * * * *', 'backgroundCheckin')
}

public static async backgroundCheckin() {
await BackgroundCheckin.checkIn()
}
}
// app/services/BackgroundCheckin.ts
export default class BackgroundCheckin extends ApplicationBackgroundedService {
public static get backgroundJobConfig(): BackgroundJobConfig<ApplicationBackgroundedService> {
return { priority: 'last' }
}

public static checkIn = async () => {
await this.background('_checkIn')
}

public static _checkIn = async () => {
await DeadMansSnitch.checkin('background_jobs_working')
}
}