Skip to content

fix(microservices): retry the tcp server again after close and listen - #18238

Merged
kamilmysliwiec merged 1 commit into
masterfrom
fix/microservices-tcp-retry-after-relisten
Oct 22, 2026
Merged

kamilmysliwiec merged 1 commit into
masterfrom
fix/microservices-tcp-retry-after-relisten

Conversation

@kamilmysliwiec

Copy link
Copy Markdown
Member

What is the current behavior?

ServerTCP#close() sets isManuallyTerminated, and nothing ever resets it. If an instance is closed and then listened again, handleClose() returns early forever, so an unexpected close of the server never re-listens, whatever retryAttempts is set to.

I reproduced this on a real socket: listen(), then close(), then listen(), then unwrap<net.Server>().close(). The server stays down.

This came up while reviewing #18223, which handles the stale retry timer in the same restart path.

What is the new behavior?

listen() resets isManuallyTerminated, as it already resets the attempt count and the pending timer.

Resetting the flag alone is not enough. A close() emits close only once its open connections end, and that can be after the following listen(). The stale event would then arm a retry that calls listen on a server that is already listening, throwing ERR_SERVER_ALREADY_LISTEN from the timer. The same real-socket probe crashed this way when only the flag was reset. To prevent that:

  • handleClose() doesn't arm a retry while the server is listening.
  • The retry timer doesn't call listen once the server is listening again.

With both checks, the probe has three cases, and all three re-listen after the external close without an error:

  1. close, wait, listen;
  2. close and listen in the same tick;
  3. close with a connection kept open.

Tests

Three unit tests:

  • a listen() after close() arms a retry again;
  • no retry on a close event while the server is listening;
  • a pending retry doesn't call listen once the server listens again.

All three fail on master. The microservices unit suite passes (1382 tests).

Does this PR introduce a breaking change?

  • Yes
  • No

close() turns retries off, and a later listen() never turned them back on, so a ServerTCP instance that was closed and listened again no longer re-listened after an unexpected close. listen() now turns them back on. The close event of the earlier close() can arrive after that listen(); a retry is not armed, or does not listen, while the server is listening, which would throw ERR_SERVER_ALREADY_LISTEN from the retry timer.
@kamilmysliwiec
kamilmysliwiec merged commit 063ca42 into master Oct 22, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant