Jobs
A job stores the work you want a worker to perform, along with its payload, status, and scheduled time.
Quick Reference
Section titled “Quick Reference”| Method / Type | Description |
|---|---|
Job | Job document interface |
enqueue() | Create a job with optional scheduling |
enqueueMany() | Submit a batch with per-job scheduling and deduplication |
now() | Create a job for immediate execution |
schedule() | Create a recurring cron-based job |
getJob() | Retrieve a single job by ID |
getJobs() | Query jobs with filters |
EnqueueOptions | Options for enqueue() |
JobStatus | Status values object |
What is a Job?
Section titled “What is a Job?”Each job has:
- Name: Identifies which worker should process it
- Data: The payload containing information needed to complete the work
- Status: Current state in the lifecycle (
pending,processing,completed,failed). Can be referenced via theJobStatusObject from@monque/core. - Scheduling: When the job should run (
nextRunAt) - Priority: Which due Job is claimed next within its Job Name (
priority, default0)
Job Interface
Section titled “Job Interface”For the full interface definition, see the Job API Reference.
Job Lifecycle
Section titled “Job Lifecycle”stateDiagram-v2
[*] --> pending: enqueue()
pending --> processing: worker claims
pending --> cancelled: cancelJob()
processing --> completed: success
processing --> pending: failure (retries left)
processing --> failed: max retries exceeded
completed --> pending: recurring job reschedules
cancelled --> pending: retryJob()
Status Definitions
Section titled “Status Definitions”| Status | Description |
|---|---|
pending | Job is waiting to be picked up by a worker |
processing | A worker has claimed and is executing the job |
completed | Job finished successfully |
failed | Job permanently failed after exhausting all retries |
cancelled | Job was manually cancelled via cancelJob() |
Creating Jobs
Section titled “Creating Jobs”Immediate Execution
Section titled “Immediate Execution”Use now() to create a Job that is due immediately.
It accepts optional priority and session options:
Enqueue a Batch
Section titled “Enqueue a Batch”Use enqueueMany() when submitting several jobs together:
Each item accepts name, data, priority, runAt, and uniqueKey. An empty batch returns
zero counts. Priorities, names, unique keys, and configured payload size limits are validated
for every item before any writes. Standard Schema validation still runs when a
worker claims each job.
Deduplication uses the same rules as enqueue(): an existing pending or processing
job with the same name and unique key keeps its priority, payload, and schedule. Writes are
unordered; when new items share a key, do not rely on which payload or priority wins. Completed,
failed, and cancelled jobs do not block a new job with that key.
The result contains counts, not job documents. Without a transaction, a database
error can leave some jobs persisted while other jobs fail. ConnectionError.cause
contains the MongoDB error and any available partial results. Use stable unique keys when retrying, and remember
that deduplication lasts only while a job is pending or processing.
Enqueue in a Transaction
Section titled “Enqueue in a Transaction”Pass a MongoDB ClientSession to enqueue(), now(), enqueueMany(), or schedule() to
commit business data and jobs together. Use the same MongoClient that created
Monque’s database, and initialize Monque before starting the transaction.
Workers see the jobs only after commit. Aborting rolls back the jobs along with
the business writes. Monque never starts, commits, aborts, or ends your session.
Passing a session alone does not start a transaction; use withTransaction() or
the driver’s explicit transaction methods. MongoDB transactions require a replica
set or sharded cluster.
Await operations sequentially inside the transaction. Native database errors are
propagated unchanged during a transaction, preserving the driver’s retry labels.
Let withTransaction() retry eligible failures, and keep external side effects
outside its callback. Do not catch a failed write and continue using an aborted
transaction. A returned job or batch count is provisional until commit succeeds.
Change streams wake workers after commit. When streams are unavailable, committed
jobs are discovered within the configured pollInterval.
Scheduled Execution
Section titled “Scheduled Execution”Use enqueue() with runAt for delayed execution:
Recurring Jobs
Section titled “Recurring Jobs”Use schedule() for cron-based recurring jobs:
Recurring schedules accept the same signed safe-integer priority as single and batch
Jobs. The default is 0. Automatic and manual retries, rescheduling, stale recovery,
and later recurring runs retain the Job’s priority. The saved timezone and cron schedule
determine when it becomes due. Returned Jobs, lifecycle events, Management responses,
and the Dashboard all show priority.
Preventing Duplicates
Section titled “Preventing Duplicates”Use uniqueKey to prevent duplicate jobs:
Querying Jobs
Section titled “Querying Jobs”Get Jobs by Filter
Section titled “Get Jobs by Filter”Get Single Job by ID
Section titled “Get Single Job by ID”You can also pass the hexadecimal ID string directly. Use metadata-only pagination for large job lists that do not need payloads.
Job Data Best Practices
Section titled “Job Data Best Practices”Keep Payloads Small
Section titled “Keep Payloads Small”Store identifiers in the payload and load larger documents in the handler:
Payload Size Validation
Section titled “Payload Size Validation”Use maxPayloadSize to enforce a hard BSON byte limit on all job payloads. When set, enqueue(), now(), and schedule() measure the payload using BSON.calculateObjectSize() before insertion and throw PayloadTooLargeError if the limit is exceeded.
PayloadTooLargeError exposes two properties:
actualSize: measured BSON byte size of the payloadmaxSize: the configured limit
When maxPayloadSize is not set (the default), no validation is performed.
Use Type-Safe Payloads
Section titled “Use Type-Safe Payloads”Define interfaces for your job data:
Idempotent Operations
Section titled “Idempotent Operations”Design workers to handle concurrent duplicates and retries after partial failure. Atomic claims prevent two workers from claiming the same pending Job at once. Recovery can expire a claim while its original handler still runs; claim ownership protects Monque’s state, but cannot cancel an external side effect.
For external calls, reuse a stable business-operation key with a provider that supports idempotency. This application adapter must forward the key and handle the provider’s response to concurrent requests with that key:
Keep the request details fixed and respect the provider’s key-retention period. Checking a local payment status before making a call cannot prevent concurrent charges or recover a lost response safely. For MongoDB-only effects, use a transaction with a unique operation identifier. See Idempotency patterns for both approaches and their prerequisites.
Job Retention
Section titled “Job Retention”Set retention periods to delete old completed, failed, and cancelled jobs automatically:
Retention uses updatedAt, so cancelled jobs age from their cancellation time, not their
creation time. A period of 0 makes that status eligible on the next cleanup pass.
Cleanup runs when the scheduler starts and at interval; you do not need a cron job.
Pending and processing jobs are never removed by retention.
Indexes
Section titled “Indexes”During initialize(), Monque creates indexes for job discovery, deduplication, ownership,
recovery, and pagination.
Listing indexes include:
{ createdAt: -1, _id: -1 }{ name: 1, createdAt: -1, _id: -1 }{ status: 1, createdAt: -1, _id: -1 }{ updatedAt: -1, _id: -1 }{ nextRunAt: -1, _id: -1 }
The _id field breaks ties for stable ordering. Name and status indexes support filtered
creation-time listings. A cleanup index is also created when jobRetention is configured.
Enabling jobRetention.cancelled adds a partial updatedAt index for cancelled jobs;
existing retention indexes remain valid when upgrading.
Without skipIndexCreation: true, initialization requires index permissions even if indexes
already exist. Priority claims and deadline discovery use separate indexes. Keep both when
managing indexes yourself; see the
production index guidance.
Deleting Jobs
Section titled “Deleting Jobs”You can use the deletion APIs for manual cleanup:
deleteJob(jobId): Permanently delete a single job.deleteJobs(filter): Delete multiple jobs matching criteria (name, status, age).
Advanced Custom Cleanup
Section titled “Advanced Custom Cleanup”If you need more complex retention logic (e.g., keep important sales reports longer), implement a custom scheduled job that uses the MongoDB driver directly:
Next Steps
Section titled “Next Steps”- Workers - Learn how to process jobs
- Scheduling - Use cron expressions
- Retry & Backoff - Configure retry behavior
API Reference
Section titled “API Reference”Job- Full job document interfacePersistedJob- Type returned from enqueue operationsMonque.enqueue()- Enqueue a jobMonque.now()- Immediate execution sugarMonque.schedule()- Recurring job schedulingMonque.getJob()- Get job by IDMonque.getJobs()- Query jobsEnqueueOptions- Enqueue optionsGetJobsFilter- Job query filterJobStatus- Status constants