Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Build Laravel 10+ apps with Eloquent, Sanctum auth, Horizon queues, Livewire, and RESTful API resources.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/queues.md
1# Queue System23## Job Patterns45```php6namespace App\Jobs;78use App\Models\Post;9use App\Models\User;10use Illuminate\Bus\Queueable;11use Illuminate\Contracts\Queue\ShouldQueue;12use Illuminate\Foundation\Bus\Dispatchable;13use Illuminate\Queue\InteractsWithQueue;14use Illuminate\Queue\SerializesModels;1516class ProcessPost implements ShouldQueue17{18use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;1920public $tries = 3;21public $timeout = 120;22public $maxExceptions = 3;23public $backoff = [60, 120, 300]; // Exponential backoff2425public function __construct(26public Post $post,27public ?User $user = null,28) {}2930public function handle(): void31{32// Process the post33$this->post->update(['processed' => true]);3435// Can access injected dependencies36$analytics = app(AnalyticsService::class);37$analytics->trackPostProcessed($this->post);38}3940public function failed(\Throwable $exception): void41{42// Handle job failure43\Log::error('Post processing failed', [44'post_id' => $this->post->id,45'error' => $exception->getMessage(),46]);47}48}49```5051## Dispatching Jobs5253```php54use App\Jobs\ProcessPost;5556// Dispatch immediately57ProcessPost::dispatch($post);5859// Dispatch to specific queue60ProcessPost::dispatch($post)->onQueue('processing');6162// Delayed dispatch63ProcessPost::dispatch($post)->delay(now()->addMinutes(10));6465// Dispatch after database commit66ProcessPost::dispatch($post)->afterCommit();6768// Dispatch conditionally69ProcessPost::dispatchIf($condition, $post);70ProcessPost::dispatchUnless($condition, $post);7172// Synchronous dispatch (no queue)73ProcessPost::dispatchSync($post);7475// Dispatch after response76ProcessPost::dispatchAfterResponse($post);77```7879## Job Chaining8081```php82use App\Jobs\{OptimizeImage, GenerateThumbnail, PublishPost};8384// Chain jobs85OptimizeImage::withChain([86new GenerateThumbnail($post),87new PublishPost($post),88])->dispatch($post);8990// Catch failures in chain91Bus::chain([92new ProcessPost($post),93new NotifyUser($user),94])->catch(function (\Throwable $e) {95// Handle failure96})->dispatch();97```9899## Job Batching100101```php102use Illuminate\Bus\Batch;103use Illuminate\Support\Facades\Bus;104105$batch = Bus::batch([106new ProcessPost($post1),107new ProcessPost($post2),108new ProcessPost($post3),109])->then(function (Batch $batch) {110// All jobs completed successfully111})->catch(function (Batch $batch, \Throwable $e) {112// First batch job failure detected113})->finally(function (Batch $batch) {114// The batch has finished executing115})->name('Process Posts')116->allowFailures()117->dispatch();118119// Check batch status120$batch = Bus::findBatch($batchId);121if ($batch->finished()) {122// Batch is complete123}124if ($batch->cancelled()) {125// Batch was cancelled126}127128// Add jobs to existing batch129$batch->add([130new ProcessPost($post4),131]);132```133134## Rate Limiting135136```php137use Illuminate\Support\Facades\Redis;138139class ProcessPost implements ShouldQueue140{141public function handle(): void142{143Redis::throttle('process-posts')144->block(0)145->allow(10)146->every(60)147->then(function () {148// Lock acquired, process job149}, function () {150// Could not acquire lock, release job back151$this->release(10);152});153}154}155156// Or using middleware157use Illuminate\Queue\Middleware\RateLimited;158159public function middleware(): array160{161return [new RateLimited('process-posts')];162}163```164165## Job Middleware166167```php168namespace App\Jobs\Middleware;169170class RateLimitedByUser171{172public function handle($job, $next): void173{174Redis::throttle("user:{$job->user->id}")175->allow(10)176->every(60)177->then(function () use ($job, $next) {178$next($job);179}, function () use ($job) {180$job->release(10);181});182}183}184185// Use in job186use App\Jobs\Middleware\RateLimitedByUser;187188public function middleware(): array189{190return [new RateLimitedByUser];191}192193// Skip middleware194use Illuminate\Queue\Middleware\WithoutOverlapping;195196public function middleware(): array197{198return [199(new WithoutOverlapping($this->user->id))->expireAfter(180),200];201}202```203204## Unique Jobs205206```php207use Illuminate\Contracts\Queue\ShouldBeUnique;208209class ProcessPost implements ShouldQueue, ShouldBeUnique210{211public int $uniqueFor = 3600;212213public function __construct(214public Post $post,215) {}216217public function uniqueId(): string218{219return $this->post->id;220}221}222223// Or use unique until processing224use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;225226class ProcessPost implements ShouldQueue, ShouldBeUniqueUntilProcessing227{228// ...229}230```231232## Failed Jobs233234```php235// Retry failed job236php artisan queue:retry <job-id>237238// Retry all failed jobs239php artisan queue:retry all240241// Flush failed jobs242php artisan queue:flush243244// Prune failed jobs245php artisan queue:prune-failed --hours=48246247// Handle in code248use Illuminate\Support\Facades\Queue;249250Queue::failing(function (JobFailed $event) {251\Log::error('Job failed', [252'connection' => $event->connectionName,253'queue' => $event->job->getQueue(),254'exception' => $event->exception->getMessage(),255]);256});257```258259## Queue Workers260261```bash262# Start worker263php artisan queue:work264265# Process specific queue266php artisan queue:work --queue=high,default267268# Process one job269php artisan queue:work --once270271# Stop worker gracefully272php artisan queue:restart273274# Timeout settings275php artisan queue:work --timeout=60276277# Memory limit278php artisan queue:work --memory=512279280# Max jobs before restart281php artisan queue:work --max-jobs=1000282283# Max time before restart284php artisan queue:work --max-time=3600285```286287## Horizon Setup288289```php290// config/horizon.php291return [292'environments' => [293'production' => [294'supervisor-1' => [295'connection' => 'redis',296'queue' => ['default'],297'balance' => 'auto',298'maxProcesses' => 10,299'maxTime' => 0,300'maxJobs' => 0,301'memory' => 512,302'tries' => 3,303'timeout' => 60,304'nice' => 0,305],306'supervisor-2' => [307'connection' => 'redis',308'queue' => ['high', 'default'],309'balance' => 'auto',310'maxProcesses' => 5,311'tries' => 3,312],313],314],315];316317// Start Horizon318php artisan horizon319320// Terminate Horizon321php artisan horizon:terminate322323// Pause workers324php artisan horizon:pause325326// Continue workers327php artisan horizon:continue328329// Check status330php artisan horizon:status331```332333## Monitoring334335```php336use Illuminate\Queue\Events\JobProcessed;337use Illuminate\Queue\Events\JobFailed;338use Illuminate\Support\Facades\Queue;339340// In AppServiceProvider341public function boot(): void342{343Queue::before(function (JobProcessing $event) {344// Called before job is processed345});346347Queue::after(function (JobProcessed $event) {348// Called after job is processed349\Log::info('Job processed', [350'job' => $event->job->resolveName(),351'time' => $event->job->processingTime(),352]);353});354355Queue::failing(function (JobFailed $event) {356// Called when job fails357\Log::error('Job failed', [358'job' => $event->job->resolveName(),359'exception' => $event->exception,360]);361});362}363```364365## Queue Configuration366367```php368// config/queue.php369return [370'default' => env('QUEUE_CONNECTION', 'sync'),371372'connections' => [373'sync' => [374'driver' => 'sync',375],376377'database' => [378'driver' => 'database',379'table' => 'jobs',380'queue' => 'default',381'retry_after' => 90,382'after_commit' => false,383],384385'redis' => [386'driver' => 'redis',387'connection' => 'default',388'queue' => env('REDIS_QUEUE', 'default'),389'retry_after' => 90,390'block_for' => null,391'after_commit' => false,392],393394'sqs' => [395'driver' => 'sqs',396'key' => env('AWS_ACCESS_KEY_ID'),397'secret' => env('AWS_SECRET_ACCESS_KEY'),398'prefix' => env('SQS_PREFIX'),399'queue' => env('SQS_QUEUE'),400'region' => env('AWS_DEFAULT_REGION'),401],402],403404'failed' => [405'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),406'database' => env('DB_CONNECTION', 'mysql'),407'table' => 'failed_jobs',408],409];410```411412## Best Practices4134141. **Keep jobs small and focused** - Single responsibility4152. **Make jobs idempotent** - Safe to run multiple times4163. **Use type hints** - Better error detection4174. **Set reasonable timeouts** - Prevent hanging jobs4185. **Monitor failed jobs** - Set up alerts4196. **Use batching for bulk operations** - Better performance4207. **Implement proper error handling** - Use failed() method4218. **Use unique jobs** - Prevent duplicate processing4229. **Queue long-running tasks** - Don't block requests42310. **Use Horizon for Redis queues** - Better monitoring424