Workers
A worker is a handler registered for a job name. Monque calls it when a matching job is due and a concurrency slot is available.
Quick Reference
Section titled “Quick Reference”| Method / Type | Description |
|---|---|
register() | Register a job handler |
WorkerOptions | Worker concurrency, retries, payload validation, and replacement |
start() | Start processing jobs |
stop() | Graceful shutdown |
pause() / resume() | Pause or resume local execution |
now() | Enqueue for immediate execution |
MonqueOptions.workerConcurrency | Global concurrency default |
Registering a Worker
Section titled “Registering a Worker”Register a handler for a job name with register():
With Type Safety
Section titled “With Type Safety”Define the expected data type for better IDE support and compile-time checks:
With Payload Validation
Section titled “With Payload Validation”Pass a Standard Schema compatible schema to validate persisted input before your handler runs. For example, using Zod:
Monque accepts the standard interface from @standard-schema/spec; install your
preferred compatible validator separately. Both synchronous and asynchronous
schemas are supported. The handler’s payload type is inferred from the schema’s
output, including transformations.
Validation happens on every claimed attempt, including jobs enqueued by another producer. Enqueueing does not validate against a local worker’s schema. The stored payload and lifecycle events retain the original input; only the handler receives transformed output. Retries validate that original input again.
If the schema returns validation issues, the job fails once with
PayloadValidationError, without invoking the handler or automatically retrying.
The job:fail error includes the schema’s issues, including their paths. Unexpected
exceptions thrown by a validator follow the worker’s normal retry policy.
With Worker Options
Section titled “With Worker Options”Pass WorkerOptions to set concurrency or replace a handler:
Worker Options
Section titled “Worker Options”| Option | Type | Default | Description |
|---|---|---|---|
concurrency | number | 5 (from workerConcurrency) | Maximum concurrent jobs for this worker |
replace | boolean | false | Replace an existing worker with the same name instead of throwing |
schema | StandardSchemaV1 | None | Validate input and pass the schema output to the handler |
Workers also accept retry overrides.
Worker Lifecycle
Section titled “Worker Lifecycle”Pausing Local Processing
Section titled “Pausing Local Processing”Pause one worker or all execution in this scheduler instance:
Running executions continue, including their heartbeats. Enqueueing, stale recovery, and other scheduler instances remain active. A claim that finishes arriving after a pause is released without starting its handler. Resuming wakes local polling promptly; it does not restart the scheduler.
Pauses are local memory state and are lost when the process exits. You can pause
before initialization or worker registration, and repeated calls are safe. A stop/start
cycle on the same instance preserves pauses. Resuming the whole instance preserves
individually paused workers; resuming a worker does not override a whole-instance pause.
isPaused(name) reports the effective state, while isPaused() reports the instance pause.
getProcessingState(name?) also returns the scheduler’s instanceId and globallyPaused,
so management clients can distinguish a worker pause from an instance-wide pause.
The same method is available on Ts.ED’s MonqueService.
Use stop() when you need to drain and shut down. A paused, connected scheduler stays
healthy. To stop execution across a fleet, pause each consumer instance.
Replacing a Handler
Section titled “Replacing a Handler”Pass { replace: true } to register a new handler for an existing job name:
Running jobs finish with their original handler and remain visible in Queue Views. Subsequent polls count them against the replacement’s concurrency limit. A poll already in progress may finish acquiring work with the previous registration. Graceful shutdown also waits for jobs started by the previous handler.
1. Job Acquisition
Section titled “1. Job Acquisition”When start() is called, Monque begins looking for jobs:
- Change Streams (if available) provide near real-time notifications: triggering targeted, debounced polls for specific workers
- Safety polling runs every
safetyPollInterval(default 30 s) as a backup when Change Streams are active - If Change Streams are unavailable, regular polling runs every
pollInterval(default 1 s)
2. Atomic Claim
Section titled “2. Atomic Claim”Monque claims a due pending job before calling its handler. This prevents two scheduler instances from claiming the same pending job at the same time. See Atomic Claim Pattern for multi-instance deployment.
3. Execution
Section titled “3. Execution”Your handler function is called with the full job document:
4. Completion or Failure
Section titled “4. Completion or Failure”- Success: One-time jobs become
completed; recurring jobs return topendingfor their next cron time. - Error thrown: Job retried or set to
failed(see Retry & Backoff)
Error Handling
Section titled “Error Handling”Automatic Retry
Section titled “Automatic Retry”If your handler throws or returns a rejected promise, the job is automatically retried:
Permanent Failure
Section titled “Permanent Failure”Throw NonRetryableError when retrying cannot help, such as when a resource has been
permanently deleted. The job becomes failed immediately and emits job:fail with
willRetry: false. Ordinary errors, including other MonqueError instances, follow the
configured retry policy.
Concurrency Control
Section titled “Concurrency Control”Global Default
Section titled “Global Default”Set in MonqueOptions:
Per-Worker Override
Section titled “Per-Worker Override”Instance-level Concurrency
Section titled “Instance-level Concurrency”Use instanceConcurrency to limit the total number of jobs processed by a single scheduler instance across all workers:
When workers share an instanceConcurrency limit, polling resumes after the worker
that last acquired a job. A busy worker cannot keep taking every new slot ahead of
later-registered workers. Notifications for one job name also check other workers
waiting for shared capacity. Running jobs are not interrupted, and jobs within each
worker are still acquired in their scheduled order.
Concurrency Strategy
Section titled “Concurrency Strategy”| Scenario | Recommended Concurrency |
|---|---|
| CPU-bound tasks | 1-2 per core |
| I/O-bound tasks (database, API) | 5-20 |
| Memory-intensive tasks | Limited by available RAM |
| External API calls | Limit simultaneous requests; enforce time-based rate limits separately |
When an execution finishes, waiting jobs can use the freed local slot without
waiting for pollInterval, even when change streams are unavailable. Work written
by other producers is discovered through change streams or, when unavailable,
within pollInterval.
Multiple Workers
Section titled “Multiple Workers”Register multiple workers for different job types:
Worker Registration Timing
Section titled “Worker Registration Timing”Workers can be registered before or after start():
Best Practices
Section titled “Best Practices”1. Keep Handlers Focused
Section titled “1. Keep Handlers Focused”Keep unrelated tasks in separate handlers so they can be retried independently:
2. Handle Partial Failures
Section titled “2. Handle Partial Failures”Track progress so retries can resume unfinished work:
A crash can occur between processItem and updateProgress, and concurrent executions
can read the same progress. Each item operation must itself be idempotent, or its effect
and progress update must commit in one transaction. See
Idempotency patterns.
3. Log Strategically
Section titled “3. Log Strategically”Use job events for centralized logging:
Next Steps
Section titled “Next Steps”- Scheduling - Configure recurring jobs
- Retry & Backoff - Understand failure handling
- Heartbeat Mechanism - Monitor heartbeats and recovery
API Reference
Section titled “API Reference”Monque.register()- Register a worker handlerMonque.start()- Start the schedulerMonque.stop()- Graceful shutdownMonque.now()- Immediate job executionWorkerOptions- Worker configurationMonqueOptions- Scheduler optionsJob- Job document passed to handlersMonqueEventMap- Available events