[2.x] feat: pausable queues#4841
Merged
Merged
Conversation
Illuminate's Worker consults the queue manager's pause surface when
popping jobs, but Flarum's QueueFactory stubbed the whole family as
no-ops. The stubs are now real, backed by the shared cache store using
Illuminate's own key format (illuminate:queue:paused:{connection}:{queue}),
so the signal reaches every worker process — across containers, and
including Horizon workers, which resolve this same factory.
- Per-queue pause plus a wildcard (*) that covers every queue on the
connection, so pause-all works without core having to enumerate queue
names. The layers are orthogonal: resuming all does not clear
individually paused queues.
- pausedQueues() tracks what is paused per connection for the dashboard,
self-healing when a pauseFor() TTL expires or a pause is cleared
through the raw cache key.
- queue:pause / queue:resume commands (Flarum-native: the stock
Illuminate ones type-hint the concrete QueueManager). The queue
argument accepts queue or connection:queue and defaults to the active
connection; --all pauses or resumes everything. Registered only for
non-sync connections, like the other queue commands.
- POST /api/queue/pause admin endpoint; refuses (409) on the sync
driver, where jobs execute inline and there is nothing to pause.
- flarum.queue.queues container binding: the queue names known to the
installation (default: ['default']). Extensions routing jobs onto
named queues can append theirs to get per-queue admin controls.
- Admin UI: a warning on the dashboard whenever any queue is paused,
and pause switches on the Advanced page — a single switch when only
one queue is known, per-queue switches plus pause-all otherwise.
Sync driver shows no pause controls.
Note for operators: long-running workers evaluate the pause state with
the code they booted with, so a one-time queue:restart is needed after
upgrading to pick up the new factory.
imorland
force-pushed
the
im/pausable-queues
branch
from
July 26, 2026 22:17
15e158a to
ba7d2d0
Compare
imorland
marked this pull request as ready for review
July 26, 2026 22:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the queue pause/resume surface that
QueueFactoryhas stubbed since #4673/#4683, removing the TODO. Works across all queue backends — database, redis, and Horizon workers all consult this same factory when popping jobs.How it works
Illuminate's
Workerfilters paused queues ingetNextJob()via the manager's pause surface.QueueFactorynow answers for real, backed by the shared cache store with Illuminate's exact key format (illuminate:queue:paused:{connection}:{queue}) — so the signal reaches every worker process, across containers, and interoperates with anything speaking the upstream convention.QueuePaused/QueueResumedevents are dispatched for parity.pause($conn, 'media')orpause($conn, '*')— the wildcard covers every queue on the connection, so pause-all needs no queue enumeration (core can't portably enumerate queue names across backends). The layers are orthogonal: resume-all doesn't clear individually paused queues.pausedQueues($connection)— a Flarum addition tracking what's paused, self-healing whenpauseFor()TTLs expire or pauses are cleared externally. Powers the dashboard warning.queue:pause/queue:resume— Flarum-native commands (the stock Illuminate ones type-hint the concreteQueueManager). Acceptqueueorconnection:queue, default to the active connection, support--all. Registered only for non-sync connections.POST /api/queue/pause— admin-gated; refuses with 409 on the sync driver (inline execution, nothing to pause).flarum.queue.queuescontainer binding — queue names known to the installation (default['default']); extensions routing jobs viaAbstractJob::$onQueueappend theirs to get per-queue admin controls.Admin UI
Validation
queue:resumeit drained within seconds — commands issued from the web container, workers running in a separate container.Operator note
Long-running workers evaluate pause state with the code they booted with — a one-time
queue:restartafter upgrading picks up the new factory.Follow-ups (fof/horizon)
Register supervisor queue names into
flarum.queue.queues; supervisor-profile extender with grouped pause controls; cross-containerhorizon:pause/continue.