fix(queue): stop a failed ack pinning its message in flight - #199
Open
levivannoort wants to merge 1 commit into
Open
fix(queue): stop a failed ack pinning its message in flight#199levivannoort wants to merge 1 commit into
levivannoort wants to merge 1 commit into
Conversation
Broker\Nats::commit() acked and then dropped the message from $inFlight, so an ack that threw left the entry behind. ackSync() is a request-reply, so a transient failure raises rather than returning a verdict, and that is exactly when the unset was skipped. Nothing else clears it. reject() is the only other place that unsets, and Adapter::runPhases() deliberately does not reject after a commit failure -- the work is done, so a NAK there is a duplicate guaranteed rather than a duplicate risked. The entry is left with no owner, and a long-lived worker accumulates one pinned JetStreamMessage per failed ack. The map records that this instance owes an ack for the message, not that one succeeded, so the unset belongs in a finally. The throw still propagates: runPhases() reports it, which is the behaviour that decided not to reject. Closes #196.
levivannoort
requested review from
ChiragAgg5k,
abnegate and
lohanidamodar
as code owners
September 2, 2026 13:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #196.
Broker\Nats::commit()acked and then dropped the message from$inFlight:ackSync()is a request-reply, so a transient failure raises rather than returning a verdict — and that is precisely when theunsetwas skipped.Nothing else clears the entry.
reject()is the only other place that unsets, andAdapter::runPhases()deliberately does not reject after a commit failure — the work is done, so a NAK there is a duplicate guaranteed rather than a duplicate risked. The entry is left with no owner, and a long-lived worker accumulates one pinnedJetStreamMessageper failed ack.The fix
The map records that this instance owes an ack for the message, not that one succeeded, so the unset belongs in a
finally. The throw still propagates, becauserunPhases()reporting it is the behaviour that decided not to reject.Verification
NatsBrokerTest::testAnAckThatFailsDoesNotPinTheMessageInFlightcloses the connection under the ack — the cheapest stand-in for the transient failure this guards — and asserts the map is clear afterwards. Confirmed red before the fix and green after:The new test is in the e2e tier, which needs a JetStream server and cannot run on a host without ext-swoole (
tests/e2e.shboots Swoole workers), so it rests on CI — the red/green above was taken against a real server via the same steps the test performs.bin/monorepo check queue(pint, phpstan, rector) passes, and the unit tier is unchanged at 100 tests.Found by a review sweep over
mainwhile resolving #192; not introduced by it. Two other findings from that sweep are filed as #197 and #198.