Wait for IRC listener cleanup and HTTP shutdown on stop (closes #159) #166

Merged
clawbot merged 1 commits from issue-159-clean-shutdown into next 2026-10-20 23:48:31 +02:00
Collaborator

Stopping the server left IRC listener sessions in the database: #159

  • stop in internal/ircserver now waits up to stopTimeout (5 s) for every connection's serve to return, cleanup included, so each session is ended, and its channel peers sent the QUIT, before the database closes. A connection accepted while stop runs is closed instead of served, so nothing joins the wait after it has begun.
  • Each connection's cleanup now runs on a context that stopping does not cancel. Waiting alone changed nothing: stop cancels the server context first, so the first query in BroadcastQuit failed and nothing was deleted. The new test exposed this.
  • The HTTP server's stop hook now calls cleanShutdown, which runs httpServer.Shutdown and waits up to shutdownTimeout (5 s) for requests in progress. httpServer is set in the start hook before the serving goroutine starts. The server's own SIGINT/SIGTERM watcher is removed: fx already runs the stop hooks on those signals, and the watcher's shutdown raced them. SIGPIPE is still ignored.

Disclosures:

  • Deviation: CONVENTIONS.md §4 shows the server watching signals itself; this removes that, for the reason above.
  • A web client's long poll open at stop holds the HTTP stop until the poll ends or 5 s pass.
  • No test reaches a connection accepted while stop runs.
  • Bug elsewhere, unfixed: a failed HTTP listen is only logged, and the process runs on without HTTP.

Model: opus-5-5

Stopping the server left IRC listener sessions in the database: https://git.eeqj.de/sneak/neoirc/issues/159 - `stop` in `internal/ircserver` now waits up to `stopTimeout` (5 s) for every connection's `serve` to return, cleanup included, so each session is ended, and its channel peers sent the QUIT, before the database closes. A connection accepted while `stop` runs is closed instead of served, so nothing joins the wait after it has begun. - Each connection's cleanup now runs on a context that stopping does not cancel. Waiting alone changed nothing: `stop` cancels the server context first, so the first query in `BroadcastQuit` failed and nothing was deleted. The new test exposed this. - The HTTP server's stop hook now calls `cleanShutdown`, which runs `httpServer.Shutdown` and waits up to `shutdownTimeout` (5 s) for requests in progress. `httpServer` is set in the start hook before the serving goroutine starts. The server's own SIGINT/SIGTERM watcher is removed: fx already runs the stop hooks on those signals, and the watcher's shutdown raced them. SIGPIPE is still ignored. Disclosures: - Deviation: `CONVENTIONS.md` §4 shows the server watching signals itself; this removes that, for the reason above. - A web client's long poll open at stop holds the HTTP stop until the poll ends or 5 s pass. - No test reaches a connection accepted while `stop` runs. - Bug elsewhere, unfixed: a failed HTTP listen is only logged, and the process runs on without HTTP. Model: opus-5-5
clawbot added the needs-review label 2026-10-20 21:47:05 +02:00
clawbot self-assigned this 2026-10-20 21:47:05 +02:00
Author
Collaborator

FAIL (needs rework)

  1. internal/server/server_test.go, TestStopWaitsForRequestInProgress, with Stop in internal/server/export_test.go: the test calls cleanShutdown through a test-only Stop that copies the stop hook's body, and never runs the stop hook that New registers. That hook calling cleanShutdown is the HTTP half of the fix for #159, and it is untested: a stop hook that does nothing, as before the fix, passes every test. Acceptable: the test stops the server through the stop hook New registers (for example with an fxtest app, as the internal/handlers tests already build the server), so a stop hook that does not wait for the request in progress fails it.

Judgement calls:

  • The CONVENTIONS.md §4 deviation is accepted: fx's Run already runs the stop hooks on SIGINT and SIGTERM, and the server's own watcher would run the HTTP shutdown a second time alongside them.
  • A web client's long poll open at stop holds the stop for the full shutdownTimeout and logs "server clean shutdown failed" each time; accepted as disclosed, since the issue asks the stop hook to wait.
  • No test covers a connection accepted while stop runs; that window cannot be reached reliably from a test without a hook added only for it, so it is not raised.
  • README.md still describes server.go as doing signal handling; left alone, as it still ignores SIGPIPE.

Model: opus-5-5

**FAIL** (needs rework) 1. `internal/server/server_test.go`, `TestStopWaitsForRequestInProgress`, with `Stop` in `internal/server/export_test.go`: the test calls `cleanShutdown` through a test-only `Stop` that copies the stop hook's body, and never runs the stop hook that `New` registers. That hook calling `cleanShutdown` is the HTTP half of the fix for https://git.eeqj.de/sneak/neoirc/issues/159, and it is untested: a stop hook that does nothing, as before the fix, passes every test. Acceptable: the test stops the server through the stop hook `New` registers (for example with an `fxtest` app, as the `internal/handlers` tests already build the server), so a stop hook that does not wait for the request in progress fails it. Judgement calls: - The `CONVENTIONS.md` §4 deviation is accepted: fx's `Run` already runs the stop hooks on SIGINT and SIGTERM, and the server's own watcher would run the HTTP shutdown a second time alongside them. - A web client's long poll open at stop holds the stop for the full `shutdownTimeout` and logs "server clean shutdown failed" each time; accepted as disclosed, since the issue asks the stop hook to wait. - No test covers a connection accepted while `stop` runs; that window cannot be reached reliably from a test without a hook added only for it, so it is not raised. - `README.md` still describes `server.go` as doing signal handling; left alone, as it still ignores SIGPIPE. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-20 22:06:36 +02:00
clawbot force-pushed issue-159-clean-shutdown from 3f936925b9 to e03e3a50c2 2026-10-20 22:32:20 +02:00 Compare
Author
Collaborator

Rework for #166 (comment):

  1. TestStopWaitsForRequestInProgress now starts the app with the providers cmd/neoircd uses and stops it through fx, so it runs the stop hook server.New registers, with a long poll in progress that the test releases through the broker. The test-only NewTestHTTPServer, Start and Stop are removed.

Also:

  • New TestStopReturnsWhileCleanupIsStuck covers the five-second bound on the IRC listener's wait, which no test covered; the stopTimeout comment now says it is the longest wait.
  • PR body: the long-poll line is reworded, and it now says no test reaches a connection accepted while stop runs.

Model: opus-5-5

Rework for https://git.eeqj.de/sneak/neoirc/pulls/166#issuecomment-152187: 1. `TestStopWaitsForRequestInProgress` now starts the app with the providers `cmd/neoircd` uses and stops it through fx, so it runs the stop hook `server.New` registers, with a long poll in progress that the test releases through the broker. The test-only `NewTestHTTPServer`, `Start` and `Stop` are removed. Also: - New `TestStopReturnsWhileCleanupIsStuck` covers the five-second bound on the IRC listener's wait, which no test covered; the `stopTimeout` comment now says it is the longest wait. - PR body: the long-poll line is reworded, and it now says no test reaches a connection accepted while `stop` runs. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-20 22:32:34 +02:00
Author
Collaborator

FAIL (needs rework)

  1. internal/ircserver/server_test.go, TestStopEndsSessions: rebased onto current next, the package's tests do not build, because the test calls db.Database.CountChannelMembers, which the fix for #153 removed from next. The gate fails, and the test that the definition of done asks for does not run. Acceptable: rebase onto current next and count the channel's members with a query next still has (for example GetChannelMemberIDs), with the gate green on the rebased tree.

Judgement calls:

  • The cleanup's context has no deadline of its own. At stop it is bounded by stopTimeout and then by the database closing, and a normal client disconnect had no deadline before this change either; not raised.
  • go srv.serveUntilShutdown() in startHTTP is not wrapped in an anonymous function as CODE_STYLEGUIDE_GO.md asks; the line moved unchanged from the old serve, so it is not raised.

Model: opus-5-5

**FAIL** (needs rework) 1. `internal/ircserver/server_test.go`, `TestStopEndsSessions`: rebased onto current `next`, the package's tests do not build, because the test calls `db.Database.CountChannelMembers`, which the fix for https://git.eeqj.de/sneak/neoirc/issues/153 removed from `next`. The gate fails, and the test that the definition of done asks for does not run. Acceptable: rebase onto current `next` and count the channel's members with a query `next` still has (for example `GetChannelMemberIDs`), with the gate green on the rebased tree. Judgement calls: - The cleanup's context has no deadline of its own. At stop it is bounded by `stopTimeout` and then by the database closing, and a normal client disconnect had no deadline before this change either; not raised. - `go srv.serveUntilShutdown()` in `startHTTP` is not wrapped in an anonymous function as `CODE_STYLEGUIDE_GO.md` asks; the line moved unchanged from the old `serve`, so it is not raised. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-20 23:12:16 +02:00
clawbot added 1 commit 2026-10-20 23:19:27 +02:00
Stopping the server returned before the IRC listener's connections had
run their cleanup, and the database closed under them, so their
sessions, clients and channel memberships stayed and their nicks stayed
taken after a restart.

The IRC listener's stop now waits up to five seconds for every
connection's cleanup, which now runs on a context that stopping does
not cancel. The HTTP server's stop hook now runs Shutdown itself and
waits for it; httpServer is set before serving starts, and the server's
own signal watcher, which raced fx's stop hooks, is gone.

Model: opus-5-5
clawbot force-pushed issue-159-clean-shutdown from e03e3a50c2 to 70913dd6bf 2026-10-20 23:19:27 +02:00 Compare
Author
Collaborator

Rework for #166 (comment):

  1. Rebased onto current next. TestStopEndsSessions now reads the channel's members with GetChannelMemberIDs and checks that the peer is the only one left.

Also: the PR body's long-poll line is shortened; nothing else in it changed.

Model: opus-5-5

Rework for https://git.eeqj.de/sneak/neoirc/pulls/166#issuecomment-152341: 1. Rebased onto current `next`. `TestStopEndsSessions` now reads the channel's members with `GetChannelMemberIDs` and checks that the peer is the only one left. Also: the PR body's long-poll line is shortened; nothing else in it changed. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-20 23:19:37 +02:00
Author
Collaborator

PASS

Model: opus-5-5

**PASS** Model: opus-5-5
clawbot removed the needs-review label 2026-10-20 23:41:43 +02:00
clawbot merged commit 497514d3df into next 2026-10-20 23:48:31 +02:00
clawbot deleted branch issue-159-clean-shutdown 2026-10-20 23:48:31 +02:00
Sign in to join this conversation.