[{"data":1,"prerenderedAt":2661},["ShallowReactive",2],{"post-laravel-queues-explained-simply":3},{"id":4,"title":5,"author":6,"body":7,"category":2643,"cover":2644,"date":2645,"description":2646,"extension":2647,"featured":2648,"meta":2649,"navigation":144,"path":2650,"published":144,"readingTime":2651,"seo":2652,"sitemap":2653,"stem":2654,"tags":2655,"updated":2645,"__hash__":2660},"posts/posts/laravel-queues-explained-simply.md","Laravel Queues Explained Simply (Laravel 13)","Kashyap Kumar",{"type":8,"value":9,"toc":2589},"minimark",[10,15,20,24,27,31,60,64,67,82,85,89,96,100,108,114,117,323,338,342,345,375,382,415,426,430,436,465,468,508,515,519,529,532,549,556,565,569,572,575,590,597,614,621,625,628,657,662,666,672,675,686,690,693,710,724,727,938,942,949,1035,1045,1049,1052,1077,1080,1084,1087,1091,1094,1117,1121,1128,1284,1299,1303,1310,1420,1424,1427,1493,1509,1513,1516,1565,1568,1572,1575,1582,1586,1589,1636,1639,1643,1649,1690,1697,1701,1708,1758,1761,1765,1772,1775,1779,1782,1786,1812,1816,1819,1836,1850,1854,1864,1914,1917,1921,1931,1935,2064,2067,2127,2131,2136,2162,2166,2173,2271,2274,2278,2281,2285,2291,2305,2308,2316,2325,2342,2345,2357,2361,2374,2378,2384,2481,2485,2488,2492,2499,2503,2509,2531,2541,2545,2548,2551,2582,2585],[11,12,14],"h2",{"id":13},"overview-of-queues","Overview of Queues",[16,17,19],"h3",{"id":18},"what-are-queues","What Are Queues?",[21,22,23],"p",{},"In web applications, some tasks take too long to complete during a normal HTTP request. For example, processing a large uploaded CSV file, sending hundreds of emails, or generating a PDF report. If you do these tasks right away, the user would have to wait several seconds (or even minutes) before seeing the next page. This creates a poor user experience.",[21,25,26],{},"Queues solve this problem by letting you defer time‑consuming tasks to be processed in the background. Your application immediately returns a response to the user, while the heavy work happens later, outside the request cycle.",[16,28,30],{"id":29},"why-are-queues-important","Why Are Queues Important?",[32,33,34,42,48,54],"ul",{},[35,36,37,41],"li",{},[38,39,40],"strong",{},"Improved user experience"," – pages load faster.",[35,43,44,47],{},[38,45,46],{},"Better scalability"," – you can add more queue workers to handle spikes in traffic.",[35,49,50,53],{},[38,51,52],{},"Reliability"," – if a task fails, the queue can retry it automatically.",[35,55,56,59],{},[38,57,58],{},"Resource management"," – you can prioritise certain jobs (e.g., process high‑priority tasks first).",[16,61,63],{"id":62},"realworld-example","Real‑World Example",[21,65,66],{},"Suppose, you run a podcast website. When a user uploads a new episode, you need to:",[68,69,70,73,76,79],"ol",{},[35,71,72],{},"Convert the audio file to different formats.",[35,74,75],{},"Generate a thumbnail.",[35,77,78],{},"Update search indexes.",[35,80,81],{},"Send confirmation emails to subscribers.",[21,83,84],{},"Doing all this synchronously would make the upload feel very slow. Instead, after the user uploads the file, you dispatch a job to a queue. The user sees “Upload successful” immediately, while behind the scenes a queue worker processes the conversion, thumbnails, etc.",[11,86,88],{"id":87},"fundamental-concepts-of-laravel-queues","Fundamental Concepts of Laravel Queues",[21,90,91],{},[92,93],"img",{"alt":94,"src":95},"Laravel Queues and Jobs Processing","/blog-post-images/laravel-queues-explained-simply/laravel-queues-jobs-processing.png",[16,97,99],{"id":98},"jobs-the-unit-of-work","Jobs – The Unit of Work",[21,101,102,103,107],{},"A job is a simple PHP class that contains the logic for a task you want to run in the background. For example, a ",[104,105,106],"code",{},"ProcessPodcast"," job might contain the code to convert an audio file.",[21,109,110,111],{},"You create a job using Artisan: ",[104,112,113],{},"php artisan make:job ProcessPodcast",[21,115,116],{},"Here’s what a basic job looks like:",[118,119,124],"pre",{"className":120,"code":121,"language":122,"meta":123,"style":123},"language-php shiki shiki-themes github-light","\u003C?php\n\nnamespace App\\Jobs;\n\nuse App\\Models\\Podcast;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Foundation\\Queue\\Queueable;\n\nclass ProcessPodcast implements ShouldQueue\n{\n    use Queueable;\n\n    public function __construct(public Podcast $podcast)\n    {}\n\n    public function handle(): void\n    {\n        // The actual work happens here\n        // e.g., convert audio, generate thumbnail...\n    }\n}\n","php","",[104,125,126,139,146,160,165,176,186,196,201,216,222,233,238,262,268,273,292,298,305,311,317],{"__ignoreMap":123},[127,128,131,135],"span",{"class":129,"line":130},"line",1,[127,132,134],{"class":133},"sD7c4","\u003C?",[127,136,138],{"class":137},"sYu0t","php\n",[127,140,142],{"class":129,"line":141},2,[127,143,145],{"emptyLinePlaceholder":144},true,"\n",[127,147,149,152,156],{"class":129,"line":148},3,[127,150,151],{"class":133},"namespace",[127,153,155],{"class":154},"s7eDp"," App\\Jobs",[127,157,159],{"class":158},"sgsFI",";\n",[127,161,163],{"class":129,"line":162},4,[127,164,145],{"emptyLinePlaceholder":144},[127,166,168,171,174],{"class":129,"line":167},5,[127,169,170],{"class":133},"use",[127,172,173],{"class":137}," App\\Models\\Podcast",[127,175,159],{"class":158},[127,177,179,181,184],{"class":129,"line":178},6,[127,180,170],{"class":133},[127,182,183],{"class":137}," Illuminate\\Contracts\\Queue\\ShouldQueue",[127,185,159],{"class":158},[127,187,189,191,194],{"class":129,"line":188},7,[127,190,170],{"class":133},[127,192,193],{"class":137}," Illuminate\\Foundation\\Queue\\Queueable",[127,195,159],{"class":158},[127,197,199],{"class":129,"line":198},8,[127,200,145],{"emptyLinePlaceholder":144},[127,202,204,207,210,213],{"class":129,"line":203},9,[127,205,206],{"class":133},"class",[127,208,209],{"class":154}," ProcessPodcast",[127,211,212],{"class":133}," implements",[127,214,215],{"class":154}," ShouldQueue\n",[127,217,219],{"class":129,"line":218},10,[127,220,221],{"class":158},"{\n",[127,223,225,228,231],{"class":129,"line":224},11,[127,226,227],{"class":133},"    use",[127,229,230],{"class":137}," Queueable",[127,232,159],{"class":158},[127,234,236],{"class":129,"line":235},12,[127,237,145],{"emptyLinePlaceholder":144},[127,239,241,244,247,250,253,256,259],{"class":129,"line":240},13,[127,242,243],{"class":133},"    public",[127,245,246],{"class":133}," function",[127,248,249],{"class":137}," __construct",[127,251,252],{"class":158},"(",[127,254,255],{"class":133},"public",[127,257,258],{"class":137}," Podcast",[127,260,261],{"class":158}," $podcast)\n",[127,263,265],{"class":129,"line":264},14,[127,266,267],{"class":158},"    {}\n",[127,269,271],{"class":129,"line":270},15,[127,272,145],{"emptyLinePlaceholder":144},[127,274,276,278,280,283,286,289],{"class":129,"line":275},16,[127,277,243],{"class":133},[127,279,246],{"class":133},[127,281,282],{"class":154}," handle",[127,284,285],{"class":158},"()",[127,287,288],{"class":133},":",[127,290,291],{"class":133}," void\n",[127,293,295],{"class":129,"line":294},17,[127,296,297],{"class":158},"    {\n",[127,299,301],{"class":129,"line":300},18,[127,302,304],{"class":303},"sAwPA","        // The actual work happens here\n",[127,306,308],{"class":129,"line":307},19,[127,309,310],{"class":303},"        // e.g., convert audio, generate thumbnail...\n",[127,312,314],{"class":129,"line":313},20,[127,315,316],{"class":158},"    }\n",[127,318,320],{"class":129,"line":319},21,[127,321,322],{"class":158},"}\n",[32,324,325,331],{},[35,326,327,330],{},[104,328,329],{},"ShouldQueue"," tells Laravel to push this job to a queue instead of running it immediately.",[35,332,333,334,337],{},"The ",[104,335,336],{},"handle()"," method is called when the queue worker processes the job.",[16,339,341],{"id":340},"connections-vs-queues","Connections vs. Queues",[21,343,344],{},"Think of your application’s queue system like a post office:",[32,346,347,369],{},[35,348,349,352,353,356,357,360,361,364,365,368],{},[38,350,351],{},"Connection"," = the postal service you choose (e.g., regular mail, express courier, or a local drop‑off). In Laravel, connections are backends like ",[104,354,355],{},"database",", ",[104,358,359],{},"redis",", or ",[104,362,363],{},"sqs",". They are defined in ",[104,366,367],{},"config/queue.php",".",[35,370,371,374],{},[38,372,373],{},"Queue"," = a specific pile or mailbox inside that service. You can have multiple queues per connection, like “emails”, “reports”, “thumbnails”.",[21,376,377,378,381],{},"By default, every job goes into the ",[104,379,380],{},"default"," queue of the default connection. But you can send a job to a specific queue:",[118,383,385],{"className":120,"code":384,"language":122,"meta":123,"style":123},"ProcessPodcast::dispatch($podcast)->onQueue('processing');\n",[104,386,387],{"__ignoreMap":123},[127,388,389,391,394,397,400,403,406,408,412],{"class":129,"line":130},[127,390,106],{"class":137},[127,392,393],{"class":133},"::",[127,395,396],{"class":154},"dispatch",[127,398,399],{"class":158},"($podcast)",[127,401,402],{"class":133},"->",[127,404,405],{"class":154},"onQueue",[127,407,252],{"class":158},[127,409,411],{"class":410},"sYBdl","'processing'",[127,413,414],{"class":158},");\n",[21,416,417,418,421,422,425],{},"This helps you prioritise work: you can tell a queue worker to process the ",[104,419,420],{},"high"," queue before the ",[104,423,424],{},"low"," queue.",[16,427,429],{"id":428},"dispatching-jobs-adding-work-to-the-queue","Dispatching Jobs – Adding Work to the Queue",[21,431,432,433,435],{},"To send a job to the queue, you call the ",[104,434,396],{}," method on the job class:",[118,437,439],{"className":120,"code":438,"language":122,"meta":123,"style":123},"use App\\Jobs\\ProcessPodcast;\n\nProcessPodcast::dispatch($podcast);\n",[104,440,441,450,454],{"__ignoreMap":123},[127,442,443,445,448],{"class":129,"line":130},[127,444,170],{"class":133},[127,446,447],{"class":137}," App\\Jobs\\ProcessPodcast",[127,449,159],{"class":158},[127,451,452],{"class":129,"line":141},[127,453,145],{"emptyLinePlaceholder":144},[127,455,456,458,460,462],{"class":129,"line":148},[127,457,106],{"class":137},[127,459,393],{"class":133},[127,461,396],{"class":154},[127,463,464],{"class":158},"($podcast);\n",[21,466,467],{},"You can also delay the job:",[118,469,471],{"className":120,"code":470,"language":122,"meta":123,"style":123},"ProcessPodcast::dispatch($podcast)->delay(now()->addMinutes(10));\n",[104,472,473],{"__ignoreMap":123},[127,474,475,477,479,481,483,485,488,490,493,495,497,500,502,505],{"class":129,"line":130},[127,476,106],{"class":137},[127,478,393],{"class":133},[127,480,396],{"class":154},[127,482,399],{"class":158},[127,484,402],{"class":133},[127,486,487],{"class":154},"delay",[127,489,252],{"class":158},[127,491,492],{"class":154},"now",[127,494,285],{"class":158},[127,496,402],{"class":133},[127,498,499],{"class":154},"addMinutes",[127,501,252],{"class":158},[127,503,504],{"class":137},"10",[127,506,507],{"class":158},"));\n",[21,509,510,511,514],{},"If you ever need to run a job immediately (in the same request), use ",[104,512,513],{},"dispatchSync()"," – useful for testing or very quick tasks.",[16,516,518],{"id":517},"queue-worker-the-engine-that-processes-jobs","Queue Worker – The Engine That Processes Jobs",[21,520,521,522,525,526,528],{},"A ",[38,523,524],{},"queue worker"," is a long‑running PHP process that constantly checks the queue for new jobs. When it finds one, it executes the job’s ",[104,527,336],{}," method.",[21,530,531],{},"Start a worker with:",[118,533,537],{"className":534,"code":535,"language":536,"meta":123,"style":123},"language-bash shiki shiki-themes github-light","php artisan queue:work\n","bash",[104,538,539],{"__ignoreMap":123},[127,540,541,543,546],{"class":129,"line":130},[127,542,122],{"class":154},[127,544,545],{"class":410}," artisan",[127,547,548],{"class":410}," queue:work\n",[21,550,551,552,555],{},"By default, the worker will run forever. For production, you should use a process monitor like ",[38,553,554],{},"Supervisor"," to automatically restart the worker if it crashes.",[557,558,559],"blockquote",{},[21,560,561,562],{},"Important: Workers keep your application in memory. If you change your code, you must restart the worker: ",[104,563,564],{},"php artisan queue:restart",[16,566,568],{"id":567},"handling-failures-what-if-something-goes-wrong","Handling Failures – What If Something Goes Wrong?",[21,570,571],{},"Sometimes a job fails (e.g., an external API is down). Laravel can retry the job a number of times before giving up.",[21,573,574],{},"You set the maximum attempts:",[32,576,577,583],{},[35,578,579,580],{},"Globally for the worker: ",[104,581,582],{},"php artisan queue:work --tries=3",[35,584,585,586,589],{},"Or on the job class using the ",[104,587,588],{},"#[Tries(3)]"," attribute.",[21,591,592,593,596],{},"When all attempts fail, Laravel stores the failed job in a ",[104,594,595],{},"failed_jobs"," database table. You can then retry failed jobs manually:",[118,598,600],{"className":534,"code":599,"language":536,"meta":123,"style":123},"php artisan queue:retry all\n",[104,601,602],{"__ignoreMap":123},[127,603,604,606,608,611],{"class":129,"line":130},[127,605,122],{"class":154},[127,607,545],{"class":410},[127,609,610],{"class":410}," queue:retry",[127,612,613],{"class":410}," all\n",[21,615,616,617,620],{},"You can also define a ",[104,618,619],{},"failed()"," method inside your job to run custom cleanup or notifications when a job finally fails.",[16,622,624],{"id":623},"putting-it-all-together-a-simple-workflow","Putting It All Together – A Simple Workflow",[21,626,627],{},"Let's put the steps together for the real‑world example:",[68,629,630,636,639,642,648,651],{},[35,631,632,633,635],{},"User uploads a podcast → Controller dispatches a ",[104,634,106],{}," job.",[35,637,638],{},"User receives “success” response immediately.",[35,640,641],{},"Queue worker (running separately) picks up the job.",[35,643,644,645,647],{},"Worker executes the ",[104,646,336],{}," method (converts audio, etc.).",[35,649,650],{},"If the job fails, Laravel retries it up to the configured limit.",[35,652,653,654,656],{},"After final failure, the job appears in the ",[104,655,595],{}," table for you to inspect and retry.",[557,658,659],{},[21,660,661],{},"Let's now explore some advanced features of Laravel queues that can help you manage complex workflows and ensure your jobs run smoothly.",[11,663,665],{"id":664},"job-middleware-adding-layers-to-your-jobs","Job Middleware – Adding Layers to Your Jobs",[21,667,668,669,671],{},"Sometimes you want to run some code before or after your job executes, without cluttering the job’s ",[104,670,336],{}," method. Job middleware lets you wrap custom logic around your job’s execution.",[21,673,674],{},"Think of it like a “filter” for your job. For example, you might want to:",[32,676,677,680,683],{},[35,678,679],{},"Rate‑limit how often a job can run.",[35,681,682],{},"Prevent two identical jobs from running at the same time.",[35,684,685],{},"Skip the job if a certain condition is not met.",[16,687,689],{"id":688},"creating-a-job-middleware","Creating a Job Middleware",[21,691,692],{},"You can generate a middleware class with Artisan:",[118,694,696],{"className":534,"code":695,"language":536,"meta":123,"style":123},"php artisan make:job-middleware RateLimited\n",[104,697,698],{"__ignoreMap":123},[127,699,700,702,704,707],{"class":129,"line":130},[127,701,122],{"class":154},[127,703,545],{"class":410},[127,705,706],{"class":410}," make:job-middleware",[127,708,709],{"class":410}," RateLimited\n",[21,711,712,713,715,716,719,720,723],{},"A middleware class contains a ",[104,714,336],{}," method that receives the job and a closure (",[104,717,718],{},"$next","). You perform your checks, and then call ",[104,721,722],{},"$next($job)"," to continue processing.",[21,725,726],{},"Example – a simple rate limiter middleware:",[118,728,730],{"className":120,"code":729,"language":122,"meta":123,"style":123},"\u003C?php\n\nnamespace App\\Jobs\\Middleware;\n\nuse Closure;\nuse Illuminate\\Support\\Facades\\Redis;\n\nclass RateLimited\n{\n    public function handle(object $job, Closure $next): void\n    {\n        Redis::throttle('key')\n            ->allow(1)->every(5)\n            ->then(function () use ($job, $next) {\n                $next($job);   // job can run\n            }, function () use ($job) {\n                $job->release(5); // try again after 5 seconds\n            });\n    }\n}\n",[104,731,732,738,742,751,755,764,773,777,783,787,813,817,835,863,883,891,905,925,930,934],{"__ignoreMap":123},[127,733,734,736],{"class":129,"line":130},[127,735,134],{"class":133},[127,737,138],{"class":137},[127,739,740],{"class":129,"line":141},[127,741,145],{"emptyLinePlaceholder":144},[127,743,744,746,749],{"class":129,"line":148},[127,745,151],{"class":133},[127,747,748],{"class":154}," App\\Jobs\\Middleware",[127,750,159],{"class":158},[127,752,753],{"class":129,"line":162},[127,754,145],{"emptyLinePlaceholder":144},[127,756,757,759,762],{"class":129,"line":167},[127,758,170],{"class":133},[127,760,761],{"class":137}," Closure",[127,763,159],{"class":158},[127,765,766,768,771],{"class":129,"line":178},[127,767,170],{"class":133},[127,769,770],{"class":137}," Illuminate\\Support\\Facades\\Redis",[127,772,159],{"class":158},[127,774,775],{"class":129,"line":188},[127,776,145],{"emptyLinePlaceholder":144},[127,778,779,781],{"class":129,"line":198},[127,780,206],{"class":133},[127,782,709],{"class":154},[127,784,785],{"class":129,"line":203},[127,786,221],{"class":158},[127,788,789,791,793,795,797,800,803,806,809,811],{"class":129,"line":218},[127,790,243],{"class":133},[127,792,246],{"class":133},[127,794,282],{"class":154},[127,796,252],{"class":158},[127,798,799],{"class":133},"object",[127,801,802],{"class":158}," $job, ",[127,804,805],{"class":137},"Closure",[127,807,808],{"class":158}," $next)",[127,810,288],{"class":133},[127,812,291],{"class":133},[127,814,815],{"class":129,"line":224},[127,816,297],{"class":158},[127,818,819,822,824,827,829,832],{"class":129,"line":235},[127,820,821],{"class":137},"        Redis",[127,823,393],{"class":133},[127,825,826],{"class":154},"throttle",[127,828,252],{"class":158},[127,830,831],{"class":410},"'key'",[127,833,834],{"class":158},")\n",[127,836,837,840,843,845,848,851,853,856,858,861],{"class":129,"line":240},[127,838,839],{"class":133},"            ->",[127,841,842],{"class":154},"allow",[127,844,252],{"class":158},[127,846,847],{"class":137},"1",[127,849,850],{"class":158},")",[127,852,402],{"class":133},[127,854,855],{"class":154},"every",[127,857,252],{"class":158},[127,859,860],{"class":137},"5",[127,862,834],{"class":158},[127,864,865,867,870,872,875,878,880],{"class":129,"line":264},[127,866,839],{"class":133},[127,868,869],{"class":154},"then",[127,871,252],{"class":158},[127,873,874],{"class":133},"function",[127,876,877],{"class":158}," () ",[127,879,170],{"class":133},[127,881,882],{"class":158}," ($job, $next) {\n",[127,884,885,888],{"class":129,"line":270},[127,886,887],{"class":158},"                $next($job);   ",[127,889,890],{"class":303},"// job can run\n",[127,892,893,896,898,900,902],{"class":129,"line":275},[127,894,895],{"class":158},"            }, ",[127,897,874],{"class":133},[127,899,877],{"class":158},[127,901,170],{"class":133},[127,903,904],{"class":158}," ($job) {\n",[127,906,907,910,912,915,917,919,922],{"class":129,"line":294},[127,908,909],{"class":158},"                $job",[127,911,402],{"class":133},[127,913,914],{"class":154},"release",[127,916,252],{"class":158},[127,918,860],{"class":137},[127,920,921],{"class":158},"); ",[127,923,924],{"class":303},"// try again after 5 seconds\n",[127,926,927],{"class":129,"line":300},[127,928,929],{"class":158},"            });\n",[127,931,932],{"class":129,"line":307},[127,933,316],{"class":158},[127,935,936],{"class":129,"line":313},[127,937,322],{"class":158},[16,939,941],{"id":940},"attaching-middleware-to-a-job","Attaching Middleware to a Job",[21,943,944,945,948],{},"Inside your job class, add a ",[104,946,947],{},"middleware()"," method that returns an array of middleware objects:",[118,950,952],{"className":120,"code":951,"language":122,"meta":123,"style":123},"use App\\Jobs\\Middleware\\RateLimited;\n\nclass ProcessPodcast implements ShouldQueue\n{\n    // ...\n\n    public function middleware(): array\n    {\n        return [new RateLimited];\n    }\n}\n",[104,953,954,963,967,977,981,986,990,1006,1010,1027,1031],{"__ignoreMap":123},[127,955,956,958,961],{"class":129,"line":130},[127,957,170],{"class":133},[127,959,960],{"class":137}," App\\Jobs\\Middleware\\RateLimited",[127,962,159],{"class":158},[127,964,965],{"class":129,"line":141},[127,966,145],{"emptyLinePlaceholder":144},[127,968,969,971,973,975],{"class":129,"line":148},[127,970,206],{"class":133},[127,972,209],{"class":154},[127,974,212],{"class":133},[127,976,215],{"class":154},[127,978,979],{"class":129,"line":162},[127,980,221],{"class":158},[127,982,983],{"class":129,"line":167},[127,984,985],{"class":303},"    // ...\n",[127,987,988],{"class":129,"line":178},[127,989,145],{"emptyLinePlaceholder":144},[127,991,992,994,996,999,1001,1003],{"class":129,"line":188},[127,993,243],{"class":133},[127,995,246],{"class":133},[127,997,998],{"class":154}," middleware",[127,1000,285],{"class":158},[127,1002,288],{"class":133},[127,1004,1005],{"class":133}," array\n",[127,1007,1008],{"class":129,"line":198},[127,1009,297],{"class":158},[127,1011,1012,1015,1018,1021,1024],{"class":129,"line":203},[127,1013,1014],{"class":133},"        return",[127,1016,1017],{"class":158}," [",[127,1019,1020],{"class":133},"new",[127,1022,1023],{"class":137}," RateLimited",[127,1025,1026],{"class":158},"];\n",[127,1028,1029],{"class":129,"line":218},[127,1030,316],{"class":158},[127,1032,1033],{"class":129,"line":224},[127,1034,322],{"class":158},[21,1036,1037,1038,1040,1041,1044],{},"Now every time ",[104,1039,106],{}," is processed, the ",[104,1042,1043],{},"RateLimited"," middleware will be applied.",[16,1046,1048],{"id":1047},"builtin-middleware","Built‑in Middleware",[21,1050,1051],{},"Laravel provides several helpful middleware out of the box:",[32,1053,1054,1059,1065,1071],{},[35,1055,1056,1058],{},[104,1057,1043],{}," – limit how often a job runs based on a rate limiter you define.",[35,1060,1061,1064],{},[104,1062,1063],{},"WithoutOverlapping"," – prevent two jobs with the same “key” from running simultaneously.",[35,1066,1067,1070],{},[104,1068,1069],{},"ThrottlesExceptions"," – if a job fails repeatedly, stop trying for a while.",[35,1072,1073,1076],{},[104,1074,1075],{},"SkipIfBatchCancelled"," – for batch jobs, skip processing if the batch was cancelled.",[21,1078,1079],{},"Using middleware keeps your jobs clean and focused on their primary task.",[11,1081,1083],{"id":1082},"job-batching-managing-groups-of-jobs","Job Batching – Managing Groups of Jobs",[21,1085,1086],{},"Sometimes you need to run a set of jobs in parallel and then do something when they all finish. Job batching is perfect for this. You can think of it as a “parent” that tracks a group of jobs and notifies you when the group is complete.",[16,1088,1090],{"id":1089},"setting-up-batches","Setting Up Batches",[21,1092,1093],{},"First, create a table to store batch information:",[118,1095,1097],{"className":534,"code":1096,"language":536,"meta":123,"style":123},"php artisan make:queue-batches-table\nphp artisan migrate\n",[104,1098,1099,1108],{"__ignoreMap":123},[127,1100,1101,1103,1105],{"class":129,"line":130},[127,1102,122],{"class":154},[127,1104,545],{"class":410},[127,1106,1107],{"class":410}," make:queue-batches-table\n",[127,1109,1110,1112,1114],{"class":129,"line":141},[127,1111,122],{"class":154},[127,1113,545],{"class":410},[127,1115,1116],{"class":410}," migrate\n",[16,1118,1120],{"id":1119},"creating-a-batch","Creating a Batch",[21,1122,1123,1124,1127],{},"Use the ",[104,1125,1126],{},"Bus"," facade to define a batch of jobs:",[118,1129,1131],{"className":120,"code":1130,"language":122,"meta":123,"style":123},"use App\\Jobs\\ImportCsv;\nuse Illuminate\\Support\\Facades\\Bus;\n\n$batch = Bus::batch([\n    new ImportCsv(1, 100),\n    new ImportCsv(101, 200),\n    new ImportCsv(201, 300),\n])->then(function ($batch) {\n    // All jobs completed successfully\n})->catch(function ($batch, $e) {\n    // A job failed\n})->dispatch();\n",[104,1132,1133,1142,1151,1155,1174,1194,1212,1230,1246,1251,1268,1273],{"__ignoreMap":123},[127,1134,1135,1137,1140],{"class":129,"line":130},[127,1136,170],{"class":133},[127,1138,1139],{"class":137}," App\\Jobs\\ImportCsv",[127,1141,159],{"class":158},[127,1143,1144,1146,1149],{"class":129,"line":141},[127,1145,170],{"class":133},[127,1147,1148],{"class":137}," Illuminate\\Support\\Facades\\Bus",[127,1150,159],{"class":158},[127,1152,1153],{"class":129,"line":148},[127,1154,145],{"emptyLinePlaceholder":144},[127,1156,1157,1160,1163,1166,1168,1171],{"class":129,"line":162},[127,1158,1159],{"class":158},"$batch ",[127,1161,1162],{"class":133},"=",[127,1164,1165],{"class":137}," Bus",[127,1167,393],{"class":133},[127,1169,1170],{"class":154},"batch",[127,1172,1173],{"class":158},"([\n",[127,1175,1176,1179,1182,1184,1186,1188,1191],{"class":129,"line":167},[127,1177,1178],{"class":133},"    new",[127,1180,1181],{"class":137}," ImportCsv",[127,1183,252],{"class":158},[127,1185,847],{"class":137},[127,1187,356],{"class":158},[127,1189,1190],{"class":137},"100",[127,1192,1193],{"class":158},"),\n",[127,1195,1196,1198,1200,1202,1205,1207,1210],{"class":129,"line":178},[127,1197,1178],{"class":133},[127,1199,1181],{"class":137},[127,1201,252],{"class":158},[127,1203,1204],{"class":137},"101",[127,1206,356],{"class":158},[127,1208,1209],{"class":137},"200",[127,1211,1193],{"class":158},[127,1213,1214,1216,1218,1220,1223,1225,1228],{"class":129,"line":188},[127,1215,1178],{"class":133},[127,1217,1181],{"class":137},[127,1219,252],{"class":158},[127,1221,1222],{"class":137},"201",[127,1224,356],{"class":158},[127,1226,1227],{"class":137},"300",[127,1229,1193],{"class":158},[127,1231,1232,1235,1237,1239,1241,1243],{"class":129,"line":198},[127,1233,1234],{"class":158},"])",[127,1236,402],{"class":133},[127,1238,869],{"class":154},[127,1240,252],{"class":158},[127,1242,874],{"class":133},[127,1244,1245],{"class":158}," ($batch) {\n",[127,1247,1248],{"class":129,"line":203},[127,1249,1250],{"class":303},"    // All jobs completed successfully\n",[127,1252,1253,1256,1258,1261,1263,1265],{"class":129,"line":218},[127,1254,1255],{"class":158},"})",[127,1257,402],{"class":133},[127,1259,1260],{"class":154},"catch",[127,1262,252],{"class":158},[127,1264,874],{"class":133},[127,1266,1267],{"class":158}," ($batch, $e) {\n",[127,1269,1270],{"class":129,"line":224},[127,1271,1272],{"class":303},"    // A job failed\n",[127,1274,1275,1277,1279,1281],{"class":129,"line":235},[127,1276,1255],{"class":158},[127,1278,402],{"class":133},[127,1280,396],{"class":154},[127,1282,1283],{"class":158},"();\n",[21,1285,1286,1287,1290,1291,1294,1295,1298],{},"You can also add callbacks for when the batch is created (",[104,1288,1289],{},"before",") or for progress updates (",[104,1292,1293],{},"progress","). Each callback receives the ",[104,1296,1297],{},"Batch"," instance, which you can use to inspect the batch’s status.",[16,1300,1302],{"id":1301},"working-with-batches-inside-a-job","Working with Batches Inside a Job",[21,1304,1305,1306,1309],{},"Inside a batchable job, you can access the current batch via the ",[104,1307,1308],{},"batch()"," method:",[118,1311,1313],{"className":120,"code":1312,"language":122,"meta":123,"style":123},"use Illuminate\\Bus\\Batchable;\n\nclass ImportCsv implements ShouldQueue\n{\n    use Batchable;\n\n    public function handle()\n    {\n        if ($this->batch()->cancelled()) {\n            return;\n        }\n        // process a chunk...\n    }\n}\n",[104,1314,1315,1324,1328,1338,1342,1351,1355,1366,1370,1395,1402,1407,1412,1416],{"__ignoreMap":123},[127,1316,1317,1319,1322],{"class":129,"line":130},[127,1318,170],{"class":133},[127,1320,1321],{"class":137}," Illuminate\\Bus\\Batchable",[127,1323,159],{"class":158},[127,1325,1326],{"class":129,"line":141},[127,1327,145],{"emptyLinePlaceholder":144},[127,1329,1330,1332,1334,1336],{"class":129,"line":148},[127,1331,206],{"class":133},[127,1333,1181],{"class":154},[127,1335,212],{"class":133},[127,1337,215],{"class":154},[127,1339,1340],{"class":129,"line":162},[127,1341,221],{"class":158},[127,1343,1344,1346,1349],{"class":129,"line":167},[127,1345,227],{"class":133},[127,1347,1348],{"class":137}," Batchable",[127,1350,159],{"class":158},[127,1352,1353],{"class":129,"line":178},[127,1354,145],{"emptyLinePlaceholder":144},[127,1356,1357,1359,1361,1363],{"class":129,"line":188},[127,1358,243],{"class":133},[127,1360,246],{"class":133},[127,1362,282],{"class":154},[127,1364,1365],{"class":158},"()\n",[127,1367,1368],{"class":129,"line":198},[127,1369,297],{"class":158},[127,1371,1372,1375,1378,1381,1383,1385,1387,1389,1392],{"class":129,"line":203},[127,1373,1374],{"class":133},"        if",[127,1376,1377],{"class":158}," (",[127,1379,1380],{"class":137},"$this",[127,1382,402],{"class":133},[127,1384,1170],{"class":154},[127,1386,285],{"class":158},[127,1388,402],{"class":133},[127,1390,1391],{"class":154},"cancelled",[127,1393,1394],{"class":158},"()) {\n",[127,1396,1397,1400],{"class":129,"line":218},[127,1398,1399],{"class":133},"            return",[127,1401,159],{"class":158},[127,1403,1404],{"class":129,"line":224},[127,1405,1406],{"class":158},"        }\n",[127,1408,1409],{"class":129,"line":235},[127,1410,1411],{"class":303},"        // process a chunk...\n",[127,1413,1414],{"class":129,"line":240},[127,1415,316],{"class":158},[127,1417,1418],{"class":129,"line":264},[127,1419,322],{"class":158},[16,1421,1423],{"id":1422},"inspecting-batches","Inspecting Batches",[21,1425,1426],{},"You can retrieve a batch by its ID and return it from a route to show progress in your frontend:",[118,1428,1430],{"className":120,"code":1429,"language":122,"meta":123,"style":123},"Route::get(‘/batch/{batchId}’, function ($batchId) {\n    return Bus::findBatch($batchId);\n});\n",[104,1431,1432,1473,1488],{"__ignoreMap":123},[127,1433,1434,1437,1439,1442,1444,1447,1450,1452,1454,1457,1460,1463,1466,1468,1470],{"class":129,"line":130},[127,1435,1436],{"class":137},"Route",[127,1438,393],{"class":133},[127,1440,1441],{"class":154},"get",[127,1443,252],{"class":158},[127,1445,1446],{"class":137},"‘",[127,1448,1449],{"class":133},"/",[127,1451,1170],{"class":137},[127,1453,1449],{"class":133},[127,1455,1456],{"class":158},"{",[127,1458,1459],{"class":137},"batchId",[127,1461,1462],{"class":158},"}",[127,1464,1465],{"class":137},"’",[127,1467,356],{"class":158},[127,1469,874],{"class":133},[127,1471,1472],{"class":158}," ($batchId) {\n",[127,1474,1475,1478,1480,1482,1485],{"class":129,"line":141},[127,1476,1477],{"class":133},"    return",[127,1479,1165],{"class":137},[127,1481,393],{"class":133},[127,1483,1484],{"class":154},"findBatch",[127,1486,1487],{"class":158},"($batchId);\n",[127,1489,1490],{"class":129,"line":148},[127,1491,1492],{"class":158},"});\n",[21,1494,1495,1496,356,1499,356,1502,1505,1506,368],{},"The returned JSON contains properties like ",[104,1497,1498],{},"totalJobs",[104,1500,1501],{},"pendingJobs",[104,1503,1504],{},"failedJobs",", and ",[104,1507,1508],{},"progress()",[16,1510,1512],{"id":1511},"adding-jobs-to-an-existing-batch","Adding Jobs to an Existing Batch",[21,1514,1515],{},"Sometimes you don’t know all jobs upfront. You can dispatch a “loader” job that adds more jobs to the batch:",[118,1517,1519],{"className":120,"code":1518,"language":122,"meta":123,"style":123},"$this->batch()->add([\n    new ImportCsv(301, 400),\n    // ...\n]);\n",[104,1520,1521,1538,1556,1560],{"__ignoreMap":123},[127,1522,1523,1525,1527,1529,1531,1533,1536],{"class":129,"line":130},[127,1524,1380],{"class":137},[127,1526,402],{"class":133},[127,1528,1170],{"class":154},[127,1530,285],{"class":158},[127,1532,402],{"class":133},[127,1534,1535],{"class":154},"add",[127,1537,1173],{"class":158},[127,1539,1540,1542,1544,1546,1549,1551,1554],{"class":129,"line":141},[127,1541,1178],{"class":133},[127,1543,1181],{"class":137},[127,1545,252],{"class":158},[127,1547,1548],{"class":137},"301",[127,1550,356],{"class":158},[127,1552,1553],{"class":137},"400",[127,1555,1193],{"class":158},[127,1557,1558],{"class":129,"line":148},[127,1559,985],{"class":303},[127,1561,1562],{"class":129,"line":162},[127,1563,1564],{"class":158},"]);\n",[21,1566,1567],{},"Batching is ideal for tasks like importing large files, sending newsletters, or any workflow where you need to wait for multiple independent tasks to complete.",[11,1569,1571],{"id":1570},"unique-jobs-preventing-duplicate-work","Unique Jobs – Preventing Duplicate Work",[21,1573,1574],{},"There are scenarios where you absolutely do not want the same job to be queued twice while a previous instance is still running. For example, you might have a job that rebuilds a user’s search index. If the user triggers the rebuild multiple times in a row, you want only one to actually run.",[21,1576,1577,1578,1581],{},"Laravel provides the ",[104,1579,1580],{},"ShouldBeUnique"," interface for this.",[16,1583,1585],{"id":1584},"making-a-job-unique","Making a Job Unique",[21,1587,1588],{},"Implement the interface on your job class:",[118,1590,1592],{"className":120,"code":1591,"language":122,"meta":123,"style":123},"use Illuminate\\Contracts\\Queue\\ShouldBeUnique;\n\nclass UpdateSearchIndex implements ShouldQueue, ShouldBeUnique\n{\n    // ...\n}\n",[104,1593,1594,1603,1607,1624,1628,1632],{"__ignoreMap":123},[127,1595,1596,1598,1601],{"class":129,"line":130},[127,1597,170],{"class":133},[127,1599,1600],{"class":137}," Illuminate\\Contracts\\Queue\\ShouldBeUnique",[127,1602,159],{"class":158},[127,1604,1605],{"class":129,"line":141},[127,1606,145],{"emptyLinePlaceholder":144},[127,1608,1609,1611,1614,1616,1619,1621],{"class":129,"line":148},[127,1610,206],{"class":133},[127,1612,1613],{"class":154}," UpdateSearchIndex",[127,1615,212],{"class":133},[127,1617,1618],{"class":154}," ShouldQueue",[127,1620,356],{"class":158},[127,1622,1623],{"class":154},"ShouldBeUnique\n",[127,1625,1626],{"class":129,"line":162},[127,1627,221],{"class":158},[127,1629,1630],{"class":129,"line":167},[127,1631,985],{"class":303},[127,1633,1634],{"class":129,"line":178},[127,1635,322],{"class":158},[21,1637,1638],{},"Now if you dispatch this job while another instance is still in the queue (or processing), the new dispatch will be ignored.",[16,1640,1642],{"id":1641},"defining-the-uniqueness-key","Defining the Uniqueness Key",[21,1644,1645,1646,1309],{},"Often you want uniqueness based on some model ID. Override the ",[104,1647,1648],{},"uniqueId()",[118,1650,1652],{"className":120,"code":1651,"language":122,"meta":123,"style":123},"public function uniqueId()\n{\n    return $this->user->id;\n}\n",[104,1653,1654,1665,1669,1686],{"__ignoreMap":123},[127,1655,1656,1658,1660,1663],{"class":129,"line":130},[127,1657,255],{"class":133},[127,1659,246],{"class":133},[127,1661,1662],{"class":154}," uniqueId",[127,1664,1365],{"class":158},[127,1666,1667],{"class":129,"line":141},[127,1668,221],{"class":158},[127,1670,1671,1673,1676,1678,1681,1683],{"class":129,"line":148},[127,1672,1477],{"class":133},[127,1674,1675],{"class":137}," $this",[127,1677,402],{"class":133},[127,1679,1680],{"class":158},"user",[127,1682,402],{"class":133},[127,1684,1685],{"class":158},"id;\n",[127,1687,1688],{"class":129,"line":162},[127,1689,322],{"class":158},[21,1691,1692,1693,1696],{},"Now only one ",[104,1694,1695],{},"UpdateSearchIndex"," job per user ID will be queued at a time.",[16,1698,1700],{"id":1699},"uniqueness-timeout","Uniqueness Timeout",[21,1702,1703,1704,1707],{},"You can also specify how long the uniqueness lock should last using the ",[104,1705,1706],{},"UniqueFor"," attribute:",[118,1709,1711],{"className":120,"code":1710,"language":122,"meta":123,"style":123},"use Illuminate\\Queue\\Attributes\\UniqueFor;\n\n#[UniqueFor(3600)] // 1 hour\nclass UpdateSearchIndex implements ShouldQueue, ShouldBeUnique\n",[104,1712,1713,1722,1726,1744],{"__ignoreMap":123},[127,1714,1715,1717,1720],{"class":129,"line":130},[127,1716,170],{"class":133},[127,1718,1719],{"class":137}," Illuminate\\Queue\\Attributes\\UniqueFor",[127,1721,159],{"class":158},[127,1723,1724],{"class":129,"line":141},[127,1725,145],{"emptyLinePlaceholder":144},[127,1727,1728,1731,1733,1735,1738,1741],{"class":129,"line":148},[127,1729,1730],{"class":158},"#[",[127,1732,1706],{"class":137},[127,1734,252],{"class":158},[127,1736,1737],{"class":137},"3600",[127,1739,1740],{"class":158},")] ",[127,1742,1743],{"class":303},"// 1 hour\n",[127,1745,1746,1748,1750,1752,1754,1756],{"class":129,"line":162},[127,1747,206],{"class":133},[127,1749,1613],{"class":154},[127,1751,212],{"class":133},[127,1753,1618],{"class":154},[127,1755,356],{"class":158},[127,1757,1623],{"class":154},[21,1759,1760],{},"If the job hasn’t finished within that time, another job with the same key can be dispatched.",[16,1762,1764],{"id":1763},"unique-until-processing","Unique Until Processing",[21,1766,1767,1768,1771],{},"By default, the lock is released after the job finishes (or fails). If you want the lock to be released as soon as processing begins (so that subsequent jobs can queue behind), implement ",[104,1769,1770],{},"ShouldBeUniqueUntilProcessing"," instead.",[21,1773,1774],{},"Unique jobs are a simple but powerful way to avoid redundant work.",[11,1776,1778],{"id":1777},"queue-priorities-handling-important-work-first","Queue Priorities – Handling Important Work First",[21,1780,1781],{},"When you have different types of jobs, you may want some to be processed before others. Laravel lets you assign jobs to different “queues” (like “high” and “low”) and then tell the worker which queues to process in which order.",[16,1783,1785],{"id":1784},"dispatching-to-a-specific-queue","Dispatching to a Specific Queue",[118,1787,1789],{"className":120,"code":1788,"language":122,"meta":123,"style":123},"ProcessPodcast::dispatch($podcast)->onQueue('high');\n",[104,1790,1791],{"__ignoreMap":123},[127,1792,1793,1795,1797,1799,1801,1803,1805,1807,1810],{"class":129,"line":130},[127,1794,106],{"class":137},[127,1796,393],{"class":133},[127,1798,396],{"class":154},[127,1800,399],{"class":158},[127,1802,402],{"class":133},[127,1804,405],{"class":154},[127,1806,252],{"class":158},[127,1808,1809],{"class":410},"'high'",[127,1811,414],{"class":158},[16,1813,1815],{"id":1814},"running-a-worker-with-priority","Running a Worker with Priority",[21,1817,1818],{},"Start the worker with a comma‑separated list of queues, ordered by priority:",[118,1820,1822],{"className":534,"code":1821,"language":536,"meta":123,"style":123},"php artisan queue:work --queue=high,low\n",[104,1823,1824],{"__ignoreMap":123},[127,1825,1826,1828,1830,1833],{"class":129,"line":130},[127,1827,122],{"class":154},[127,1829,545],{"class":410},[127,1831,1832],{"class":410}," queue:work",[127,1834,1835],{"class":137}," --queue=high,low\n",[21,1837,1838,1839,1841,1842,1844,1845,1847,1848,368],{},"Now the worker will process all jobs from the ",[104,1840,420],{}," queue before moving to the ",[104,1843,424],{}," queue. If the ",[104,1846,420],{}," queue is empty, it will process jobs from ",[104,1849,424],{},[16,1851,1853],{"id":1852},"default-queue","Default Queue",[21,1855,1856,1857,1859,1860,1863],{},"You can set a default queue for a connection in ",[104,1858,367],{}," under the ",[104,1861,1862],{},"queue"," key. For example:",[118,1865,1867],{"className":120,"code":1866,"language":122,"meta":123,"style":123},"'redis' => [\n    'driver' => 'redis',\n    'queue' => 'default',\n    // ...\n],\n",[104,1868,1869,1880,1893,1905,1909],{"__ignoreMap":123},[127,1870,1871,1874,1877],{"class":129,"line":130},[127,1872,1873],{"class":410},"'redis'",[127,1875,1876],{"class":133}," =>",[127,1878,1879],{"class":158}," [\n",[127,1881,1882,1885,1887,1890],{"class":129,"line":141},[127,1883,1884],{"class":410},"    'driver'",[127,1886,1876],{"class":133},[127,1888,1889],{"class":410}," 'redis'",[127,1891,1892],{"class":158},",\n",[127,1894,1895,1898,1900,1903],{"class":129,"line":148},[127,1896,1897],{"class":410},"    'queue'",[127,1899,1876],{"class":133},[127,1901,1902],{"class":410}," 'default'",[127,1904,1892],{"class":158},[127,1906,1907],{"class":129,"line":162},[127,1908,985],{"class":303},[127,1910,1911],{"class":129,"line":167},[127,1912,1913],{"class":158},"],\n",[21,1915,1916],{},"Priorities are essential when you have time‑sensitive jobs (like sending password reset emails) that should not wait behind bulk imports.",[11,1918,1920],{"id":1919},"testing-queues-keeping-your-tests-fast","Testing Queues – Keeping Your Tests Fast",[21,1922,1923,1924,1926,1927,1930],{},"When testing code that dispatches jobs, you don’t want to actually run the jobs. That would slow down tests and might have side effects. Laravel provides a ",[104,1925,373],{}," facade with a ",[104,1928,1929],{},"fake()"," method that intercepts job dispatches.",[16,1932,1934],{"id":1933},"basic-fake-and-assertions","Basic Fake and Assertions",[118,1936,1938],{"className":120,"code":1937,"language":122,"meta":123,"style":123},"use Illuminate\\Support\\Facades\\Queue;\n\npublic function test_orders_are_shipped()\n{\n    Queue::fake();\n\n    // Call your code that dispatches a job...\n    // e.g., $this->post(‘/orders’, [...]);\n\n    Queue::assertPushed(ShipOrder::class);\n    Queue::assertPushedTimes(ShipOrder::class, 1);\n    Queue::assertPushedOn(‘shipping’, ShipOrder::class);\n}\n",[104,1939,1940,1949,1953,1964,1968,1980,1984,1989,1994,1998,2017,2038,2060],{"__ignoreMap":123},[127,1941,1942,1944,1947],{"class":129,"line":130},[127,1943,170],{"class":133},[127,1945,1946],{"class":137}," Illuminate\\Support\\Facades\\Queue",[127,1948,159],{"class":158},[127,1950,1951],{"class":129,"line":141},[127,1952,145],{"emptyLinePlaceholder":144},[127,1954,1955,1957,1959,1962],{"class":129,"line":148},[127,1956,255],{"class":133},[127,1958,246],{"class":133},[127,1960,1961],{"class":154}," test_orders_are_shipped",[127,1963,1365],{"class":158},[127,1965,1966],{"class":129,"line":162},[127,1967,221],{"class":158},[127,1969,1970,1973,1975,1978],{"class":129,"line":167},[127,1971,1972],{"class":137},"    Queue",[127,1974,393],{"class":133},[127,1976,1977],{"class":154},"fake",[127,1979,1283],{"class":158},[127,1981,1982],{"class":129,"line":178},[127,1983,145],{"emptyLinePlaceholder":144},[127,1985,1986],{"class":129,"line":188},[127,1987,1988],{"class":303},"    // Call your code that dispatches a job...\n",[127,1990,1991],{"class":129,"line":198},[127,1992,1993],{"class":303},"    // e.g., $this->post(‘/orders’, [...]);\n",[127,1995,1996],{"class":129,"line":203},[127,1997,145],{"emptyLinePlaceholder":144},[127,1999,2000,2002,2004,2007,2009,2012,2015],{"class":129,"line":218},[127,2001,1972],{"class":137},[127,2003,393],{"class":133},[127,2005,2006],{"class":154},"assertPushed",[127,2008,252],{"class":158},[127,2010,2011],{"class":137},"ShipOrder",[127,2013,2014],{"class":133},"::class",[127,2016,414],{"class":158},[127,2018,2019,2021,2023,2026,2028,2030,2032,2034,2036],{"class":129,"line":224},[127,2020,1972],{"class":137},[127,2022,393],{"class":133},[127,2024,2025],{"class":154},"assertPushedTimes",[127,2027,252],{"class":158},[127,2029,2011],{"class":137},[127,2031,2014],{"class":133},[127,2033,356],{"class":158},[127,2035,847],{"class":137},[127,2037,414],{"class":158},[127,2039,2040,2042,2044,2047,2049,2052,2054,2056,2058],{"class":129,"line":235},[127,2041,1972],{"class":137},[127,2043,393],{"class":133},[127,2045,2046],{"class":154},"assertPushedOn",[127,2048,252],{"class":158},[127,2050,2051],{"class":137},"‘shipping’",[127,2053,356],{"class":158},[127,2055,2011],{"class":137},[127,2057,2014],{"class":133},[127,2059,414],{"class":158},[127,2061,2062],{"class":129,"line":240},[127,2063,322],{"class":158},[21,2065,2066],{},"You can also pass a closure to make more specific assertions:",[118,2068,2070],{"className":120,"code":2069,"language":122,"meta":123,"style":123},"Queue::assertPushed(function (ShipOrder $job) use ($order) {\n    return $job->order->id === $order->id;\n});\n",[104,2071,2072,2096,2123],{"__ignoreMap":123},[127,2073,2074,2076,2078,2080,2082,2084,2086,2088,2091,2093],{"class":129,"line":130},[127,2075,373],{"class":137},[127,2077,393],{"class":133},[127,2079,2006],{"class":154},[127,2081,252],{"class":158},[127,2083,874],{"class":133},[127,2085,1377],{"class":158},[127,2087,2011],{"class":137},[127,2089,2090],{"class":158}," $job) ",[127,2092,170],{"class":133},[127,2094,2095],{"class":158}," ($order) {\n",[127,2097,2098,2100,2103,2105,2108,2110,2113,2116,2119,2121],{"class":129,"line":141},[127,2099,1477],{"class":133},[127,2101,2102],{"class":158}," $job",[127,2104,402],{"class":133},[127,2106,2107],{"class":158},"order",[127,2109,402],{"class":133},[127,2111,2112],{"class":158},"id ",[127,2114,2115],{"class":133},"===",[127,2117,2118],{"class":158}," $order",[127,2120,402],{"class":133},[127,2122,1685],{"class":158},[127,2124,2125],{"class":129,"line":148},[127,2126,1492],{"class":158},[16,2128,2130],{"id":2129},"faking-only-specific-jobs","Faking Only Specific Jobs",[21,2132,2133,2134,288],{},"If you want to let some jobs run normally while faking others, pass an array of class names to ",[104,2135,1929],{},[118,2137,2139],{"className":120,"code":2138,"language":122,"meta":123,"style":123},"Queue::fake([ShipOrder::class]); // only ShipOrder is faked, others run\n",[104,2140,2141],{"__ignoreMap":123},[127,2142,2143,2145,2147,2149,2152,2154,2156,2159],{"class":129,"line":130},[127,2144,373],{"class":137},[127,2146,393],{"class":133},[127,2148,1977],{"class":154},[127,2150,2151],{"class":158},"([",[127,2153,2011],{"class":137},[127,2155,2014],{"class":133},[127,2157,2158],{"class":158},"]); ",[127,2160,2161],{"class":303},"// only ShipOrder is faked, others run\n",[16,2163,2165],{"id":2164},"testing-job-chaining-and-batching","Testing Job Chaining and Batching",[21,2167,2168,2169,2172],{},"For chains and batches, use ",[104,2170,2171],{},"Bus::fake()"," instead:",[118,2174,2176],{"className":120,"code":2175,"language":122,"meta":123,"style":123},"Bus::fake();\n\n// Dispatch chain or batch...\n\nBus::assertChained([ProcessPodcast::class, OptimizePodcast::class]);\nBus::assertBatched(function ($batch) {\n    return $batch->jobs->count() === 3;\n});\n",[104,2177,2178,2188,2192,2197,2201,2225,2240,2267],{"__ignoreMap":123},[127,2179,2180,2182,2184,2186],{"class":129,"line":130},[127,2181,1126],{"class":137},[127,2183,393],{"class":133},[127,2185,1977],{"class":154},[127,2187,1283],{"class":158},[127,2189,2190],{"class":129,"line":141},[127,2191,145],{"emptyLinePlaceholder":144},[127,2193,2194],{"class":129,"line":148},[127,2195,2196],{"class":303},"// Dispatch chain or batch...\n",[127,2198,2199],{"class":129,"line":162},[127,2200,145],{"emptyLinePlaceholder":144},[127,2202,2203,2205,2207,2210,2212,2214,2216,2218,2221,2223],{"class":129,"line":167},[127,2204,1126],{"class":137},[127,2206,393],{"class":133},[127,2208,2209],{"class":154},"assertChained",[127,2211,2151],{"class":158},[127,2213,106],{"class":137},[127,2215,2014],{"class":133},[127,2217,356],{"class":158},[127,2219,2220],{"class":137},"OptimizePodcast",[127,2222,2014],{"class":133},[127,2224,1564],{"class":158},[127,2226,2227,2229,2231,2234,2236,2238],{"class":129,"line":178},[127,2228,1126],{"class":137},[127,2230,393],{"class":133},[127,2232,2233],{"class":154},"assertBatched",[127,2235,252],{"class":158},[127,2237,874],{"class":133},[127,2239,1245],{"class":158},[127,2241,2242,2244,2247,2249,2252,2254,2257,2260,2262,2265],{"class":129,"line":188},[127,2243,1477],{"class":133},[127,2245,2246],{"class":158}," $batch",[127,2248,402],{"class":133},[127,2250,2251],{"class":158},"jobs",[127,2253,402],{"class":133},[127,2255,2256],{"class":154},"count",[127,2258,2259],{"class":158},"() ",[127,2261,2115],{"class":133},[127,2263,2264],{"class":137}," 3",[127,2266,159],{"class":158},[127,2268,2269],{"class":129,"line":198},[127,2270,1492],{"class":158},[21,2272,2273],{},"Testing with fakes ensures your tests remain fast and focused on the dispatching logic, not the actual job execution.",[11,2275,2277],{"id":2276},"common-pitfalls-and-best-practices","Common Pitfalls and Best Practices",[21,2279,2280],{},"Even with a solid understanding, beginners often stumble on a few points. Here are some tips to avoid trouble.",[16,2282,2284],{"id":2283},"_1-restart-workers-after-deployments","1. Restart Workers After Deployments",[21,2286,2287,2288,2290],{},"Workers keep the application in memory. If you change code (e.g., in ",[104,2289,336],{},"), the running worker won’t see the changes. Always run:",[118,2292,2294],{"className":534,"code":2293,"language":536,"meta":123,"style":123},"php artisan queue:restart\n",[104,2295,2296],{"__ignoreMap":123},[127,2297,2298,2300,2302],{"class":129,"line":130},[127,2299,122],{"class":154},[127,2301,545],{"class":410},[127,2303,2304],{"class":410}," queue:restart\n",[21,2306,2307],{},"after deployment. The command sends a signal that causes workers to exit gracefully after their current job.",[16,2309,2311,2312,2315],{"id":2310},"_2-set-appropriate-retry_after-and-timeout-values","2. Set Appropriate ",[104,2313,2314],{},"retry_after"," and Timeout Values",[21,2317,2318,2319,2321,2322,2324],{},"In ",[104,2320,367],{},", each connection has a ",[104,2323,2314],{}," value. This is how long the queue driver waits before making a job available again if it’s not completed. If your job takes longer than this, the driver will think it failed and retry it, leading to duplicate processing.",[32,2326,2327,2333],{},[35,2328,2329,2330,2332],{},"Ensure ",[104,2331,2314],{}," is longer than your longest‑running job.",[35,2334,2335,2336,2339,2340,368],{},"Also set the worker timeout with ",[104,2337,2338],{},"--timeout"," – this should be less than ",[104,2341,2314],{},[21,2343,2344],{},"Example:",[32,2346,2347],{},[35,2348,2349,2350,2353,2354,368],{},"Job takes up to 90 seconds → set ",[104,2351,2352],{},"retry_after = 120"," and ",[104,2355,2356],{},"--timeout=100",[16,2358,2360],{"id":2359},"_3-use-the-database-queue-driver-for-development","3. Use the Database Queue Driver for Development",[21,2362,2363,2364,2353,2367,2370,2371,2373],{},"The database driver is the easiest to set up and inspect. Run ",[104,2365,2366],{},"php artisan make:queue-table",[104,2368,2369],{},"php artisan migrate",". You can then look at the ",[104,2372,2251],{}," table to see what’s pending.",[16,2375,2377],{"id":2376},"_4-handle-failures-gracefully","4. Handle Failures Gracefully",[21,2379,2380,2381,2383],{},"Always define a ",[104,2382,619],{}," method on jobs that might need cleanup or user notification:",[118,2385,2387],{"className":120,"code":2386,"language":122,"meta":123,"style":123},"public function failed(Throwable $exception): void\n{\n    Log::error(‘Job failed: ‘ . $exception->getMessage());\n    $this->podcast->update([‘status’ => ‘failed’]);\n}\n",[104,2388,2389,2410,2414,2450,2477],{"__ignoreMap":123},[127,2390,2391,2393,2395,2398,2400,2403,2406,2408],{"class":129,"line":130},[127,2392,255],{"class":133},[127,2394,246],{"class":133},[127,2396,2397],{"class":154}," failed",[127,2399,252],{"class":158},[127,2401,2402],{"class":137},"Throwable",[127,2404,2405],{"class":158}," $exception)",[127,2407,288],{"class":133},[127,2409,291],{"class":133},[127,2411,2412],{"class":129,"line":141},[127,2413,221],{"class":158},[127,2415,2416,2419,2421,2424,2426,2429,2431,2434,2436,2439,2442,2444,2447],{"class":129,"line":148},[127,2417,2418],{"class":137},"    Log",[127,2420,393],{"class":133},[127,2422,2423],{"class":154},"error",[127,2425,252],{"class":158},[127,2427,2428],{"class":137},"‘Job",[127,2430,2397],{"class":137},[127,2432,2433],{"class":158},": ",[127,2435,1446],{"class":137},[127,2437,2438],{"class":133}," .",[127,2440,2441],{"class":158}," $exception",[127,2443,402],{"class":133},[127,2445,2446],{"class":154},"getMessage",[127,2448,2449],{"class":158},"());\n",[127,2451,2452,2455,2457,2460,2462,2465,2467,2470,2472,2475],{"class":129,"line":162},[127,2453,2454],{"class":137},"    $this",[127,2456,402],{"class":133},[127,2458,2459],{"class":158},"podcast",[127,2461,402],{"class":133},[127,2463,2464],{"class":154},"update",[127,2466,2151],{"class":158},[127,2468,2469],{"class":137},"‘status’",[127,2471,1876],{"class":133},[127,2473,2474],{"class":137}," ‘failed’",[127,2476,1564],{"class":158},[127,2478,2479],{"class":129,"line":167},[127,2480,322],{"class":158},[16,2482,2484],{"id":2483},"_5-keep-jobs-small-and-focused","5. Keep Jobs Small and Focused",[21,2486,2487],{},"A job should do one thing. If you need multiple steps, consider chaining jobs or using batches. Large, complex jobs are harder to debug and retry.",[16,2489,2491],{"id":2490},"_6-monitor-your-queues","6. Monitor Your Queues",[21,2493,2494,2495,2498],{},"Schedule ",[104,2496,2497],{},"queue:monitor"," to detect when a queue gets too long. Combine it with notifications to stay ahead of issues.",[16,2500,2502],{"id":2501},"_7-beware-of-database-transactions","7. Beware of Database Transactions",[21,2504,2505,2506,1309],{},"If you dispatch a job inside a database transaction, the job may run before the transaction is committed, causing it to see stale data. Use the ",[104,2507,2508],{},"afterCommit()",[118,2510,2512],{"className":120,"code":2511,"language":122,"meta":123,"style":123},"ProcessPodcast::dispatch($podcast)->afterCommit();\n",[104,2513,2514],{"__ignoreMap":123},[127,2515,2516,2518,2520,2522,2524,2526,2529],{"class":129,"line":130},[127,2517,106],{"class":137},[127,2519,393],{"class":133},[127,2521,396],{"class":154},[127,2523,399],{"class":158},[127,2525,402],{"class":133},[127,2527,2528],{"class":154},"afterCommit",[127,2530,1283],{"class":158},[21,2532,2533,2534,2537,2538,368],{},"Or set the ",[104,2535,2536],{},"after_commit"," option in your queue connection configuration to ",[104,2539,2540],{},"true",[11,2542,2544],{"id":2543},"conclusion","Conclusion",[21,2546,2547],{},"Queues are one of the most powerful features of Laravel. They turn a slow, synchronous application into a fast, responsive one. Start simple – use the database driver, create a few jobs, run a worker manually, and then gradually explore advanced features like job middleware, batching, and custom retry strategies.",[21,2549,2550],{},"Here are some helpful tips:",[32,2552,2553,2561,2568,2575],{},[35,2554,2555,2556,2353,2558,2560],{},"Set up the database queue driver – run ",[104,2557,2366],{},[104,2559,2369],{}," to create the jobs table. This is the easiest driver to start with.",[35,2562,2563,2564,2567],{},"Use Supervisor in production to keep your workers alive. A typical ",[104,2565,2566],{},"laravel-worker.conf"," file runs 8 workers and restarts them automatically.",[35,2569,2570,2571,2574],{},"Monitor your queues – Laravel can dispatch a ",[104,2572,2573],{},"QueueBusy"," event when a queue exceeds a certain size. Use it to send alerts.",[35,2576,2577,2578,2581],{},"Testing – Use ",[104,2579,2580],{},"Queue::fake()"," in your tests to prevent real queue processing, and assert that jobs were dispatched.",[21,2583,2584],{},"Thanks for reading!",[2586,2587,2588],"style",{},"html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}",{"title":123,"searchDepth":141,"depth":141,"links":2590},[2591,2596,2604,2609,2616,2622,2627,2632,2642],{"id":13,"depth":141,"text":14,"children":2592},[2593,2594,2595],{"id":18,"depth":148,"text":19},{"id":29,"depth":148,"text":30},{"id":62,"depth":148,"text":63},{"id":87,"depth":141,"text":88,"children":2597},[2598,2599,2600,2601,2602,2603],{"id":98,"depth":148,"text":99},{"id":340,"depth":148,"text":341},{"id":428,"depth":148,"text":429},{"id":517,"depth":148,"text":518},{"id":567,"depth":148,"text":568},{"id":623,"depth":148,"text":624},{"id":664,"depth":141,"text":665,"children":2605},[2606,2607,2608],{"id":688,"depth":148,"text":689},{"id":940,"depth":148,"text":941},{"id":1047,"depth":148,"text":1048},{"id":1082,"depth":141,"text":1083,"children":2610},[2611,2612,2613,2614,2615],{"id":1089,"depth":148,"text":1090},{"id":1119,"depth":148,"text":1120},{"id":1301,"depth":148,"text":1302},{"id":1422,"depth":148,"text":1423},{"id":1511,"depth":148,"text":1512},{"id":1570,"depth":141,"text":1571,"children":2617},[2618,2619,2620,2621],{"id":1584,"depth":148,"text":1585},{"id":1641,"depth":148,"text":1642},{"id":1699,"depth":148,"text":1700},{"id":1763,"depth":148,"text":1764},{"id":1777,"depth":141,"text":1778,"children":2623},[2624,2625,2626],{"id":1784,"depth":148,"text":1785},{"id":1814,"depth":148,"text":1815},{"id":1852,"depth":148,"text":1853},{"id":1919,"depth":141,"text":1920,"children":2628},[2629,2630,2631],{"id":1933,"depth":148,"text":1934},{"id":2129,"depth":148,"text":2130},{"id":2164,"depth":148,"text":2165},{"id":2276,"depth":141,"text":2277,"children":2633},[2634,2635,2637,2638,2639,2640,2641],{"id":2283,"depth":148,"text":2284},{"id":2310,"depth":148,"text":2636},"2. Set Appropriate retry_after and Timeout Values",{"id":2359,"depth":148,"text":2360},{"id":2376,"depth":148,"text":2377},{"id":2483,"depth":148,"text":2484},{"id":2490,"depth":148,"text":2491},{"id":2501,"depth":148,"text":2502},{"id":2543,"depth":141,"text":2544},"Backend","/blog-covers/laravel-queues-explained-simply.png","2026-03-31","Learn how Laravel queues work and how to use them effectively in your applications using Laravel 13.","md",false,{},"/posts/laravel-queues-explained-simply","30 min read",{"title":5,"description":2646},{"loc":2650,"lastmod":2645},"posts/laravel-queues-explained-simply",[2656,2657,2658,2659],"laravel","queues","jobs processing","backend","A7itBn-psCn1V20fz0tcYtCGOjHTHKL1VRTT6Y793gQ",1777897734773]