Quick Start
Define a Job
A job holds its data and does its work in handle(). With ContainerJobCodec it implements PersistableJobInterface: jsonSerialize() returns the data to store, and createFromContainer() rebuilds the job from that data, taking its services from the container.
use Psr\Container\ContainerInterface;
use Solo\JobQueue\Codec\PersistableJobInterface;
final class SendEmailJob implements PersistableJobInterface
{
public function __construct(
private readonly string $to,
private readonly string $subject,
private readonly ?Mailer $mailer = null,
) {
}
public static function createFromContainer(ContainerInterface $container, array $data): self
{
return new self($data['to'], $data['subject'], $container->get(Mailer::class));
}
/** @return array{to: string, subject: string} */
public function jsonSerialize(): array
{
return ['to' => $this->to, 'subject' => $this->subject];
}
public function handle(): void
{
$this->mailer->send($this->to, $this->subject);
}
}Only data is stored, never services: the mailer comes from the container when a worker restores the job.
Enqueue
$id = $queue->push(new SendEmailJob('user@example.com', 'Welcome'), 'email');
// Not before an hour from now, and not at all after tomorrow.
$queue->push(
new SendEmailJob('user@example.com', 'Your trial ends soon'),
'email',
scheduledAt: new DateTimeImmutable('+1 hour'),
expiresAt: new DateTimeImmutable('+1 day'),
);
$count = $queue->pushMany($jobs, 'import');The second argument is the job's type: workers can be limited to one type, and statistics can be counted per type. Without one a job gets the type default. See Enqueueing.
Run a Worker
// bin/worker.php
use Solo\JobQueue\Worker\{Worker, WorkerLimits};
$worker = new Worker(
queue: $queue,
type: 'email',
limits: new WorkerLimits(maxJobs: 1000, maxRuntime: 3600, maxMemoryMb: 256),
logger: $logger,
);
$worker->run();The worker claims one job, runs it, acknowledges it, and repeats; it sleeps for a second when nothing is ready. SIGTERM and SIGINT let the current job finish before it exits, and so does reaching a limit, so run it under systemd, Supervisor or another process supervisor that starts it again. See Worker.
Or Process from Cron
Without a long-running process, a cron job can drain the queue every minute:
$queue->processJobs(100); // maintenance, then up to 100 jobs of any type
$queue->processJobs(100, 'email'); // only email jobsInspect and Retry
$stats = $queue->getStats('email');
// ['pending' => 7, 'in_progress' => 1, 'completed' => 192, 'failed' => 2, 'expired' => 0]
foreach ($queue->getFailedJobs(limit: 20, type: 'email') as $record) {
printf("#%d %s after %d attempts: %s\n", $record->id, $record->name, $record->attempts, $record->error);
}
$queue->retry($id); // a failed job runs again, with a fresh attempt budgetA job that fails is retried with backoff until its attempts run out; then it is failed. See Retries and Deadlines.
Clean Up
Completed and failed jobs stay in the table until you prune them:
use Solo\JobQueue\JobStatus;
$queue->prune(new DateTimeImmutable('-7 days')); // completed jobs finished over a week ago
$queue->prune(new DateTimeImmutable('-30 days'), [JobStatus::Failed, JobStatus::Expired]);Next Steps
- Processing and Leases — Claims, tokens and what happens when a worker dies
- Delivery and Errors — At-least-once delivery and idempotent handlers
- API Reference — Every method of
JobQueue