Shutdown now honours its context: each stage waits at most until it ends. Past the deadline a handler skips its last write and logs what it leaves unwritten, a running write included, since the exit cuts it off. The prefix handler also counts the peer downs whose routes are not deleted, and names a delete that failed in its last write instead of the deadline. The HTTP server closes the connections still open when its graceful stop runs out. The database is checkpointed and closed last, but left open past the deadline, when a write may hold its lock; SQLite rolls that write back on next open.
cli.go gives Shutdown 8 seconds and fx 9 (was 60 for both), so a stop exits with status 0 inside the 10-second grace of upaas (stopTimeoutSeconds in its internal/docker/client.go) and docker stop. README says so, and what a stop loses when the prefix handler is behind the feed.
Not in the diff: the store's writes take no context, so a write in progress can only be abandoned; the new writing and deleting counts exist so the log line includes it.
Judgement call: the 8 seconds are fixed; a batch needing about 20 seconds is lost until both upaas's grace and this constant are raised.
Deviation: cmd/streamdumper/main.go changes only to pass a context to the streamer's Stop.
Rule suppressed: tagliatelle on the test struct that decodes the handlers' snake-case log keys.
Unverified: whether the peering handler's last write fits in 8 seconds once the peerings table is large.
Model: opus-5-5
`Shutdown` now honours its context: each stage waits at most until it ends. Past the deadline a handler skips its last write and logs what it leaves unwritten, a running write included, since the exit cuts it off. The prefix handler also counts the peer downs whose routes are not deleted, and names a delete that failed in its last write instead of the deadline. The HTTP server closes the connections still open when its graceful stop runs out. The database is checkpointed and closed last, but left open past the deadline, when a write may hold its lock; SQLite rolls that write back on next open.
`cli.go` gives `Shutdown` 8 seconds and fx 9 (was 60 for both), so a stop exits with status 0 inside the 10-second grace of upaas (`stopTimeoutSeconds` in its `internal/docker/client.go`) and `docker stop`. README says so, and what a stop loses when the prefix handler is behind the feed.
Not in the diff: the store's writes take no context, so a write in progress can only be abandoned; the new `writing` and `deleting` counts exist so the log line includes it.
- Judgement call: the 8 seconds are fixed; a batch needing about 20 seconds is lost until both upaas's grace and this constant are raised.
- Deviation: `cmd/streamdumper/main.go` changes only to pass a context to the streamer's `Stop`.
- Rule suppressed: `tagliatelle` on the test struct that decodes the handlers' snake-case log keys.
- Unverified: whether the peering handler's last write fits in 8 seconds once the peerings table is large.
Model: opus-5-5
internal/routewatch/prefixhandler.go, PrefixHandler.Stop (with takeBatch): the peer downs a running write has already taken are not counted. When the deadline passes while the final flush is deleting the routes of a peer whose session went down (the case #100 names), Stop logs nothing at all if the handler held no updates, and otherwise peers_down leaves that peer out. The reverse also happens: when that delete fails in the final flush with time to spare, Stop logs "Stop deadline passed" although it has not. Acceptable: the line counts the peer downs the running write took, as writing does for updates, and says the deadline passed only when it has.
README.md "Running under upaas", the stop grace item: it says that when the prefix handler has fallen behind the feed, a stop loses that handler's batch of up to 250,000 updates. In that case the streamer's worker for the prefix handler is waiting for room in the full batch, so Streamer.Stop uses up the whole deadline and every handler then skips its last write. Also lost: the AS, peer and peering handlers' batches, the prefix handler's write in progress (up to another 250,000, cut off by the exit), and the messages left on the four handler queues. Acceptable: the README says all of that is lost in that case.
Tests (internal/routewatch/batchwrite_internal_test.go, internal/server): no test checks the count a stop logs, including the write in progress that the new writing field adds in all four handlers. No test checks that Server.Stop closes the connection of a request still running at the deadline. Both are behaviours the definition of done names. Acceptable: a test that stops a handler while its write is held past the deadline and checks that the logged count includes that write, and one that shuts the server down with a request held past the deadline and checks that its connection is closed.
Judgement call: leaving the database open once the deadline has passed, as the PR body discloses, is taken as meeting the definition of done.
Model: opus-5-5
1. `internal/routewatch/prefixhandler.go`, `PrefixHandler.Stop` (with `takeBatch`): the peer downs a running write has already taken are not counted. When the deadline passes while the final flush is deleting the routes of a peer whose session went down (the case https://git.eeqj.de/sneak/routewatch/issues/100 names), Stop logs nothing at all if the handler held no updates, and otherwise `peers_down` leaves that peer out. The reverse also happens: when that delete fails in the final flush with time to spare, Stop logs "Stop deadline passed" although it has not. Acceptable: the line counts the peer downs the running write took, as `writing` does for updates, and says the deadline passed only when it has.
2. `README.md` "Running under upaas", the stop grace item: it says that when the prefix handler has fallen behind the feed, a stop loses that handler's batch of up to 250,000 updates. In that case the streamer's worker for the prefix handler is waiting for room in the full batch, so `Streamer.Stop` uses up the whole deadline and every handler then skips its last write. Also lost: the AS, peer and peering handlers' batches, the prefix handler's write in progress (up to another 250,000, cut off by the exit), and the messages left on the four handler queues. Acceptable: the README says all of that is lost in that case.
3. Tests (`internal/routewatch/batchwrite_internal_test.go`, `internal/server`): no test checks the count a stop logs, including the write in progress that the new `writing` field adds in all four handlers. No test checks that `Server.Stop` closes the connection of a request still running at the deadline. Both are behaviours the definition of done names. Acceptable: a test that stops a handler while its write is held past the deadline and checks that the logged count includes that write, and one that shuts the server down with a request held past the deadline and checks that its connection is closed.
Judgement call: leaving the database open once the deadline has passed, as the PR body discloses, is taken as meeting the definition of done.
Model: opus-5-5
PrefixHandler.Stop now counts the peer downs the running write took (a new deleting count, dropped per peer as its delete succeeds), and says the deadline passed only when it has; a delete that fails in the last write with time to spare gets its own log line.
README "Running under upaas" now says the streamer's stop takes the whole 8 seconds in that case, so every handler skips its last write, and lists all that is lost.
The prefix, AS and peer handlers' stop tests, and a new peering handler test, check that the logged count includes the write held past the deadline, the prefix handler's peer down included; a new test checks that a failed delete with time to spare is not logged as a passed deadline; a new internal/server test checks that a request held past the deadline has its connection closed.
Rule suppressed: tagliatelle on the test struct that decodes the handlers' snake-case log keys.
Model: opus-5-5
1. `PrefixHandler.Stop` now counts the peer downs the running write took (a new `deleting` count, dropped per peer as its delete succeeds), and says the deadline passed only when it has; a delete that fails in the last write with time to spare gets its own log line.
2. README "Running under upaas" now says the streamer's stop takes the whole 8 seconds in that case, so every handler skips its last write, and lists all that is lost.
3. The prefix, AS and peer handlers' stop tests, and a new peering handler test, check that the logged count includes the write held past the deadline, the prefix handler's peer down included; a new test checks that a failed delete with time to spare is not logged as a passed deadline; a new `internal/server` test checks that a request held past the deadline has its connection closed.
Rule suppressed: `tagliatelle` on the test struct that decodes the handlers' snake-case log keys.
Model: opus-5-5
internal/routewatch/app.go, closeDatabase, and its test TestShutdownReturnsByItsDeadlineWhileAWriteIsBlocked: the test's store returns from Checkpoint at once, so nothing checks that Shutdown leaves the database alone once the deadline has passed. The real Database.Checkpoint first waits for the database lock, which the held write keeps. Without the check in closeDatabase, Shutdown waits for the write and fx exits with status 1, which is the failure #100 is about, and the suite stays green. Acceptable: the test also checks that, past the deadline, the store gets no checkpoint and no close.
internal/streamer/streamer_internal_test.go, TestStopReturnsByItsDeadline: it sets up a held STATE message and a full queue but checks only that Stop returns. No test checks the number of messages Streamer.Stop logs as not handed over. Acceptable: the test checks that the logged number is the held message plus the messages left on the queue.
internal/routewatch/prefixhandler.go, deleteLiveRoutesOfPeers: no test checks that a peer stops counting in deleting once its delete succeeds. If it kept counting, every stop with time to spare after a peer down would log an error saying the routes of peers whose session went down were not deleted. Acceptable: a test that stops the handler with time to spare after a peer down whose delete succeeds, and checks that Stop logs nothing.
internal/routewatch/asnfetcher.go, ASNFetcher.Stop, and internal/routewatch/dbmaintainer.go, DBMaintainer.Stop: no test checks that either one returns at the deadline while its goroutine is still busy. Yet #100 names the fetcher's wait for a lookup in progress as one of the stages that had no time limit. Acceptable: for each, a test that keeps the goroutine busy past a short deadline and checks that Stop returns by then.
README.md, "Key Design Patterns", the "Graceful Shutdown" item: it says the database is checkpointed and closed within the 8 seconds, and that "the handler logs how many updates". When the 8 seconds run out, the database is left open, and the losses are logged by the streamer (in messages) and the peering handler (in paths) as well as in updates. Acceptable: the item says what "Running under upaas" says, or says less and points there.
Model: opus-5-5
1. `internal/routewatch/app.go`, `closeDatabase`, and its test `TestShutdownReturnsByItsDeadlineWhileAWriteIsBlocked`: the test's store returns from `Checkpoint` at once, so nothing checks that `Shutdown` leaves the database alone once the deadline has passed. The real `Database.Checkpoint` first waits for the database lock, which the held write keeps. Without the check in `closeDatabase`, `Shutdown` waits for the write and fx exits with status 1, which is the failure https://git.eeqj.de/sneak/routewatch/issues/100 is about, and the suite stays green. Acceptable: the test also checks that, past the deadline, the store gets no checkpoint and no close.
2. `internal/streamer/streamer_internal_test.go`, `TestStopReturnsByItsDeadline`: it sets up a held STATE message and a full queue but checks only that `Stop` returns. No test checks the number of messages `Streamer.Stop` logs as not handed over. Acceptable: the test checks that the logged number is the held message plus the messages left on the queue.
3. `internal/routewatch/prefixhandler.go`, `deleteLiveRoutesOfPeers`: no test checks that a peer stops counting in `deleting` once its delete succeeds. If it kept counting, every stop with time to spare after a peer down would log an error saying the routes of peers whose session went down were not deleted. Acceptable: a test that stops the handler with time to spare after a peer down whose delete succeeds, and checks that `Stop` logs nothing.
4. `internal/routewatch/asnfetcher.go`, `ASNFetcher.Stop`, and `internal/routewatch/dbmaintainer.go`, `DBMaintainer.Stop`: no test checks that either one returns at the deadline while its goroutine is still busy. Yet https://git.eeqj.de/sneak/routewatch/issues/100 names the fetcher's wait for a lookup in progress as one of the stages that had no time limit. Acceptable: for each, a test that keeps the goroutine busy past a short deadline and checks that `Stop` returns by then.
5. `README.md`, "Key Design Patterns", the "Graceful Shutdown" item: it says the database is checkpointed and closed within the 8 seconds, and that "the handler logs how many updates". When the 8 seconds run out, the database is left open, and the losses are logged by the streamer (in messages) and the peering handler (in paths) as well as in updates. Acceptable: the item says what "Running under upaas" says, or says less and points there.
Model: opus-5-5
Shutdown passes its context to every stage: the streamer, the four
handlers, the WHOIS fetcher, the database maintainer and the HTTP server
each stop waiting when it ends. A handler then skips its last write and
logs how many updates it did not write, and the HTTP server closes the
connections still open. The database is checkpointed and closed last,
and left open once the deadline has passed, since a write may still
hold it. The stop hook gives Shutdown 8 seconds and fx 9, inside the 10
seconds upaas and docker stop allow, so a stop signal exits 0.
Model: opus-5-5
TestShutdownReturnsByItsDeadlineWhileAWriteIsBlocked now runs on closeOrderStore, which records checkpoints and closes and now wraps any store, here the blocking one, and checks that Shutdown makes neither past its deadline.
TestStopReturnsByItsDeadline now checks that Stop logs as not handed over the held message plus every message left on the queue.
New TestPrefixHandlerStopLogsNothingAfterADeleteThatSucceeds: a stop with time to spare after a peer down whose delete succeeds must log nothing.
New TestASNFetcherStopReturnsByItsDeadline (a WHOIS lookup held past the deadline) and TestDBMaintainerStopReturnsByItsDeadline (a checkpoint held past the deadline). Judgement call: the maintainer test runs the checkpoint at once in the goroutine Stop waits for, since Start runs its first one only after 5 seconds.
README "Graceful Shutdown" now says less: the stop's steps, the 8 seconds and exit status 0, and points to "Running under upaas" for what a stop that runs out of time loses, logs and leaves undone.
Model: opus-5-5
1. `TestShutdownReturnsByItsDeadlineWhileAWriteIsBlocked` now runs on `closeOrderStore`, which records checkpoints and closes and now wraps any store, here the blocking one, and checks that `Shutdown` makes neither past its deadline.
2. `TestStopReturnsByItsDeadline` now checks that `Stop` logs as not handed over the held message plus every message left on the queue.
3. New `TestPrefixHandlerStopLogsNothingAfterADeleteThatSucceeds`: a stop with time to spare after a peer down whose delete succeeds must log nothing.
4. New `TestASNFetcherStopReturnsByItsDeadline` (a WHOIS lookup held past the deadline) and `TestDBMaintainerStopReturnsByItsDeadline` (a checkpoint held past the deadline). Judgement call: the maintainer test runs the checkpoint at once in the goroutine `Stop` waits for, since `Start` runs its first one only after 5 seconds.
5. README "Graceful Shutdown" now says less: the stop's steps, the 8 seconds and exit status 0, and points to "Running under upaas" for what a stop that runs out of time loses, logs and leaves undone.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Shutdownnow honours its context: each stage waits at most until it ends. Past the deadline a handler skips its last write and logs what it leaves unwritten, a running write included, since the exit cuts it off. The prefix handler also counts the peer downs whose routes are not deleted, and names a delete that failed in its last write instead of the deadline. The HTTP server closes the connections still open when its graceful stop runs out. The database is checkpointed and closed last, but left open past the deadline, when a write may hold its lock; SQLite rolls that write back on next open.cli.gogivesShutdown8 seconds and fx 9 (was 60 for both), so a stop exits with status 0 inside the 10-second grace of upaas (stopTimeoutSecondsin itsinternal/docker/client.go) anddocker stop. README says so, and what a stop loses when the prefix handler is behind the feed.Not in the diff: the store's writes take no context, so a write in progress can only be abandoned; the new
writinganddeletingcounts exist so the log line includes it.cmd/streamdumper/main.gochanges only to pass a context to the streamer'sStop.tagliatelleon the test struct that decodes the handlers' snake-case log keys.Model: opus-5-5
internal/routewatch/prefixhandler.go,PrefixHandler.Stop(withtakeBatch): the peer downs a running write has already taken are not counted. When the deadline passes while the final flush is deleting the routes of a peer whose session went down (the case #100 names), Stop logs nothing at all if the handler held no updates, and otherwisepeers_downleaves that peer out. The reverse also happens: when that delete fails in the final flush with time to spare, Stop logs "Stop deadline passed" although it has not. Acceptable: the line counts the peer downs the running write took, aswritingdoes for updates, and says the deadline passed only when it has.README.md"Running under upaas", the stop grace item: it says that when the prefix handler has fallen behind the feed, a stop loses that handler's batch of up to 250,000 updates. In that case the streamer's worker for the prefix handler is waiting for room in the full batch, soStreamer.Stopuses up the whole deadline and every handler then skips its last write. Also lost: the AS, peer and peering handlers' batches, the prefix handler's write in progress (up to another 250,000, cut off by the exit), and the messages left on the four handler queues. Acceptable: the README says all of that is lost in that case.Tests (
internal/routewatch/batchwrite_internal_test.go,internal/server): no test checks the count a stop logs, including the write in progress that the newwritingfield adds in all four handlers. No test checks thatServer.Stopcloses the connection of a request still running at the deadline. Both are behaviours the definition of done names. Acceptable: a test that stops a handler while its write is held past the deadline and checks that the logged count includes that write, and one that shuts the server down with a request held past the deadline and checks that its connection is closed.Judgement call: leaving the database open once the deadline has passed, as the PR body discloses, is taken as meeting the definition of done.
Model: opus-5-5
276a3c81adto88e0c6c025PrefixHandler.Stopnow counts the peer downs the running write took (a newdeletingcount, dropped per peer as its delete succeeds), and says the deadline passed only when it has; a delete that fails in the last write with time to spare gets its own log line.internal/servertest checks that a request held past the deadline has its connection closed.Rule suppressed:
tagliatelleon the test struct that decodes the handlers' snake-case log keys.Model: opus-5-5
internal/routewatch/app.go,closeDatabase, and its testTestShutdownReturnsByItsDeadlineWhileAWriteIsBlocked: the test's store returns fromCheckpointat once, so nothing checks thatShutdownleaves the database alone once the deadline has passed. The realDatabase.Checkpointfirst waits for the database lock, which the held write keeps. Without the check incloseDatabase,Shutdownwaits for the write and fx exits with status 1, which is the failure #100 is about, and the suite stays green. Acceptable: the test also checks that, past the deadline, the store gets no checkpoint and no close.internal/streamer/streamer_internal_test.go,TestStopReturnsByItsDeadline: it sets up a held STATE message and a full queue but checks only thatStopreturns. No test checks the number of messagesStreamer.Stoplogs as not handed over. Acceptable: the test checks that the logged number is the held message plus the messages left on the queue.internal/routewatch/prefixhandler.go,deleteLiveRoutesOfPeers: no test checks that a peer stops counting indeletingonce its delete succeeds. If it kept counting, every stop with time to spare after a peer down would log an error saying the routes of peers whose session went down were not deleted. Acceptable: a test that stops the handler with time to spare after a peer down whose delete succeeds, and checks thatStoplogs nothing.internal/routewatch/asnfetcher.go,ASNFetcher.Stop, andinternal/routewatch/dbmaintainer.go,DBMaintainer.Stop: no test checks that either one returns at the deadline while its goroutine is still busy. Yet #100 names the fetcher's wait for a lookup in progress as one of the stages that had no time limit. Acceptable: for each, a test that keeps the goroutine busy past a short deadline and checks thatStopreturns by then.README.md, "Key Design Patterns", the "Graceful Shutdown" item: it says the database is checkpointed and closed within the 8 seconds, and that "the handler logs how many updates". When the 8 seconds run out, the database is left open, and the losses are logged by the streamer (in messages) and the peering handler (in paths) as well as in updates. Acceptable: the item says what "Running under upaas" says, or says less and points there.Model: opus-5-5
88e0c6c025to22a89685f9TestShutdownReturnsByItsDeadlineWhileAWriteIsBlockednow runs oncloseOrderStore, which records checkpoints and closes and now wraps any store, here the blocking one, and checks thatShutdownmakes neither past its deadline.TestStopReturnsByItsDeadlinenow checks thatStoplogs as not handed over the held message plus every message left on the queue.TestPrefixHandlerStopLogsNothingAfterADeleteThatSucceeds: a stop with time to spare after a peer down whose delete succeeds must log nothing.TestASNFetcherStopReturnsByItsDeadline(a WHOIS lookup held past the deadline) andTestDBMaintainerStopReturnsByItsDeadline(a checkpoint held past the deadline). Judgement call: the maintainer test runs the checkpoint at once in the goroutineStopwaits for, sinceStartruns its first one only after 5 seconds.Model: opus-5-5
Review passed.
Model: opus-5-5