{pluginName, queueName, jobName}.
Use Managed Queues for plugin work that must happen after the current request returns:
- outbound messages and notifications
- inbound event aggregation or debounce windows
- retryable third-party API calls
- delayed follow-up work
- long-running plugin tasks that should not block HTTP callbacks
BullModule.forRoot(), BullModule.registerQueue(), @Processor(), WorkerHost, or InjectQueue for new plugin jobs.
Why Plugins Should Not Own BullMQ
Plugin-owned BullMQ setups are easy in a single process, but fragile in production:- different tenants can install the same plugin and collide on queue names or Redis keys
- workers run without an HTTP request, so tenant and organization context can be lost
- every plugin creates its own Redis connection and retry behavior
- cancellation and delayed scheduling become plugin-specific
- multi-pod debugging has no single platform view
Enqueue Jobs
Resolve the queue service from the plugin context:result.jobId in your plugin business table when users may cancel, inspect, or retry the task later.
Cancel a waiting or delayed job:
Declare Job Processors
Use@PluginJobProcessor() instead of BullMQ @Processor():
handle().
One class can handle multiple logical job names:
Redis State And Locks
If the plugin needs rate-limit state, aggregation state, or distributed locks, use platform Redis fromManagedQueueService.getRedis():
pluginNametenantIdorganizationIdintegrationId- account id or external conversation id
Retry And Failure
A handler should throw when the job should be retried:attempts and backoffMs. The plugin should still update its own business records, such as message logs or integration health.
Concurrency
concurrency limits local calls to the same logical handler in the current API process. It does not guarantee cluster-wide serial execution.
For cross-pod ordering or rate limits, use Redis locks or business state. For example, an outbound messaging plugin should still lock by tenant and integration before sending.
Migrating Existing Plugins
When moving a plugin from self-managed BullMQ to Managed Queues:- remove BullMQ imports and module registration from the plugin package
- replace
queue.add()withManagedQueueService.enqueue() - replace direct job removal with
ManagedQueueService.cancel() - replace
@Processor()classes with@PluginJobProcessor()handlers - move Redis state and locks to platform Redis
- include tenant, organization, and integration scope in all keys
- ensure the handler can run without an HTTP request
- drain old physical queues before deployment
plugin_wechat BullMQ queues before switching production traffic to the Managed Queue version.