পাঠ্যক্রম খুলুন
মধ্যবর্তী পর্যায়

Laravel Queue: job, retry ও timeout

Laravel Jobs, Queues, and Retries

এই পাঠের কুইজ ↓৬ মিনিট পড়াPHP 8.3+ · Laravel 12
এই পাঠের সূচিপত্র

এই পাঠে যা শিখবেন

  • Synchronous request থেকে job আলাদা করা
  • Laravel retries ও timeout design
  • Duplicate delivery-তে idempotent processing

সমস্যা দিয়ে শুরু

Order create-এর response দিতে invoice PDF বানানো পর্যন্ত অপেক্ষা করলে user ধীর response পান। PDF generation ব্যর্থ হলেই order কি ব্যর্থ হবে? অধিকাংশ ক্ষেত্রে order স্থায়ী রেখে invoice পরে বানানো যায়। Queue এই কাজের সময় ও execution context আলাদা করে।

সহজ করে বুঝি

Producer queue-তে job দেয়, worker পরে job reserve করে কাজ চালায়। সফল হলে acknowledge বা delete হয়। Worker crash করলে job আবার available হতে পারে। তাই “একবার dispatch করেছি” মানে “একবারই execute হবে” নয়। At-least-once ধরনের delivery ধরে idempotent handler design করা নিরাপদ শুরু।

Laravel-এ job class ShouldQueue implement করে, queue connection backend নির্ধারণ করে এবং worker আলাদা process-এ চলে। QUEUE_CONNECTION=sync হলে কাজ request-এর মধ্যেই চলবে; async শেখার জন্য database বা Redis backend ও worker দরকার।

পরিচিত একটি উপমা

একটি ছাপাখানার counter print request নিয়ে token দেয়। Printer operator পরে তালিকা থেকে কাজ করেন। Operator মাঝপথে চলে গেলে অন্যজন একই token আবার দেখতে পারেন। Print output-এর stable file name এবং completion record থাকলে duplicate কাজের ক্ষতি কমানো যায়।

নকশাটি দেখুন

Diagram লোড হচ্ছে…

Worker crash-এর পরে পুনরায় execution সম্ভব। Stable output ও completion state handler-কে retry-safe করতে সাহায্য করে।
Diagram-এর text flow দেখুন
flowchart TD
 A[Order transaction commits] --> B[Dispatch invoice job]
 B --> Q[(Redis Queue)]
 Q --> W[Laravel Worker]
 W --> F[Write stable invoice path]
 F --> D[(Mark invoice generated)]
 W -->|Failure and retry| Q

Order commit-এর পরে dispatch করুন। Worker order reload করে বর্তমান state দেখবে। কাজ ইতোমধ্যে শেষ হলে ফিরে যাবে। এখানে illustration হিসেবে deterministic text invoice লেখা হচ্ছে, PDF renderer নয়। Concurrent duplicate ও crash-এর ক্ষেত্রে file write পুনরায় হতে পারে; content এবং path একই রাখায় ফল overwrite-safe।

PHP ও Laravel-এ প্রয়োগ

নিচের সম্পূর্ণ job class-এ Order model-এ invoice_generated_at field এবং local disk configuration আছে ধরে নেওয়া হয়েছে। Invoice renderer বা mail provider নিজে যোগ করা হয়নি। Private storage disk ব্যবহার করুন; customer access authorization দিয়ে দিন।

php
namespace App\Jobs;

use App\Models\Order;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Storage;

class GenerateInvoice implements ShouldQueue
{
    use Queueable;

    public int $tries = 3;
    public int $timeout = 30;

    public function __construct(public int $orderId) {}

    public function backoff(): array
    {
        return [10, 30];
    }

    public function handle(): void
    {
        $order = Order::query()->findOrFail($this->orderId);
        if ($order->invoice_generated_at !== null) {
            return;
        }

        $path = "invoices/{$order->id}.txt";
        $written = Storage::disk('local')->put($path, "Order #{$order->id}");
        if ($written === false) {
            throw new \RuntimeException('Invoice file could not be written.');
        }
        $order->forceFill(['invoice_generated_at' => now()])->save();
    }
}

Storage::put failure-এ false ফিরতে পারে; তাই false হলে exception তুলে job fail করা হয়েছে। Disk-এর throw option চালু থাকলে filesystem নিজেই exception তুলতে পারে। কোনো failure-ই success হিসেবে invoice_generated_at update করবে না। $tries = 3 মানে প্রথম execution-সহ সর্বোচ্চ তিনটি attempt; exception-এর পরে retry হলে অপেক্ষা যথাক্রমে ১০ ও ৩০ সেকেন্ড। Worker বন্ধ থাকা বা backlog থাকলে execution আরও দেরি হতে পারে। Manual retry নতুন execution শুরু করতে পারে।

সুবিধা, সীমাবদ্ধতা ও trade-off

Queue request latency কমায় এবং worker capacity আলাদা scale করতে দেয়। তবে completion পরে হয়, queue backlog ও failure দেখতে হয়। Job timeout backend retry_after-এর চেয়ে কম রাখুন, যাতে পুরোনো worker চলতে থাকা অবস্থায় অন্য worker একই job না পায়। Timeout-এর পরে external side effect হয়েছে কি না অনিশ্চিত থাকতে পারে। Redis/database connection-এ retry_after প্রযোজ্য; SQS-এ visibility timeout configure করতে হয়। Worker timeout-এর জন্য PHP pcntl extension দরকার এবং blocking I/O-এর নিজস্ব timeout-ও নির্ধারণ করুন।

সাধারণ ভুল

  • Worker না চালিয়ে async job হবে ধরে নেওয়া।
  • সব retry-তে একই payment আবার charge করা।
  • Failed jobs শুধু জমতে দেওয়া, alert বা recovery process না রাখা।
  • Shared output দরকার হলেও local disk-কে সব worker-এর common disk ভাবা।

আরও একটু গভীরে

এই invoice example-এর overwrite-safe file operation email বা payment-এর general solution নয়। Provider idempotency key, unique database operation record ও reconciliation লাগতে পারে। Order commit এবং broker dispatch-এর মাঝে process crash-এর gap-ও থাকে; afterCommit সেই gap পুরো বন্ধ করে না। Durable handoff দরকার হলে transactional outbox শিখুন।

মনে রাখুন

  • Job একাধিকবার চলতে পারে ধরে design করুন।
  • Backend, worker ও failure policy তিনটিই configure করতে হয়।
  • Retry-safe state change এবং external side effect আলাদা করে ভাবুন।

নিজে চেষ্টা করুন

একই order ID দিয়ে GenerateInvoice job দুবার চালিয়ে final path ও DB state পরীক্ষা করুন। File লেখা ও database save-এর মাঝখানে crash হলে retry কী করবে লিখুন। তারপর email পাঠানোতে একই strategy কেন যথেষ্ট নয় ব্যাখ্যা করুন।

নিজের বোঝা যাচাই করুন

1. dispatch করলেই async?

না। sync connection হলে request-এর মধ্যেই execute হয়। Async backend ও worker দরকার।

2. Job আবার কেন চলতে পারে?

Worker crash, acknowledgement হারানো, reservation expiry বা retry-এর কারণে।

3. timeout আর retry_after-এর সম্পর্ক কী?

Job/worker timeout যথেষ্ট কম হতে হবে, যাতে backend job পুনরায় available করার আগে পুরোনো execution বন্ধ হয়।

4. afterCommit কি durable outbox?

না। Commit-এর আগে job দেখা বন্ধ করতে সাহায্য করে; commit ও broker dispatch-এর crash gap নিজে বন্ধ করে না।

5. Idempotent মানে কোনো কাজই দুবার হবে না?

না। Execution পুনরাবৃত্তি হলেও observable business effect একই থাকবে—এই লক্ষ্য।

আরও পড়ুন

এই পাঠের কুইজ

এই পাঠ থেকে ২টি প্রশ্ন। উত্তর দিয়ে ব্যাখ্যা দেখুন; অধ্যায়ের কুইজ আলাদাভাবে দিতে হবে।

এই পাঠের ২টি প্রশ্নের উত্তর দিন। পাস করতে দুটিই সঠিক হতে হবে।

জমা দেওয়ার পর সঠিক উত্তর ও ব্যাখ্যা দেখবেন। ফলাফল এই ব্রাউজারে সংরক্ষিত থাকবে। অনুশীলনের ফল অধ্যায় সম্পন্নের হিসাবে যোগ হয় না।

সব বিষয় মিলিয়ে অনুশীলন করুন →

অগ্রগতি এই ব্রাউজারে সংরক্ষিত থাকে। শুধু পাঠ খুললে সম্পন্ন হয় না।