Repository navigation
6.0.0
Three changes, two of them behavioural. Nothing in this release announces itself through the type system except the first, so read the second and third before upgrading.
Publisher\Synchronous::getFailedCount()
New method on the interface. It reports everything a queue could not get through — on Redis the failed, dead and poison lists together, on NATS the dead stream.
getQueueSize() keeps its name and signature and delegates to it, so no caller has to move. Implementers do: any class implementing Publisher\Synchronous needs the new method.
Counting only the failed list answered zero through exactly the incidents the other lists exist to record — a handler declaring work permanently impossible, or a codec change leaving envelopes nobody can decode — while Broker\Nats answered the same call with its dead stream.
This changes what
$publisher->getSize(failed: true)returns. A deployment with old dead letters will see that number rise on upgrade without a single new failure, and anything asserting a threshold against it — Appwrite's/v1/health/queue/failed/{name}endpoint does — can flip to unhealthy on the strength of the upgrade alone. Check your thresholds before rolling this out.
A type error out of a handler is terminal
TypeError and ValueError escaping a handler are now treated as permanent, without the handler saying so. The payload and the signature disagree, and they will disagree identically on every delivery — every attempt after the first spends the redelivery budget to reach the conclusion the first one already reached.
On JetStream that is not merely wasteful: a rejected message holds one of the consumer's maxAckPending slots for the whole of its backoff, so enough of them leave the consumer no slot to deliver into and the queue stops for the healthy work behind them. Measured on a staging fleet: 701 messages whose payload carried an object where the handler constructs from an array each burned maxDeliver=5 over ~22 minutes of backoff while holding one of 60 slots, and that worker delivered nothing else for hours.
Deliberately narrow. Every other \Error keeps its budget — OutOfMemoryError and the stack overflow say the host was short at that moment, not that the work is impossible, and that is what redelivery is for.
A terminal message on Redis stays where the sweep can reach it
Broker\Redis::reject() no longer routes a terminal message to the dead list. It stays on failed, with every other rejection.
The verdict buys an ack slot back, and Redis has no such ceiling to protect — a rejected message is already out of the way there, and nothing re-runs it until an operator sweeps. Routing it to dead only put it beyond retry(), which reads failed and is the only recovery path that exists. Broker\Nats is unchanged: it still dead-letters at once, onto a stream retry() can re-drive.
This makes a codec migration recoverable on both transports: flip the writer, let the payloads that no longer fit their signatures fail fast, fix the handlers, re-drive.
Upgrading
- Add
getFailedCount()to any class implementingPublisher\Synchronous. - Re-check any threshold or alert built on a failed-queue count.
- No caller of
getQueueSize(),publish(),publishMany(),retry()or theConsumerinterface needs to change. - fix(queue): age the job before asserting the sweep re-drives it
- Merge remote-tracking branch 'origin/main' into fix/queue-type-errors-are-terminal
- fix(queue)!: a terminal verdict must not outrun the recovery a broker has
- test(queue): assert the retry sweep's own list, not the summed read
- test(queue): prove the failed read over real storage, and fix what it broke
- test(queue): assert the verdict where the message lands, not on the flag
- refactor(queue): decide the verdict where it is used
- refactor(queue): name the failed read, and keep getQueueSize as it is
- refactor(queue)!: split the depth read into pending and failed counts
- Merge remote-tracking branch 'origin/main' into feat/queue-size-poison
- fix(queue)!: a type error is terminal, with no switch to turn it on
- Merge branch 'main' into fix/queue-type-errors-are-terminal
- fix(queue): make the terminal verdict opt-in, so nothing changes on upgrade
- fix(queue): non-static helper, and drop the branch TypeError already covers
- fix(queue): a type error is not worth a redelivery budget
- fix(queue): count everything a queue could not get through