Repository navigation
fix(server): wait for background DB init before returning from shutdown - #691
Conversation
|
Warning Review limit reached
This review includes 3 billable files and costs up to $0.75.
Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing. Or wait 39 minutes for your next included review. View limit detailsLimit details: You’ve used the included review currently available. Your 85 included PR review attempts over the past 7 days set your current allowance at 1 review per hour. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (3)
Comment |
|
Subject: PR 691 (#664) review at head 232fc2c VERDICT: no blocking findings at 232fc2c. Four P3 test/robustness notes below. CI not checked by me. Method: read-only. Source from the writer's worktree (HEAD 232fc2c, clean; base e5baaab is an ancestor). Checks via
P3 notes (none block): |
|
Subject: PR 691 delta review 232fc2c -> a0a64ba VERDICT: no blocking findings. 1 non-blocking finding (listener assertion can pass vacuously, ~20% on the wait-before-Shutdown mutant), everything else as claimed. CI not checked by me. Method: read-only. Delta read with git diff in the writer's worktree (HEAD a0a64ba, clean); go test -overlay from scratch copies, GOWORK=off GOTOOLCHAIN=go1.26.6, ./internal/server only, no -race, platform test-suite lockf, df 20 GiB. Worktree unchanged afterwards. Real StartHTTPServer + fake resolver, not a live DB. Delta review: http.go change is the only production change: Mutants at a0a64ba (scratch overlays on http.go):
FINDING (P3, test robustness): the order assertion dials the port after SIGTERM and treats the first dial error as "listener closed". The test only waits for the resolver to be entered, which does not imply ListenAndServe has bound the port yet. If SIGTERM and the first dial happen before the bind, the dial fails with connection refused, the loop breaks, and the assertion passes without having observed a closed listener. Suggested fix: before sending SIGTERM, poll-dial until a connection succeeds (bounded, e.g. 5s), then send SIGTERM and require the dial to fail. That makes the assertion deterministic and removes the ~20% escape. |
|
Subject: PR 691 delta review #2 a0a64ba -> 7b4b88d VERDICT: no findings. The racy-listener note is fixed: the wait-before-Shutdown mutant is now killed 20/20 (was 16/20). CI not checked by me. Method: read-only. git diff in the writer's worktree (HEAD 7b4b88d, clean); go test -overlay from a scratch copy of http.go, GOWORK=off GOTOOLCHAIN=go1.26.6, ./internal/server only, no -race, platform test-suite lockf, df 16 GiB. Worktree sha and git status unchanged afterwards. Real StartHTTPServer + fake resolver, not a live DB. Delta: 1 file, +16 lines, internal/server/http_shutdown_test.go only; http.go is byte-identical to a0a64ba (git diff shows no http.go change). startServerWithBlockedInit now polls (10ms step, 100ms dial timeout, 5s deadline, t.Fatalf on timeout) until a TCP connect succeeds before returning, so SIGTERM is only sent once the listener is provably bound. The poll connection is closed immediately; the server sees an idle connection that Shutdown closes, no effect on later assertions. Evidence:
|
startBackgroundInit now returns a channel closed when the initializer exits, and StartHTTPServer waits on it after server.Shutdown inside the same 30s grace period. Without the wait, main ran app.Close while ensureDB might still be writing app.DB. The test blocks ensureDB in secret resolution, sends SIGTERM, and asserts StartHTTPServer does not return until the initializer exits. Closes #664 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Three mutants survived review of the shutdown wait: an unbounded wait, a no-DB path that leaves the done channel open, and the wait placed before server.Shutdown. Make the grace period a variable so a test can shorten it, assert the listener is closed while the initializer is still parked, assert the no-DB channel is already closed, and harden the blocking resolver (sync.Once, release on cleanup). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The "listener closed after SIGTERM" check could pass only because the server had not bound its port yet, so the wait-before-Shutdown mutant was not reliably killed. Poll with a deadline until the server accepts a connection before the helper returns, so SIGTERM is sent only once the listener is provably up. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
7b4b88d to
b764ddc
Compare
|
Delta review 7b4b88d..b764ddc (rebase onto main 0e34519). Reviewer: archera-quotes-kimi (Claude Sonnet 5.5). Verdict: pure rebase, no findings. The per-file patches of the PR at 7b4b88d (GitHub compare against main, merge base e5baaab) and at b764ddc (PR files API) are byte-identical for all three files (internal/server/http.go, http_shutdown_test.go, readiness_test.go), including hunk headers and context, so the rebase moved no hunk and the 3 commits keep their content and order. The earlier verdicts (no blocking findings; wait-before-Shutdown mutant killed 20/20 at 7b4b88d) therefore carry over. I did not re-run tests or mutants myself for this delta; platform-new reports go vet and go test ./internal/server ok on Go 1.27.1 and 1.26.9. CI at b764ddc, read just now: the Build & Test run (37928136610) is still pending on most jobs (Unit Tests, Lint Code, Build Docker Image, Security Scanning, E2E and Integration pending); pre-commit passes. The Review gate needs the full CI green on this SHA before merge; that is not yet the case. |
|
Merged at reviewed SHA b764ddc (squash, merge commit 16aec29). |
… offering type (#1796) * chore(deps): bump cloud-commitments-go to 790f369f Moves all four cloud-commitments-go modules to go main 790f369f, which adds #682 (fractional RI quantities rounded down with their costs scaled), #684 (purchase counts above int32 refused before any AWS call), #685 (GCP lists only live commitments), #686 (the optional observed-rate capability), #688 (exchange targets need the source RI's offering type) and #691 (commitment rates fetched per layer). Minimum version selection also raises a few transitive AWS SDK and Google API modules the library requires. No replace directives; go mod verify passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ri-exchange): forward the source RI's offering type to the target lookup cloud-commitments-go#688 makes FindConvertibleOfferingParams.OfferingType required, so an exchange only targets an offering with the source RI's payment option. Pin pkg and providers/aws to that commit and forward the field: convertForAutoExchange copies ConvertibleRI.OfferingType into RIMetadataInfo, and newConvertibleOfferingLookup passes it to the provider. Without the forwarding, every exchange lookup is refused once the pin lands. The zone reshape tests' fake RI had no offering type, so the provider now refuses its lookup. The fixture now carries Partial Upfront, and the zonal test asserts that type reaches DescribeReservedInstancesOfferings. Part of #1789 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Summary
Closes #664.
StartHTTPServerstarted the background DB initializer but never waited for it. On SIGTERM it ranserver.Shutdownand returned, somainrandefer app.Close()whileensureDBcould still be writingapp.DB: an unsynchronized read/write, and a pool created just afterCloseread nil was never closed.startBackgroundInitnow returns a channel closed when the initializer exits (already closed when there is nodbConfig).StartHTTPServerwaits on it afterserver.Shutdown, inside the same 30sshutdownCtx; if the grace period runs out it logs and returns as before.Out of scope (noted in the issue):
runMigrationsBoundedtakes a pool, not a context, so SIGTERM mid-migration is still not interrupted.Verification (honest labels)
TestStartHTTPServerWaitsForBackgroundInitOnShutdowndrives the realStartHTTPServer: a secret resolver blocks insideGetSecret(keepsensureDBrunning), the test sends SIGTERM, and assertsStartHTTPServerdoes not return until the initializer exits.-race(disk rules); it does not need it.go vet,go test ./internal/server(whole package),golangci-lint run ./internal/server/...(0 issues, GOTOOLCHAIN=go1.26.6) all pass locally.🤖 Generated with Claude Code