All skills
asyrafhussin avatar

/laravel-queues

@2631530

Laravel 13 queue and job patterns — driver choice, job design (idempotency, ShouldQueue, model serialisation), retry and failure handling, worker scaling, Bus batching and chaining, Horizon when warranted, queue testing. Use when designing async jobs, scheduling background work, configuring Horizon, debugging stuck jobs, or auditing queue health. Triggers on "Laravel queue", "Laravel job", "background job", "Horizon setup", "failed jobs", "Bus batch", "queue worker tuning".

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/laravel-queues

This session only. Nothing lands on disk.

rulesretry-tries-and-backoff.md

≈1.2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

$tries and #[Backoff] — Configure Both

Impact: HIGH (Default 3 tries with no backoff causes retry storms; misconfigured backoff means slow recovery)

Laravel's default retry behaviour is conservative: 1 attempt unless you set $tries (or the --tries flag on queue:work). That's almost never enough for jobs that touch external services — Stripe blips, network glitches, third-party rate limits all warrant a retry. But retrying immediately with no delay is just as bad — you hammer the same failing dependency 5 times in a row.

Set both: $tries (or --tries flag) and #[Backoff(...)] (or $backoff property) for delays. Use exponential delays for transient failures.

Incorrect

// ❌ Default 1 attempt — any transient failure is fatal
class ChargeCustomer implements ShouldQueue { /* … */ }
// No $tries → 1 attempt → first Stripe blip = job marked failed

// ❌ Many tries with no backoff — retry storm
class ChargeCustomer implements ShouldQueue
{
    public int $tries = 5;
    // No backoff → 5 immediate retries → if Stripe is rate-limiting you, you're rate-limited worse
}

// ❌ Constant backoff = naive
class ChargeCustomer implements ShouldQueue
{
    public int $tries = 5;
    public int $backoff = 30;
    // 5 retries × 30s apart = 150s recovery on a service that's been down for 5 minutes
}

Correct — Laravel 13 idiom: #[Backoff] attribute with exponential delays

// ✅ Production-grade retry config

namespace App\Jobs;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Attributes\Backoff;

#[Backoff([1, 5, 30, 120, 600])]   // exponential-ish: 1s, 5s, 30s, 2min, 10min
class ChargeCustomer implements ShouldQueue
{
    use Dispatchable, Queueable;

    public int $tries = 5;
    public int $timeout = 30;
    public int $maxExceptions = 3;   // stop retrying after 3 different exceptions

    // …
}

What each setting does:

Setting Effect
$tries = 5 Job will be released back to the queue up to 4 times before being marked failed
#[Backoff([1, 5, 30, 120, 600])] Delay between retries: 1s, 5s, 30s, 2min, 10min
$timeout = 30 Each attempt has 30s before being killed (release or fail depending on FailOnTimeout)
$maxExceptions = 3 Even if $tries is 5, stop after 3 distinct exception types — protects against catastrophic bugs

Property vs attribute

Both forms work in Laravel 13. The attribute (#[Backoff(...)]) is preferred for new code — cleaner, sits next to the class declaration:

// ✅ Attribute (preferred in Laravel 13)
#[Backoff([1, 5, 30])]
class X implements ShouldQueue {}

// ✅ Property (still works)
class X implements ShouldQueue {
    public int|array $backoff = [1, 5, 30];
}

// ✅ Computed backoff via method
public function backoff(): array
{
    return [1, 5, 30 * $this->attempts()];   // dynamic
}

What value should $tries be?

Job type Recommended $tries
Payment / external charge 5–7 (with idempotency)
External API call (Stripe, Shopify, etc.) 3–5
Email / notification 3
Internal pure computation 1 (don't retry on real bugs)
Data export / batch processing 3 (with chunking)

For internal-only jobs (no external dependencies), $tries = 1 is often right — retrying on a programmer error is just wasted compute.

Setting at the worker level vs the job level

# Worker-level defaults (apply to all jobs unless overridden)
php artisan queue:work --tries=3 --backoff=3 --max-time=3600

Job-level settings ($tries, #[Backoff]) override worker-level. Use job-level for production — different jobs need different policies. Use worker-level as a sensible default for unconfigured jobs.

Detection

# Jobs without $tries or #[Tries] / #[Backoff]
for f in app/Jobs/*.php; do
  if grep -q 'implements ShouldQueue' "$f" && ! grep -qE '\$tries|#\[Tries|#\[Backoff' "$f"; then
    echo "NO RETRY CONFIG: $f"
  fi
done

# Find jobs with constant backoff (= 0 or a single int) on external-facing work
grep -rEn 'public int \$backoff = [0-9]+;' --include='*.php' app/Jobs/

Reference: Laravel 13 — Queues: Max Job Attempts & Timeout · Laravel 13 — Queues: Backoff Configuration

Source: SKILL.md on GitHub

No alerts16d3 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The analyzed skill contains high-quality reference guidelines and checklists for configuring and auditing Laravel 13 queue structures and background jobs. No security risks, malicious patterns, or vulnerabilities were identified.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

Signed by skilld at 2631530. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 5 months ago
metadata
{
  "author": "agent-skills",
  "version": "1.0.0"
}

README badge

README badge for asyrafhussin/agent-skills/laravel-queues