Queue
Introduction
A queue is a background job processing system that allows tasks to run asynchronously instead of blocking your main application flow. It improves performance, distributes workload efficiently, and ensures time-consuming tasks are handled reliably in the background. Queues are essential for scaling modern applications and maintaining smooth user experiences.
Doppar queue system offers robust features including multiple queue support for organizing jobs by priority or category, automatic retry logic with configurable attempts and delays, and comprehensive failed job tracking for easier debugging. It supports delayed execution for scheduling jobs in the future, graceful shutdown handling to safely stop workers, and built-in memory management that automatically restarts workers when limits are exceeded.
Features
The Doppar Framework queue system is designed to handle background tasks efficiently with reliability and scalability in mind. Its feature set ensures smooth job processing, better performance, and full control over how tasks are executed. See the features of doppar queue.
| Feature | Description |
|---|---|
| Multiple Queue Support | Organize jobs by priority and type. |
| Automatic Retry Logic | Configurable retry attempts with delays. |
| Failed Job Tracking | Store and analyze failed jobs for debugging. |
| Delayed Execution | Schedule jobs for future execution. |
| Graceful Shutdown | Handle SIGTERM and SIGINT signals safely. |
| Memory Management | Automatic worker restart when memory limits are exceeded. |
| Job Serialization | Safely serialize complex job data for storage in queues. |
| Fluent API | Fluent syntax for job dispatching and chaining. |
| Custom Failure Callbacks | Handle job failures gracefully at the job or chain level. |
| Job Chaining | Execute multiple jobs sequentially, where each job runs only after the previous one succeeds. |
| Chain-Level Callbacks | Define then() and catch() handlers for entire job chains. |
| Per-Job Execution Timeouts | Define maximum execution time per job using the #[Queueable(timeout:)] attribute. |
| Static and Instance Dispatching | Dispatch jobs via static methods or directly from job instances. |
| Synchronous Job Execution | Optionally execute jobs immediately using dispatchSync(). |
| Dynamic Queue Assignment | Assign jobs to queues dynamically at runtime. |
| Worker Options | Configure queue workers with --queue, --sleep, --memory, --timeout, and --limit. |
Installation
You may install Doppar Queue via the composer require command:
composer require doppar/queueRegister Provider
Next, register the Queue service provider so that Doppar can initialize it properly. Open your config/app.php file and add the QueueServiceProvider to the providers array:
'providers' => [
// Other service providers...
\Doppar\Queue\QueueServiceProvider::class,
],This step ensures that Doppar knows about Queue and can load its functionality when the application boots.
Publish Configuration
Now we need to publish the configuration files by running this pool command.
php pool vendor:publish --provider="Doppar\Queue\QueueServiceProvider"Now run migrate command to migrate queue related tables
php pool migrateThis creates two tables:
queue_jobs- Stores pending and processing jobsfailed_jobs- Stores failed jobs for debugging
Quick Start
Doppar makes it easy to create a new queue job using the pool command. For example, to generate a job for sending a welcome email, run:
php pool make:job SendWelcomeEmailJobThis will create a ready-to-use job class that you can customize and dispatch to your queue.
Dispatch the Job
Once your job class is ready, you can dispatch it to the queue like this:
(new SendWelcomeEmailJob($user))->dispatch();This sends the job to the queue for asynchronous processing, allowing your application to continue running without waiting for the task to complete.
Now update your newly create jobs as like this. By default you will get #[Queueable] as commented, uncomment it to use this job class as queueable.
<?php
namespace App\Jobs;
use Doppar\Queue\Job;
use Doppar\Queue\Dispatchable;
use Doppar\Queue\Attributes\Queueable;
use App\Models\User;
#[Queueable]
class SendWelcomeEmailJob extends Job
{
public function __construct(public User $user){}
/**
* Execute the job.
*
* @return void
*/
public function handle(): void
{
info("Email has been sent to $this->user->email");
}
/**
* Handle a job failure.
*
* @param \Throwable $exception
* @return void
*/
public function failed(\Throwable $exception): void
{
//
}
}Run the Worker
To start processing queued jobs, run the worker using the following command:
php pool queue:run💡 Your job class is now ready to work as a queueable job. However, if you remove or comment out the
#[Queueable]attribute, it will run as asynchronousjob, meaning it will execute immediately without being queued.
Customize #[Queueable] Attributes
You can customize how a job behaves in the queue by configuring the #[Queueable] attribute directly on the job class:
#[Queueable(
tries: 3,
retryAfter: 60,
delayFor: 300,
timeout: 60,
onQueue: 'email',
)]
class SendWelcomeEmailJob extends Job
{
//
}Now run the queue worker like this way
php pool queue:run --queue=emailUncomment and adjust these values as needed to control the job's queueing behavior.
Job Properties
Each job in Doppar queue can be configured with the following properties:
| Property | Type | Default | Description |
|---|---|---|---|
$tries | int | 3 | Maximum number of retry attempts. |
$retryAfter | int | 60 | Seconds to wait before retrying a failed job. |
$queueName | string | 'default' | The queue to which the job will be pushed. |
$jobDelay | int | 0 | Delay in seconds before executing the job. |
$timeout | int(second) | null | Allows each job to define its own maximum execution time. |
Per-Job Execution Timeouts
You can customize a job’s behavior using the #[Queueable] attribute. Each property can be used individually or combined, allowing you to fine-tune how a specific job is executed.
For example, the timeout property lets a job define its own maximum execution time, providing fine-grained control beyond the worker-level timeout.
use Doppar\Queue\Attributes\Queueable;
#[Queueable(timeout: 60)]
class SendEmailJob extends Job
{
public function handle(): void
{
// This job will timeout after 60 seconds
// If a job exceeds its defined execution timeout,
// It is considered as a failed job
}
}All available Queueable properties can be used together, partially, or individually, depending on the needs of the job.
Retry Jobs
Sometimes jobs may fail due to temporary issues, like network errors or external service downtime. Doppar queue allows you to automatically retry failed jobs a configurable number of times. You can specify the number of attempts for each job using the $tries property in your job class.
<?php
namespace App\Jobs;
use Doppar\Queue\Job;
class SendWelcomeEmailJob extends Job
{
/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries = 3;
}Retry Delay
You can control how long the queue should wait before retrying a failed job using the $retryAfter property. This allows temporary issues to resolve before the job is attempted again, preventing immediate repeated failures.
<?php
namespace App\Jobs;
use Doppar\Queue\Job;
class SendWelcomeEmailJob extends Job
{
/**
* The number of seconds to wait before retrying.
*
* @var int
*/
public $retryAfter = 60;
}With Queue and Delay
You can specify a custom queue and set a delay for job execution. This is useful when you want to separate jobs by type or control when they are processed.
(new ProcessVideo($videoPath))
->onQueue('videos')
->delayFor(300)
->dispatch();In this example, the job is pushed to the videos queue and will execute after a 300-second delay. To start processing this specific queue, run:
php pool queue:run --queue=videosUsing Static Helper
Doppar provides a convenient static helper to dispatch jobs immediately without manually instantiating them:
SendWelcomeEmail::dispatchNow($user);This creates a new instance of the SendWelcomeEmail job and pushes it to the queue in a single line. Ideal for quick dispatching when you don’t need to customize the job further.
Dispatch to a Specific Queue
You can assign a job to a particular queue using the dispatchOn method. This is useful for separating jobs by type or priority:
(new GenerateReport($data))->dispatchOn('reports');The job is sent to the reports queue, allowing you to run workers dedicated to specific queues for better control and efficiency.
Dispatch After a Delay
Jobs can be scheduled to run after a specific delay using dispatchAfter. This is perfect for sending reminders or delayed notifications:
(new SendReminder($user))->dispatchAfter(3600); // 1 hourHere, the job will be executed 1 hour later, without blocking your application or requiring manual scheduling.
Force Queue
To ensure a job is always pushed to the queue—regardless of whether it uses the #[Queueable] attribute—you can explicitly force it to queue:
(new GenerateReport($data))->forceQueue();Dispatch as Sync
To force a job to run immediately—without being queued—you can dispatch it synchronously:
SendReminder::dispatchSync($user);Dispatching with Static API
You can now dispatch jobs directly using static methods, eliminating the need to manually instantiate job objects. This makes the process simpler, more readable, and flexible.
$jobId = SendEmailJob::dispatchWith($user);The SendEmailJob will work as like
- If the job class uses the
#[Queueable]attribute, it will be queued - If the job class does not have the
#[Queueable]attribute, it will run synchronously
Dispatch Job Synchronously
You can force a job to run immediately, bypassing the queue, by using the queueAsSync method:
SendEmailJob::queueAsSync($user);The job executes instantly without being pushed to any queue.
Dispatch Job to a Specific Queue
You can force a job to always be queued on a specific queue using the queueOn method:
// Queue the job on the 'high-priority' queue
$jobId = SendEmailJob::queueOn('high-priority', $user);
// Queue another job on the 'reports' queue
$jobId = GenerateReport::queueOn('reports', $reportData);Dispatches the job to a specified queue, regardless of whether the job class has the #[Queueable] attribute. Ensures the job is always queued.
Dispatch Job with Delay
You can schedule a job to be queued after a specified delay using the queueAfter method:
// Queue the job to run 5 minutes later (300 seconds)
$jobId = SendEmailJob::queueAfter(300, $user);
// Queue another job to run 10 minutes later
$jobId = GenerateReport::queueAfter(600, $reportData);Dispatches the job to the queue after a delay (in seconds), regardless of the #[Queueable] attribute.
Job Chaining
Job Chaining allows you to execute multiple jobs sequentially, where each job runs only after the previous one succeeds. This makes it perfect for multi-step workflows where order and dependencies matter.
How Job Chaining Works
- A chain is created with a list of jobs.
- Only the first job is pushed to the queue.
- After each job completes, the next job in the chain is automatically dispatched.
- If any job fails:
- The chain stops immediately
- Remaining jobs are never executed
- Optional failure callbacks can handle errors globally for the chain
This ensures reliable sequential execution while avoiding partial state issues caused by failed intermediate jobs.
Basic Example
use Doppar\Queue\Drain;
Drain::conduct([
new DownloadJob($url),
new ProcessJob($path),
new UploadJob($file),
new NotifyJob($userId),
])->dispatch();What happens step by step
DownloadJobexecutes.ProcessJobexecutes only if DownloadJob succeeds.UploadJobexecutes only if ProcessJob succeeds.NotifyJobexecutes only if UploadJob succeeds.
Instance Method Chaining
Doppar allows you to create a job chain directly from a job instance using the chain() method. This can make your code more expressive, especially when starting from a specific job object.
$chainId = (new Job1())->chain([
new Job2(),
new Job3(),
])->dispatch();If any job fails, the chain stops immediately, and subsequent jobs are never executed.
Job Chaining With Configuration
Doppar allows you to customize job chains with queue options, delays, and other configurations before dispatching
use Doppar\Queue\Drain;
Drain::conduct([
new Job1(),
new Job2()
])
->onQueue('priority') // Specify the queue name
->delayFor(60) // Delay execution by 60 seconds
->dispatch();If any job fails, the chain stops, respecting failure handling rules as like before.
Job Chaining With Callbacks
Doppar allows you to attach global success and failure handlers to a job chain. This makes it easy to handle completion logic or errors at the chain level, without modifying individual jobs.
use Doppar\Queue\Drain;
Drain::conduct([
new Job1(),
new Job2(),
new Job3()
])
->onQueue('priority')
->delayFor(60)
->then(fn() => echo "Done!")
->catch(fn($job, $ex, $index) => Log::error($ex))
->dispatch();The catch callback is executed with:
$job— the job that failed$ex— the thrown exception$index— position of the failed job in the chain
If all jobs succeed, the then callback is executed.
Synchronous Job Chaining
By default, Doppar queues dispatch jobs asynchronously. Sometimes, you may want to execute a chain immediately, blocking the current process until all jobs complete.
use Doppar\Queue\Drain;
Drain::conduct([
new Job1(),
new Job2()
])
->dispatchSync(); // Blocks until all jobs completeNote:
dispatchSync()runs the chain immediately in the current process and blocks until all jobs complete.
Running the Queue Worker
To start processing jobs from the default queue, simply run:
php pool queue:runThe worker will continuously poll the queue and execute jobs as they arrive.
Custom Options
You can customize the worker’s behavior with the following options:
php pool queue:run --queue=emails --sleep=3 --memory=256 --timeout=3600 --limit=1000--queue– Process jobs from a specific queue (e.g., emails).--sleep– Number of seconds to wait between polling when no jobs are available.--memory– Maximum memory in MB before the worker automatically restarts.--timeout– Maximum execution time in seconds before the worker stops.--limit— Maximum number of jobs the worker will process before exiting
This allows you to run workers efficiently in production and tailor them to different job types or workloads.
Available Options
These options let you customize how the worker polls the queue, manages resources, and handles long-running tasks efficiently.
| Option | Description | Default |
|---|---|---|
--queue | Name of the queue to process | default |
--sleep | Seconds to wait when the queue is empty | 3 |
--memory | Maximum memory in MB before restarting | 128 |
--timeout | Maximum execution time in seconds | 3600 |
--limit | Maximum number of jobs the worker will process before exiting | unlimited |
Running Multiple Workers
You can run multiple workers simultaneously to process different queues in parallel. This is useful for prioritizing tasks or separating workloads:
# Terminal 1 – High priority queue
php pool queue:run --queue=high-priority &
# Terminal 2 – Default queue
php pool queue:run --queue=default &
# Terminal 3 – Low priority queue
php pool queue:run --queue=low-priority &Job Lifecycle
When using the Doppar queue system, every job follows a structured lifecycle from dispatch to completion. Understanding this lifecycle helps you manage retries, failures, and queue processing more effectively.
The following diagram illustrates how Doppar handles single jobs and job chains, from dispatch to completion or failure:
┌─────────────────────────────────────────────────────────────────┐
│ JOB DISPATCH LAYER │
└─────────────────────────────────────────────────────────────────┘
│
┌───────────┴───────────┐
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Single Job │ │ Job Chain │
│ Dispatch │ │ Drain │
└────────┬───────┘ └────────┬───────┘
│ │
│ │
└───────────┬───────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ QUEUE STORAGE - Database │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ queue_jobs: │ │
│ │ • id, queue, payload, attempts, reserved_at, │ │
│ │ available_at, created_at │ │
│ └──────────────────────────────────────────────────────────┘ │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ WORKER PROCESSING │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ • Pop job from queue │ │
│ │ • Reserve job - mark as processing │ │
│ │ • Increment attempts counter │ │
│ │ • Check others configuration like delayFor │ │
│ │ • Execute with timeout protection │ │
│ └────────────────────────────────────────────────────────┘ │
└────────────────────────────┬────────────────────────────────────┘
│
┌─────────┴─────────┐
│ │
SUCCESS FAILURE
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ Job Completed │ │ Job Failed │
│ Successfully │ │ with Exception │
└────────┬─────────┘ └────────┬─────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ 1. Delete from │ │ Check Retry │
│ queue_jobs │ │ Logic │
│ 2. Check if │ └────────┬─────────┘
│ chained │ │
└────────┬─────────┘ ┌────────┴────────┐
│ │ │
│ attempts < tries? │
│ │ │
│ ┌─────┘ └─────┐
│ │ YES NO │
│ ▼ ▼
│ ┌──────────────┐ ┌──────────────────┐
│ │ Release Job │ │ Mark as Failed │
│ │ Back to │ │ • Move to │
│ │ Queue with │ │ failed_jobs │
│ │ Delay │ │ • Delete from │
│ └──────────────┘ │ queue_jobs │
│ │ • Call failed() │
│ │ callback │
│ └──────────────────┘
│
┌─────────┴─────────┐
│ Is job chained? │
└─────────┬─────────┘
│
┌───────┴───────┐
│ YES │ NO
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Check Chain │ │ Job Complete │
│ Status │ │ End │
└──────┬───────┘ └──────────────┘
│
┌──────┴──────┐
│ More jobs │
│ in chain? │
└──────┬──────┘
│
┌──────┴──────┐
│ YES │ NO
▼ ▼
┌──────────┐ ┌──────────────────┐
│ Dispatch │ │ Chain Complete │
│ Next Job │ │ • Call then() │
│ →chainA │ │ callback │
│ →chainB │ │ • End │
└──────────┘ └──────────────────┘This diagram clearly shows the end-to-end lifecycle of single jobs and chained jobs, including retries, failures, and chain completion callbacks.
Dynamic Queue Assignment
You can assign a specific queue to a job by setting the $queueName property in the job class. This is useful for categorizing jobs by type or priority:
class SendInvoiceEmail extends Job
{
/**
* The name of the queue the job should be sent to.
*
* @var string
*/
public $queueName = 'billing';
// Job logic here...
}When dispatched, this job will automatically be pushed to the billing queue.
For jobs that need dynamic routing based on runtime conditions, you can use the onQueue() method:
(new ProcessOrder($order))
->onQueue($order->priority === 'high' ? 'urgent' : 'standard')
->dispatch();Here, the job will be sent to the urgent queue if the order is high priority, otherwise it goes to the standard queue. This allows flexible queue management depending on job-specific criteria.
Queue Commands
Doppar provides a set of CLI commands to manage and monitor your queues efficiently. These commands allow you to run workers, inspect failed jobs, retry or flush jobs, and view queue statistics.
Process Jobs
Processes jobs in the queue one by one. You can specify options such as queue name, sleep interval, memory limit, and execution timeout.
# Run the default queue
php pool queue:run
# Run a specific queue with custom options
php pool queue:run --queue=emails --sleep=3 --memory=256 --timeout=3600List Failed Jobs
Lists all jobs that have failed and been recorded in the failed jobs table. Useful for monitoring and debugging job issues.
# List all failed jobs
php pool queue:failedDelete Failed Jobs
Deletes failed jobs from the failed jobs table. You can delete a specific job by ID or all failed jobs if no ID is provided.
# Delete a specific failed job
php pool queue:flush 5
# Delete all failed jobs
php pool queue:flushRetry Failed Jobs
Retries failed jobs either by ID or all failed jobs if no ID is specified. Jobs will be pushed back to the queue for reprocessing.
# Retry a specific failed job
php pool queue:retry 3
# Retry all failed jobs
php pool queue:retryMonitor Queue Statistics
Displays statistics about the queues, including pending jobs, failed jobs, and active workers. Useful for monitoring queue health and performance.
# Monitor all queues
php pool queue:monitorProduction Setup
To run Doppar queue workers in production reliably, it’s recommended to use Supervisor to manage worker processes. Supervisor ensures that workers automatically restart if they fail and allows you to run multiple processes in parallel.
Create Supervisor Configuration
Create a file at /etc/supervisor/conf.d/queue-worker.conf with the following contents:
[program:queue-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/pool queue:run --sleep=3 --memory=256
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/www/html/storage/logs/worker.log
stopwaitsecs=3600Start Supervisor
Reload Supervisor to apply the new configuration and start the workers:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start queue-worker:*Monitor Workers
You can check the status of your Doppar queue workers managed by Supervisor with:
sudo supervisorctl status queue-worker:*This displays all running worker processes, their current state, and uptime.
Automated Monitoring
To keep track of queue statistics regularly, you can create a cron job that runs the Doppar queue monitor every 5 minutes:
*/5 * * * * php /var/www/html/pool queue:monitor >> /var/log/queue-monitor.logThis will append queue statistics, such as pending and failed jobs, to the log file for easy monitoring and analysis.
