agentleFS
AnyOrgNameAnyOrgName/php-skills/skills/laravel/queues/SKILL.md

queues

Skill4 starsChanged 4 months agoMITView on GitHub

Run work asynchronously with Laravel queues — dispatch jobs, choose a queue driver (sync, database, Redis, SQS, Beanstalkd), run workers or Horizon, and tune retries, backoff, timeouts, failed jobs, batching, and rate limiting. Use when the user needs to choose or configure a queue driver such as sync, database, Redis, SQS, or Beanstalkd; the user wants to move slow work (email, image processing, API calls) out of the web request into a background job; the user is configuring job retries, backoff, timeouts, or handling failed jobs and workers that keep reprocessing. Keywords: queue, queues, jobs, workers, horizon, redis, sqs, performance, retries, failed-jobs, input, laravel.

5 other projects use a version of this.

Adopted by

Most recently taken up by

The file

Show its rules and text

We split this skill into the rules it gives an agent

14 rules. 0 are common, 6 are distinctive, and 8 are very specific to this project.

  • 0

    Common

    Most of the 5 edited versions we read keep these.

  • 6

    Distinctive

    This author's own choices. Worth a look.

  • 8

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Do not use queues to run periodic time-based tasks.

    line 82
  2. Distinctive:

    Do not queue work whose result the caller needs within the same request.

    line 83
  3. Very specific:

    Run php artisan make:job SendInvoiceEmail.

    line 132
  4. Very specific:

    Verify: The generated class implements ShouldQueue and has a handle() method.

    line 134
  5. Very specific:

    Verify: handle() receives its dependencies and $tries is a finite number.

    line 137
  6. Distinctive:

    Verify: The request returns immediately and the worker log shows the job processed once.

    line 140
  7. Very specific:

    Verify: config('queue.default') is redis and retry_after exceeds the max job runtime.

    line 156
  8. Very specific:

    Run php artisan queue:work redis --timeout=<below retry_after> (or install Horizon) and manage the process with Supervisor or systemd for …

    line 157
  9. Distinctive:

    Verify: The worker consumes the redis queue and restarts automatically after it exits.

    line 159
  10. Very specific:

    Add php artisan queue:restart to the deploy pipeline and configure a failed_jobs store plus alerting so exhausted jobs are surfaced.

    line 160
  11. Distinctive:

    Verify: A deploy reloads worker code and a forced failure records to failed_jobs and alerts.

    line 162
  12. Very specific:

    When many jobs form one logical unit and you need a completion or failure callback, dispatch them with …

    line 166
  13. Very specific:

    When a job calls a rate-limited API, apply middleware such as RateLimited or WithoutOverlapping instead of sleeping inside the job

    line 168
  14. Distinctive:

    When the same job must not run twice concurrently (e.g

    line 170

“Common” is measured: kept by at least 6 in 10 of the 5 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md
<!-- generated: do not edit — source: knowledge/atoms/laravel/queues/ --><!-- schema: atomVersion=3 skillVersion=2 graphVersion=1 --><!-- compile: @php-skills/compiler v1.0.0 (deterministic build) -->

Laravel Queues

When to use

  • The user needs to choose or configure a queue driver such as sync, database, Redis, SQS, or Beanstalkd.
  • The user wants to move slow work (email, image processing, API calls) out of the web request into a background job.
  • The user is configuring job retries, backoff, timeouts, or handling failed jobs and workers that keep reprocessing.

When NOT to use

  • Do not use queues to run periodic time-based tasks; use the task scheduler (cron) to dispatch them on a schedule.
  • Do not queue work whose result the caller needs within the same request; run it synchronously instead.

Core guidance

  • Use backoff and capped retries for flaky external calls — For jobs hitting external APIs, set a $backoff (often an array for exponential delay) and a finite $tries or retryUntil() rather than unlimited retries.
  • Prefer Redis + Horizon for high-throughput, low-latency queues — When throughput is high or jobs must start within seconds, use the Redis driver with Horizon for near-instant dispatch, atomic pops, and per-queue autoscaling.
  • Keep worker timeout below the connection retry_after — Set the worker --timeout lower than the queue connection's retry_after so a job is killed before the queue makes it available again.

Decision Engine

Walk this section before choosing an implementation path.

Do you need real async processing, and if so is the platform managed AWS or
self-hosted with Redis available?

├── No Redis or AWS, low volume: → database driver — zero extra infrastructure and transactional with your data;
fine for low volume but polls and contends on writes as load grows.

├── Redis available, need throughput and Horizon: → redis driver with Horizon — low latency, atomic pops, per-queue autoscaling and
metrics; the default choice for high-throughput self-hosted apps.

├── AWS-managed, no Redis to operate: → sqs driver — fully managed, scales without servers to run; accept ~seconds of
latency and the 15-minute visibility-timeout ceiling.

└── Local dev or tests only: → sync driver — runs jobs inline in the current process; use only in local
development and tests, never in production.

Full engine: references/decision-engine.md — Decision Tree, Priority Matrix, Decision Table.

Workflows

Workflow: Dispatch Job

Progress:
- [ ] Run `php artisan make:job SendInvoiceEmail`; it implements `ShouldQueue` and
gets the queueable traits so it runs asynchronously
- [ ] Type-hint dependencies in `handle()` so the container injects them, and set
`public int $tries` and `public array $backoff` for bounded, backed-off retries
- [ ] Dispatch with `SendInvoiceEmail::dispatch($invoice)` (pass an id, not a heavy
graph) and run a worker with `php artisan queue:work` to process it
  1. Run php artisan make:job SendInvoiceEmail; it implements ShouldQueue and gets the queueable traits so it runs asynchronously.
    • Verify: The generated class implements ShouldQueue and has a handle() method.
  2. Type-hint dependencies in handle() so the container injects them, and set public int $tries and public array $backoff for bounded, backed-off retries.
    • Verify: handle() receives its dependencies and $tries is a finite number.
  3. Dispatch with SendInvoiceEmail::dispatch($invoice) (pass an id, not a heavy graph) and run a worker with php artisan queue:work to process it.
    • Verify: The request returns immediately and the worker log shows the job processed once.

Workflow: Configure Redis Driver

Progress:
- [ ] Install predis or phpredis, then set `QUEUE_CONNECTION=redis` and configure the
redis connection's `retry_after` in `config/queue.php` above your longest job
- [ ] Run `php artisan queue:work redis --timeout=<below retry_after>` (or install
Horizon) and manage the process with Supervisor or systemd for auto-restart
- [ ] Add `php artisan queue:restart` to the deploy pipeline and configure a
failed_jobs store plus alerting so exhausted jobs are surfaced
  1. Install predis or phpredis, then set QUEUE_CONNECTION=redis and configure the redis connection's retry_after in config/queue.php above your longest job.
    • Verify: config('queue.default') is redis and retry_after exceeds the max job runtime.
  2. Run php artisan queue:work redis --timeout=<below retry_after> (or install Horizon) and manage the process with Supervisor or systemd for auto-restart.
    • Verify: The worker consumes the redis queue and restarts automatically after it exits.
  3. Add php artisan queue:restart to the deploy pipeline and configure a failed_jobs store plus alerting so exhausted jobs are surfaced.
    • Verify: A deploy reloads worker code and a forced failure records to failed_jobs and alerts.

Best practices

  • When many jobs form one logical unit and you need a completion or failure callback, dispatch them with Bus::batch([...])->then(...)->catch(...)
  • When a job calls a rate-limited API, apply middleware such as RateLimited or WithoutOverlapping instead of sleeping inside the job
  • When the same job must not run twice concurrently (e.g

Anti-patterns

Anti-patternWhy it failsDo instead
Running queue:work without a process supervisorThe worker exits on fatal errors, deploys, or memory limits, and silently stops processing jobs with no restart.Manage workers with Supervisor, systemd, or Horizon so they auto-restart and stay running.
Serializing heavy state into the job payloadBloated payloads slow enqueue and dequeue, blow size limits (SQS caps at 256 KB), and can capture stale data.Pass identifiers (or use SerializesModels, which stores only the model key) and<br>reload fresh state inside handle().
QUEUE_CONNECTION=sync in productionInline execution blocks the response and removes all queue benefits — slow requests, timeouts, and no retry safety.Set a real driver (redis, sqs, or database) in production and run a worker or Horizon to process jobs.

See references/anti-patterns.md.

Debugging

  1. Workers leak memory without recycling — Static caches, event listeners, and retained models keep growing the resident set of a persistent process. Fix: Recycle workers with --max-jobs and --max-time, cap --memory, and avoid holding large collections in the job after they are processed.
  2. Poison messages retry forever and block the queue — Unbounded retries turn one permanently-broken payload into a queue-wide denial of service. Fix: Set a finite $tries (or retryUntil()) and a $maxExceptions cap so the job is sent to failed_jobs instead of cycling forever.
  3. Confusing retry_after with the worker timeout — If --timeout is greater than or equal to retry_after, the queue re-dispatches the job to another worker while the first is still running it, causing duplicates. Fix: Always set --timeout a few seconds below retry_after, and size retry_after above the job's worst-case runtime.
  4. Long-lived workers run stale code after deploy — Workers silently process jobs with the old code, producing confusing "the fix didn't deploy" bugs. Fix: Run php artisan queue:restart at the end of every deploy so workers exit and reload on new code.

Performance notes

  • Workers leak memory without recycling — A long-lived worker accumulates memory across jobs and is eventually killed for exceeding the limit, dropping the in-flight job.
  • Throttle jobs against rate-limited dependencies — When a job calls a rate-limited API, apply middleware such as RateLimited or WithoutOverlapping instead of sleeping inside the job.
  • Prefer Redis + Horizon for high-throughput, low-latency queues — When throughput is high or jobs must start within seconds, use the Redis driver with Horizon for near-instant dispatch, atomic pops, and per-queue autoscaling.

Production checklist

  • Every job sets a finite tries or retryUntil plus backoff so poison messages cannot retry forever.
  • The deploy pipeline runs queue:restart so workers pick up newly deployed code.
  • A failed_jobs store is configured and jobs implement failed() to alert and compensate on final failure.
  • QUEUE_CONNECTION in production is a real driver (redis, sqs, or database), never sync.
  • Workers run under Supervisor, systemd, or Horizon so they restart automatically on exit.
  • The worker --timeout is set below the connection retry_after, which itself exceeds the job's max runtime.

Version notes

  • Laravel 13 tightens queued job serialization — Laravel 13 serializes queued job properties more strictly; ensure constructor arguments are serializable or guard with WithoutOverlapping.
  • Job batching available from Laravel 8 — Bus::batch() with then/catch/finally callbacks and a batches table has been available since Laravel 8 and is supported across the 10.x–12.x range.
  • ShouldBeUnique available from Laravel 8 — The ShouldBeUnique interface with uniqueId() and uniqueFor() is available from Laravel 8 and requires a cache driver that supports locks (Redis, Memcached).

Security

  • Treat job payloads as untrusted and never queue secrets — Job payloads are serialized to the queue store (DB rows, Redis, SQS) and may be logged or inspected, so validate deserialized input inside handle() and never put raw secrets or passwords in the constructor arguments.

Testing

  • Activation and decision behavior are verified in eval/scenarios/laravel/queues.yaml.

Additional resources

  • references/anti-patterns.md
  • references/core.md
  • references/decision-engine.md
  • references/examples.md
  • references/pitfalls.md
  • references/recipes.md
  • references/version-differences.md

Knowledge graph

Prerequisites: laravel/service-container
Related: audit/laravel, databases/redis, laravel/eloquent-relationships
Behavior constraints: ai-rules/strategy-minimal-diff

<!-- graph-fragment: graph/fragments/laravel/queues.yaml -->

Skill contract

Produces: queue.driver-selected, queue.worker-configured
Risk: medium · Confidence: 0.95

<!-- contract-fragment: contracts/fragments/laravel/queues.yaml -->

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.