Skip to content

fix(node): close the api service on shutdown - #5671

Open
martinconic wants to merge 2 commits into
masterfrom
fix/node-shutdown-api-service-close
Open

martinconic wants to merge 2 commits into
masterfrom
fix/node-shutdown-api-service-close

Conversation

@martinconic

@martinconic martinconic commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Checklist

  • I have read the coding guide.
  • My change requires a documentation update, and I have done it.
  • I have added tests to cover my changes.
  • I have filled out the description and linked the related issues.

Description

Closes #5670.

On node shutdown, Bee.Shutdown closed only the API's http.Server. http.Server does not track hijacked connections, so open websockets were left alone, and api.Service.Close() was never called. As a result, s.quit never fired, s.bgCtx was never canceled, and websocket clients of /pss, /gsoc and /chunks/stream were dropped without a close frame (1006) when the process exited. Before #4651 merged the bee and debug APIs, the node closed the API service on shutdown; that line was dropped.

Changes:

  • pkg/node: Shutdown closes the API service right after the http.Server. The service is only registered for closing once Configure has run, because its quit channel and background context are created there: a node whose startup fails before that point must not close it. The server stops accepting connections first; then the service tells the websocket handlers that are still open to send their close frame and return. api.Service.Close() waits up to 1s for them, and an error is added to the shutdown errors like any other component's.
  • pkg/api chunk upload stream: its read loop checked s.quit only between messages, so an idle client kept the handler blocked in a read and made the service close time out. A watcher now sends 1001 and closes the socket on shutdown. sync.Once guards the close frame, since the read loop and the watcher can both try to send it.

Testing

  • TestShutdownClosesAPIService checks that Shutdown closes the server and then the service, and returns the service's close error.
  • TestChunkUploadStreamShutdownWithIdleClient checks that an idle upload stream gets 1001 and that Service.Close does not report open websockets.
  • TestNewBeeStartupFailureBeforeAPIConfigure runs the real NewBee with a payment threshold below the minimum, which fails after the API service is created but before it is configured, and checks that it returns the error instead of panicking with close of nil channel during shutdown.
  • All three tests fail with their fix reverted.
  • Live check on a Sepolia testnet full node, with SIGTERM while streams are open:
    • Before: /chunks/stream and /pss/subscribe both got 1006, and the node log had no api shutting down line.
    • After: bee start --payment-threshold=1 shuts down cleanly instead of panicking; on a normal SIGTERM the idle /chunks/stream gets 1001 "node shutting down", and /pss/subscribe gets a close frame (1005, since the pss handler sends one without a status code). The API service closes in under 1ms and the node exits normally.
  • make build, make lint, go vet and the race detector on pkg/api and pkg/node all pass.

This merges cleanly with #5618, which documents the current 1006 behavior for the new chunk stream and can return to promising 1001 once this lands.

AI Disclosure

  • This PR contains code that has been generated by an LLM.
  • I have reviewed the AI generated code thoroughly.
  • I possess the technical expertise to responsibly review the code generated in this PR.

Shutdown closed only the api http.Server, which leaves hijacked
websocket connections open, so api.Service.Close was never called.
Websocket handlers never saw s.quit and clients were dropped without a
close frame when the process exited. The api service was closed on
shutdown before the bee and debug apis were merged.

The chunk upload stream checked s.quit only between messages, so an
idle client would still hold up the service close. Close it from a
watcher, and send at most one close frame.

Closes #5670.
Comment thread pkg/node/node.go Outdated
The api service's quit channel and background context are created in
Configure, so closing it before then panics with close of nil channel.
A node whose startup failed between creating the api service and
configuring it did that during its shutdown, for example with a payment
threshold below the minimum.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Node shutdown never calls api.Service.Close, so websocket clients get 1006 instead of 1001

2 participants