Skip to content

v2.10.1

Choose a tag to compare

@bpacholek bpacholek released this 03 Oct 03:45
· 96 commits to main since this release

Patch release. One fix for what happens when the server closes a connection with a fatal -ERR, found while fixing symfony-nats-messenger's lost-connection bug: the operation that reads the error is now the only one to fail, instead of leaving the connection Open for the next operation to fail on as well.

Upgrading is recommended for anyone whose process can sit idle between operations - a PHP web app in worker mode between requests, a daemon between batches - since a server drops a client that stops answering its pings with -ERR 'Stale Connection'.

Upgrade notes

  • After a fatal -ERR from the server, the connection is no longer left Open: with reconnect off it is Closed by the time the read that met the -ERR throws, and with reconnect on that read first waits for the reconnect within its own timeout (a processIncoming() without a cancellation waits for the whole reconnect, as it already did after a failed read). The read still throws the same ConnectionException (Server sent error frame: ...). The same holds for a server PING whose PONG the socket would not take; that read still throws the socket's own error.

Fixed

  • A fatal -ERR from the server - the error it sends right before it closes the connection, such as Stale Connection for a client that stopped answering its pings - failed the operation that read it but left the connection Open (#171). The next operation then wrote into the socket the server had closed and failed as well: with reconnect off with Reconnect is disabled, with reconnect on only after waiting out its whole timeout, since its request had gone to the dead socket. Such a frame, and a PONG the socket would not take, now ends the connection like a failed read: it reconnects, or with reconnect off closes for good, and the read that met it fails with the server's error once the connection is Closed or, with reconnect on, once the reconnect is done or the read's own timeout runs out (not waiting at all with waitForReconnect: false). The heartbeat's read does the same. A full subscription queue still does not end the connection.

Quality gates

PHPStan level 8, 2342 unit tests (with data sets), 149 live integration tests, 48 Behat scenarios and every runnable example executed against a live server, 99.23% combined statement coverage (97% floor enforced in CI) and 94% Infection covered MSI over 7367 mutants (90% floor enforced in CI). Every part of the fix has a test that fails when that part alone is reverted, and mutation testing on the changed lines killed all 14 mutants (one by a test added for it). The fix was also verified through symfony-nats-messenger against nats-server 2.12 configured to drop idle clients. The full record is in the CHANGELOG.