-
Notifications
You must be signed in to change notification settings - Fork 511
Description
I need to ensure a job runs at least once after an action occurs, while avoiding unnecessary duplicate executions when multiple triggers happen in rapid succession.
Currently, BullMQ's deduplication modes have a critical gap:
- Simple mode: Deduplicates until job completes, but if a trigger occurs while the job is actively processing, that trigger is lost forever
- Throttle mode: Similar issue - triggers during job execution are ignored
- Debounce mode: Requires a delay and doesn't handle triggers during active processing
This is a common pattern needed for:
- Data synchronization: Multiple data changes should eventually sync, with at least one sync after the last change
- Cache invalidation: Multiple updates should trigger invalidation, ensuring cache is refreshed after the last update
- Webhook processing: Multiple webhook events for the same resource should be processed at least once after the last event
Related to #3151 (comment), which describes a similar need, but I think hasn't been completely fulfilled yet.
Describe the solution you'd like
A new deduplication mode that guarantees at least one job execution after the last trigger attempt:
await myQueue.add(
'process-action',
{ data },
{
deduplication: {
id: 'unique-job-id',
requeueIfActive: true // new option
}
}
);
Behavior:
- If job with same dedup ID is waiting/delayed → Deduplicate (ignore new job)
- If job is active (processing) → Mark for automatic requeue after completion
- If job is completed/failed → Allow new job immediately
- While marking a job as complete → Check if requeue was requested and automatically schedule a new job
This ensures:
✅ No unnecessary duplicate processing when job is already queued
✅ No lost triggers when job is actively processing
✅ Automatic handling without manual event listeners
✅ Works across multiple Node.js processes
Example use case
// Deployment pipeline triggered by git webhooks
async function onGitPush(commitData) {
await deployQueue.add(
'deploy',
{ commit: commitData.sha },
{
deduplication: {
id: `deploy-${commitData.repo}`,
requeueIfActive: true
}
}
);
}
// If multiple pushes happen:
// - Push 1 at 0ms: Job scheduled
// - Push 2 at 100ms (job waiting): Deduplicated
// - Push 3 at 5000ms (job active): Deduplicated but marked for requeue
// - Push 4 at 5100ms (job active): Deduplicated, requeue already marked
// - Job completes at 10000ms: New job automatically scheduled
// Result: Exactly 2 deployments run, last push is always deployed
Describe alternatives you've considered
- Manual tracking with deduplicated events: Requires complex state management in Redis, prone to race conditions
- Using job.updateData() during processing: Worker needs to check for updates, prone to race conditions too
- Version-based deduplication: Every trigger creates a job (no deduplication benefit)
- External pending queue: Adds complexity and potential for race conditions
None of these alternatives are as clean or reliable as having this built into BullMQ.
Additional context
I'm willing to contribute a PR for this feature if the maintainers agree with the approach. The implementation could:
- Use atomic Redis operations to track requeue requests
- Integrate with existing deduplication infrastructure
- Include comprehensive tests
- Maintain backward compatibility
- Add minimal performance overhead (only for jobs using this mode)
Would the team be open to this feature? If so, I'm happy to discuss implementation details and submit a PR.