fix(restore): join downloads during Fx shutdown - #1908
Conversation
✅ Approve — automated reviewThe shutdown admission gate, request/job joins, cancellation, and Fx hook ordering are consistent. No actionable correctness defect was found in the current diff. No findings. |
|
Both reviews were verified line-by-line against the diff and the code under test. The PR faithfully implements the requested restore-mode lifetime boundary: Recommendation: approve with comments. Standards
Spec
No other spec findings: admission closure, the admit-then-stopping recheck at job registration, the no-mutex join, the staging-store-closes-last ordering, and the documented fail-loud choice to ignore the Fx deadline when a backend violates context cancellation all match the PR's claims. Reviewed independently by GLM (glm-5.3-flash) and Codex (gpt-5.6-sol) via Shipfox; verified and synthesized by GLM. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## release/v3.0 #1908 +/- ##
=============================================
Coverage 83.29% 83.30%
=============================================
Files 460 460
Lines 42399 42456 +57
=============================================
+ Hits 35316 35367 +51
- Misses 7078 7084 +6
Partials 5 5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
8caf088 to
8797af3
Compare
|
Addressed the four items in the Shipfox review after rebasing onto
No unresolved inline threads or pending reviews were present. The S3 Fx regression and both affected package race suites passed locally. Canonical validation and the final candidate are recorded in the PR description and follow-up status. Fix commit: |
|
This PR makes restore-mode download jobs children of the application lifetime and adds a phased Fx shutdown: admission closes and the job is canceled first ( Standards
No documented-standard violations found: no SpecThe Spec axis has no confirmed material finding — no missing or partial requirements, no scope creep, and no implementation defects. All four PR-body constraints are implemented and verified: (1) no new job starts after shutdown begins ( One residual risk is explicitly acknowledged in the PR and is a deliberate trade-off, not a defect: Reviewed independently by GLM (glm-5.3-flash) and DeepSeek (deepseek-v4-pro-0813) via Shipfox; verified and synthesized by GLM. |
What changed
Restore-mode services now own asynchronous download jobs for the full Fx application lifetime. Shutdown rejects new restore RPC work, cancels and joins an active download, waits for admitted restore RPCs, then closes retained staging Pebble state.
RPC detachment remains intentional: cancelling the initiating StartDownload RPC does not cancel the background job; explicit CancelDownload remains the job-level cancellation API.
Why
The current service creates downloads from a background context and has no Fx stop hook. A deterministic production-module regression showed Fx shutdown returning while an S3 manifest request and restore job remained alive, leaving service-owned staging state without teardown ownership.
Product / operational motivation
Need: restore work may outlive its initiating RPC, but must never outlive the restore-mode application that owns it.
Current limitation: Fx shutdown neither cancels nor joins active download work and never closes retained staging Pebble state.
Requirement / constraint: once shutdown begins, no new job starts; active work receives cancellation and is joined; admitted RPCs finish; service-owned staging state closes last.
Evidence: internal/bootstrap/restore_download_lifecycle_test.go and internal/adapter/grpc/server_restore_lifecycle_test.go.
Durable repository evidence: docs/ops/backup-restore.md and docs/technical/architecture/subsystems/api/grpc-api.md.
Technical decision
Decision: add a restore-service lifetime context, an admission/stopping gate, active-request and active-job joins, and phased Fx stop hooks that order admission closure before transports and resource closure after gRPC draining.
Why now / why proportionate: this is the smallest ownership boundary matching the existing single-job service and Fx lifecycle.
Alternatives considered: tying work to the initiating RPC would break intentional asynchronous behavior; closing staging state directly from Fx would race active restore operations; a broader restore subsystem redesign is unnecessary.
Risk
MEDIUM: shutdown ordering and concurrency change in restore mode; deterministic synchronization tests and focused race validation cover the affected paths.
Validation
bash scripts/agent-check(standalone, then repeated by the final PR gate)AI_REVIEW_BASE_SHA=afdd58395fd4689c624286893c2c618e38db8148 bash scripts/agent-check-prwith shared caches and reduced local build concurrency (GOMAXPROCS=4 GOFLAGS=-p=2)./internal/adapter/grpcand./internal/bootstrapgo test -race -tags s3 ./internal/bootstrap -run '^TestRestoreDownloadStopsWithFxApplication$' -count=1 -timeout 2mTestsexplicitly runs the tagged Fx regression against the in-process S3 endpoint; no MinIO or Docker is required for this case8797af37b73b3619e6408c396234f1f478146cf6, including the explicit S3 Fx lifecycle stepArchitecture / behavior impact
No wire, storage-format, FSM, Raft, or compatibility change. Restore-mode shutdown now owns asynchronous work and retained staging-store teardown. Explicit cancellation still leaves the application available for another download.
Review focus
Please check lock/join ordering, the double admission check at job registration, and the phased Fx hook order around HTTP/gRPC shutdown and staging-store closure.
Known concerns
A download backend that ignores context cancellation can keep shutdown blocked. That is intentional fail-loud ownership behavior; returning would violate the application-lifetime invariant.