Add database-backed job queue with WP-Cron and loopback workers - #16
Merged
Merged
Conversation
Adds a Laravel-style queue for plugins built on the framework, designed for
WordPress hosting where no `queue:work` daemon can run.
Job API
- `ShouldQueue` contract and `Queueable` trait; a job is a class with
`handle()`, whose class-typed parameters are resolved from the container.
- `Job::dispatch(...)` returns a pending dispatch written on destruction, with
`delay()`, `on_queue()` and `with_priority()`; plus `dispatch_if`,
`dispatch_unless` and `dispatch_sync`.
- Inside `handle()`: `attempts()`, `release($delay)` and `fail($e)`.
- `$tries`, `$backoff` (seconds or per-attempt array) and `failed(Throwable)`.
Storage and processing
- Jobs live in app-prefixed `{prefix}jobs` / `{prefix}failed_jobs` tables, so
several framework plugins on one site never share a queue.
- Workers claim batches atomically with a single tokened
`UPDATE ... ORDER BY priority DESC ... LIMIT n`, work within a time budget,
hand back unstarted jobs without using an attempt, and reclaim reservations
older than `retry_after` so a crashed worker cannot strand a job.
- An every-minute WP-Cron sweep and a shutdown spawn after undelayed
dispatches start an HMAC-signed, non-blocking admin-ajax loopback worker that
daisy-chains until the queue is empty. One chain runs at a time.
Opt-in and tooling
- Enabled only by registering `QueueServiceProvider`; without it nothing is
hooked and dispatch throws a helpful `QueueException`.
- `Queue` facade (`push`, `later`, `size`, `clear`) and `Queue::fake()` with
`assert_pushed`, `assert_pushed_times`, `assert_not_pushed`,
`assert_nothing_pushed`.
- `JobQueued`, `JobProcessing`, `JobProcessed`, `JobFailed` events; failures
are also logged.
- WP-CLI: `queue:table` (generates migrations, runs no DDL), `make:job`,
`queue:work`, `queue:failed`, `queue:retry`, `queue:forget`, `queue:flush`,
`queue:clear`.
- `docs/queues.md`, including the blocked-loopback / low-traffic fallback and
where this differs from Laravel; optional `config/queue.php` in the example.
Also records the grilling preference in CLAUDE.md.
New public API is tagged @SInCE 3.2.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Adds a Laravel-style queue for plugins built on the framework, designed for WordPress hosting where no
queue:workdaemon can run.Job API
ShouldQueuecontract andQueueabletrait; a job is a class withhandle(), whose class-typed parameters are resolved from the container.Job::dispatch(...)returns a pending dispatch written on destruction, withdelay(),on_queue()andwith_priority(); plusdispatch_if,dispatch_unlessanddispatch_sync.handle():attempts(),release($delay)andfail($e).$tries,$backoff(seconds or per-attempt array) andfailed(Throwable).Storage and processing
{prefix}jobs/{prefix}failed_jobstables, so several framework plugins on one site never share a queue.UPDATE ... ORDER BY priority DESC ... LIMIT n, work within a time budget, hand back unstarted jobs without using an attempt, and reclaim reservations older thanretry_afterso a crashed worker cannot strand a job.Opt-in and tooling
QueueServiceProvider; without it nothing is hooked and dispatch throws a helpfulQueueException.Queuefacade (push,later,size,clear) andQueue::fake()withassert_pushed,assert_pushed_times,assert_not_pushed,assert_nothing_pushed.JobQueued,JobProcessing,JobProcessed,JobFailedevents; failures are also logged.queue:table(generates migrations, runs no DDL),make:job,queue:work,queue:failed,queue:retry,queue:forget,queue:flush,queue:clear.docs/queues.md, including the blocked-loopback / low-traffic fallback and where this differs from Laravel; optionalconfig/queue.phpin the example.Also records the grilling preference in CLAUDE.md.
New public API is tagged @SInCE 3.2.0.