Skip to content

DbQueueDriver

PDO-backed PollableQueueDriverInterface.

Portable across PostgreSQL and SQLite (no SERIAL/AUTOINCREMENT, no FOR UPDATE SKIP LOCKED) — id/reserved_token are random hex strings rather than an autoincrement key, following PdoRateLimiterStorage’s portability approach.

reserve() claims a row via an UPDATE-then-SELECT-by-token pair rather than SELECT ... FOR UPDATE SKIP LOCKED, so it works on both backends; under heavy concurrent polling on PostgreSQL this is “reasonably safe”, not provably race-free — acceptable for v1, a documented limitation rather than a silent one.

Schema (see DbQueueDriver::schema()): CREATE TABLE quiote_queue_jobs ( id VARCHAR(32) PRIMARY KEY, job_class VARCHAR(255) NOT NULL, params TEXT NOT NULL, attempts INTEGER NOT NULL DEFAULT 0, available_at INTEGER NOT NULL, reserved_at INTEGER NULL, reserved_token VARCHAR(32) NULL );

final readonly class DbQueueDriver implements PollableQueueDriverInterface

ImplementsPollableQueueDriverInterface
SourceDbQueueDriver.php

public function __construct(PDO $pdo, string $table = 'quiote_queue_jobs', ClockInterface $clock = new SystemClock(…), RandomnessInterface $randomness = new SystemRandomness(…)): mixed

ParameterTypeDescription
$pdoPDO
$tablestring
$clockClockInterface
$randomnessRandomnessInterface

Returns mixed

MethodDescription
ack(ReservedJob $job): voidDeletes the job’s row, so a successfully processed job is never served again.
discard(ReservedJob $job): voidDeletes the job’s row after its retries are exhausted.
push(JobPayload $payload): voidInserts the job as an unreserved row with a fresh random id.
release(ReservedJob $job, int $delaySeconds): voidClears the reservation and makes the job due again after the delay.
reserve(): ?ReservedJobClaims the oldest due, unreserved row and returns it as a reserved job.
schema(string $table = 'quiote_queue_jobs'): stringDDL to create the backing table (PostgreSQL / SQLite compatible).

public function ack(ReservedJob $job): void

Deletes the job’s row, so a successfully processed job is never served again.

ParameterTypeDescription
$jobReservedJob

public function discard(ReservedJob $job): void

Deletes the job’s row after its retries are exhausted.

Identical in effect to DbQueueDriver::ack(); the dead-letter record has already been written by JobExecutor.

ParameterTypeDescription
$jobReservedJob

public function push(JobPayload $payload): void

Inserts the job as an unreserved row with a fresh random id.

Params are stored as JSON, so they must be JSON-serializable. The row’s available_at is JobPayload::$availableAt when set and the current time otherwise, which is what keeps a delayed job invisible to DbQueueDriver::reserve() until it is due.

ParameterTypeDescription
$payloadJobPayload
ThrowsWhen
JsonExceptionif the payload params cannot be encoded.

public function release(ReservedJob $job, int $delaySeconds): void

Clears the reservation and makes the job due again after the delay.

The stored attempt count is incremented, so a released job carries its retry history forward. A negative $delaySeconds is clamped to zero, making the job immediately due.

ParameterTypeDescription
$jobReservedJob
$delaySecondsint

public function reserve(): ?ReservedJob

Claims the oldest due, unreserved row and returns it as a reserved job.

The claim stamps a random token and reserved_at onto exactly one row via UPDATE, then reads that row back by token. Returns null when the UPDATE matched nothing — the backlog holds no row that is both due (available_at <= now) and unreserved — or when the read-back finds no row for the token because another connection has since removed it.

Returns ?ReservedJob

ThrowsWhen
RuntimeExceptionif the stored job_class does not implement Job, or a column has an unusable type.
JsonExceptionif the stored params are not valid JSON.

public static function schema(string $table = 'quiote_queue_jobs'): string

DDL to create the backing table (PostgreSQL / SQLite compatible).

ParameterTypeDescription
$tablestring

Returns string