| name | superpowers-sage:acorn-queues |
| description | Background job processing in WordPress via Acorn: Laravel queue, dispatch, queue:work, ShouldQueue, failed jobs, Action Scheduler, as_schedule_single_action, as_schedule_recurring_action, job retries, backoff, job chaining, job batching, ShouldBeUnique, Acorn queue, Redis queue driver, database queue driver, queue worker, background tasks — using Acorn's Laravel queue stack in Sage/WordPress
|
| user-invocable | false |
Queues, Jobs, and Background Tasks
When to Use What
| Criteria | Action Scheduler | Laravel Queue + Job |
|---|
| Best for | Simple recurring tasks, WP-native workflows | Robust async, retry logic, heavy computation |
| Infrastructure | None — runs on WP cron | Needs queue driver (database or Redis) |
| Retry/backoff | Manual | Built-in ($tries, $backoff, exponential) |
| Monitoring | WP Admin > Tools > Scheduled Actions | lando acorn queue:failed, logs |
| Examples | Daily cleanups, content sync, email digests | Image processing, API syncs, bulk imports |
| Already available | Bundled with WooCommerce; standalone via woocommerce/action-scheduler | Requires Acorn queue config + worker |
Rule of thumb: Start with Action Scheduler for simple recurring WordPress tasks. Move to Laravel Queue + Job when you need retries, backoff, chaining, batching, or processing that could take more than a few seconds.
Quick Start — Laravel Queue
bash skills/acorn-queues/scripts/create-job.sh ProcessImage
lando acorn queue:table
lando acorn queue:failed-table
lando acorn migrate
bash skills/acorn-queues/scripts/run-worker.sh
bash skills/acorn-queues/scripts/run-worker.sh emails
Quick Start — Action Scheduler
lando composer require woocommerce/action-scheduler
as_schedule_single_action(strtotime('+10 minutes'), 'app/sync_content', ['post_id' => 42], 'content-sync');
if (! as_has_scheduled_action('app/cleanup_tokens', [], 'maintenance')) {
as_schedule_recurring_action(time(), HOUR_IN_SECONDS, 'app/cleanup_tokens', [], 'maintenance');
}
Register callbacks in ThemeServiceProvider::boot():
add_action('app/sync_content', function (int $postId): void {
$this->app->make(\App\Services\ContentSyncService::class)->syncPost($postId);
});
Job Class Anatomy
class ProcessImage implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public array $backoff = [10, 60, 300];
public function __construct(protected readonly int $attachmentId) {}
public function handle(): void
{
$file = get_attached_file($this->attachmentId);
if (! $file || ! file_exists($file)) {
return;
}
}
public function failed(?\Throwable $exception): void
{
Log::error('ProcessImage permanently failed', ['attachment_id' => $this->attachmentId]);
}
}
Dispatching Jobs
ProcessImage::dispatch(attachmentId: $id);
SyncContent::dispatch(sourceId: $id)->delay(now()->addMinutes(5));
SendEmail::dispatch(campaignId: $id)->onQueue('emails');
add_action('add_attachment', function (int $id): void {
if (wp_attachment_is_image($id)) {
ProcessImage::dispatch(attachmentId: $id);
}
});
Scripts
bash skills/acorn-queues/scripts/create-job.sh <JobName>
bash skills/acorn-queues/scripts/run-worker.sh
bash skills/acorn-queues/scripts/run-worker.sh emails
Script source: scripts/create-job.sh · scripts/run-worker.sh
Assets
Boilerplate templates with {{CLASS_NAME}} placeholder. Copy and replace.
- job-simple.php.tpl — Simple job with
handle() + failed(), $tries, $backoff, wp_set_current_user stub.
- job-batched.php.tpl — Batched job with
use Batchable, $this->batch()->cancelled() check.
References
Deep content loaded on demand — zero tokens until needed.
- action-scheduler.md — Action Scheduler setup,
as_schedule_single_action, as_schedule_recurring_action, callbacks, duplicate-schedule prevention, cron-based queue trigger.
- laravel-queue.md — Full
config/queue.php, all drivers, database setup, dispatching patterns, job chaining, batching, testing with Bus::fake().
- redis-driver.md — Redis-backed queue, Lando Redis service config (
REDIS_HOST=cache), queue isolation, failover to database driver, Supervisor config.
- job-patterns.md — Idempotency patterns, chunking large datasets, retry/backoff config,
ShouldBeUnique + uniqueId(), wp_set_current_user in jobs, pass IDs not objects.
- troubleshooting.md — Failed jobs table,
queue:work debug flags, common errors (jobs stuck, Redis refused, class not found, memory exhaustion), escalation paths.
Verification
- Dispatch a test job and confirm it appears in the queue: check the
jobs table (database driver) or lando redis-cli -h cache LLEN queues:default (Redis driver).
- Run
lando acorn queue:work --once and confirm the job processes successfully with expected log output.
- After processing, verify the job is removed from the queue and does not appear in
lando acorn queue:failed.
Failure modes
Problem: Jobs dispatched but never processed
- Cause: No worker is running, or
QUEUE_CONNECTION in .env does not match a connection in config/queue.php.
- Fix: Start a worker (
bash skills/acorn-queues/scripts/run-worker.sh). Verify QUEUE_CONNECTION matches a valid connection key. For the database driver, run lando acorn queue:table && lando acorn migrate.
Problem: Jobs fail and the failed_jobs table does not exist
- Cause: The failed jobs migration was never run.
- Fix:
lando acorn queue:failed-table && lando acorn migrate. Then retry with lando acorn queue:retry all.
For all other failure modes see references/troubleshooting.md.
Critical Rules
- Action Scheduler vs Laravel Queue: Use Action Scheduler for simple recurring WP-native tasks. Use Laravel Queue + Job when you need retries, backoff, chaining, batching, or heavy computation.
wp_set_current_user in jobs: If a job calls current_user_can() or any WP function that relies on the current user, call wp_set_current_user($userId) at the top of handle(). Jobs run outside the HTTP lifecycle — WP does not set a current user automatically.
- Idempotency: Every job must be safe to run more than once. Use guard clauses or upsert patterns. See
references/job-patterns.md.
- Pass IDs, not objects: Pass
$postId instead of $post. Re-fetch in handle() to avoid serialization issues and stale data on retry.
- Set
$tries and $backoff: Never let jobs retry infinitely. Use exponential backoff arrays for external API jobs.
- Failed jobs table must exist: Run
lando acorn queue:failed-table && lando acorn migrate before running workers in production.
sync driver is for development only. It blocks the HTTP request and runs jobs inline. Set QUEUE_CONNECTION=database or redis in production.
- Redis host inside Lando is the service name (
cache), not 127.0.0.1. See references/redis-driver.md.