Queue (Redis)

Note

Not part of core. Install it separately:

composer require kinetis/queue-redis

Adds Redis as a backend for Queue. Application code that already pushes and pops jobs through QueueInterface needs no changes at all to switch — only your configuration changes.

QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
vendor/bin/kinetis queue:work --queue=high,default

Configuring

This package introduces no configuration keys of its own — every REDIS_* setting RedisSimpleCache (Persistence) already reads is the exact one this backend reads too, scoped by QUEUE_CONNECTION_NAME the same way as everywhere else in Kinetis.

A crashed worker’s job is never lost

A naive Redis list pop() removes the item at pop time — if a worker crashed mid-job, it would just be gone, with no way to detect or retry it. This backend uses the “reliable queue” pattern instead: pop() atomically moves a job’s payload from a queue’s pending list to a separate processing list, rather than deleting it outright. ack() removes it from processing; release() moves it back onto pending. A job whose worker crashes before any of ack()/release()/fail() runs is stranded, not lost — it stays exactly where a future reaper would find it, though this backend doesn’t ship one yet: closing that gap is a disclosed, still-open limitation.

The two transitions that could otherwise lose a job outright — release() (processingpending) and delayed-job promotion (delayedpending) — each run as a single Lua script, which Redis always executes as one indivisible unit. A process crash can never land between the two halves of either move. release()’s move is also conditional, not just indivisible: calling it a second time with the same QueuedJob — a duplicate call, or a retry after a connection failure whose server-side outcome wasn’t known — throws Kinetis\Queue\Exception\StaleJobHandleException instead of enqueueing a second replacement; QueueWorker already treats this as a benign “already released through another path” outcome rather than letting it crash the worker.

Delayed-job promotion also bounds how much it moves in one call (RedisQueue::DELAYED_PROMOTION_BATCH_SIZE, currently 100) — a large ready backlog is promoted in batches across successive polls rather than inside one Lua script, since Redis executes one command at a time and an unbounded promotion would stall every other client sharing that Redis for its full duration.

Delayed jobs

$this->queue->push(new SendReminderEmail($userId), delaySeconds: 3600);

Checked on this backend’s own polling cycle rather than firing at the exact moment the delay ends, so a delayed job can run slightly later than its exact target time — typically by a few seconds, not less.

Retries and giving up

Everything Queue documents about maxAttempts, QUEUE_MAX_ATTEMPTS, and the log entry written when a job is finally given up on works identically here — nothing about retry behavior changes by switching to this backend.

Named connections

QUEUE_CONNECTION_NAME=reports
REDIS_REPORTS_HOST=127.0.0.1

Same convention as everywhere else in Kinetis (see Configuration): QUEUE_CONNECTION_NAME picks which named block of REDIS_* settings a worker reads, and 'default' (or simply not setting it) reads the plain keys shown earlier in this page.

If the package isn’t installed

Setting QUEUE_CONNECTION=redis without having run composer require kinetis/queue-redis produces a clear error telling you which package to install, rather than a confusing crash.

See also

  • Queue — writing jobs, pushing and popping, and everything about retries that applies to every backend equally.

  • Persistence — the REDIS_* configuration convention this backend reuses.

  • Configuration — the named-connection convention used above.