Skip to content

v2.13.0

Choose a tag to compare

@bpacholek bpacholek released this 07 Oct 00:19
· 57 commits to main since this release

Minor release. A read that throws a subscription handler's exception now delivers the other subscriptions' messages behind it first (#177). Before, the exception ended the whole delivery, and everything the read had queued behind the failing message, for every subscription, waited for the next read that received bytes from the server. An operation in another fiber whose result was among those messages got no wake-up and waited for its deadline: on a real server next() returned nothing after 3 s with its message queued the whole time, and request() timed out. Both now get their result at once.

Upgrading is recommended if your subscription handlers can throw and you read the connection from more than one fiber, or poll, request or fetch beside a processIncoming() loop. There are no API changes; one documented behaviour of your own read changes, below. symfony-nats-messenger 5.4.1 works with this release unchanged.

Upgrade notes

  • Your own read delivers the other subscriptions' messages before it throws. processIncoming() and readIncoming() still throw a handler's exception, and still stop delivering to the subscription whose handler threw: its later messages stay queued, in order, for the next read that delivers, which throws again if that message's handler throws, so each failing message still surfaces as its own exception. The messages of the other subscriptions behind the failing one are now delivered, in subscription order, before the exception reaches you. Code that assumed nothing behind a throwing handler ran before it saw the exception sees those handlers run first, and a handler that awaits among them delays the exception.
  • Two subscriptions failing in one read. The first exception is thrown; the second is passed to the errorListener and logged at error level, as a second frame failure or a second overflow in one read already was. Register an errorListener or a logger to see it.
  • handlerErrorsFailOperations: true. An operation's read that meets another subscription's throwing handler still fails the operation with that exception, as the option promises, but delivers the other subscriptions' messages behind it first. A service's request read behind a poisoned message is answered by that read.

Fixed

  • Messages left queued behind a throwing handler (#177). A handler's exception used to end the read's delivery at once, and every message queued behind it waited for the next read that received bytes, up to the operation's deadline for an operation waiting on one of them. The read now holds the exception, delivers the other subscriptions' messages, which fires an operation's wake-up as any delivery does, and throws once the pass is over. Only the failing subscription's own later messages stay queued. The serving loop's delivery of what another read has queued and not reached stays, for a delivery held up in an awaiting handler.

Quality gates

  • PHPStan level 8.
  • 2705 unit tests (with data sets) on PHP 8.2 to 8.5, 149 live integration tests and 48 Behat scenarios.
  • All 45 runnable examples executed against a live server.
  • Statement coverage 99.05% from the unit suite (95% floor enforced in CI) and 99.11% combined (97% floor).
  • Infection covered MSI 93.7% over the 3836 mutants it ran (90% floor enforced in CI); it skipped 3891 of the 7727 it generated as too slow, so the score does not include them.
  • Every CI job passed on the first attempt. Every fix has tests that fail on the old code (23 new ones): the other subscriptions' messages were not delivered, and the operations waiting on them returned at their deadline. On a real server with AmpSocketTransport, next() returned its message in 1 ms where it returned nothing at 2.98 s before, and request() its reply in 3 ms where it timed out at 3 s.
  • The change was reviewed for semantics and for interleavings by two independent reviewers, and their findings were applied.
  • symfony-nats-messenger 5.4.1's suites pass against this release: PHPStan level max, 528 unit tests, 52 functional scenarios against live NATS and 5 examples.

The full record is in the CHANGELOG.