Cover batched checkpoint retry recovery · Entire
Cover batched checkpoint retry recovery
68845c9→main
Sessions
e4aeef3a16bbView transcript
Changes
2
docs
- Mrewrite-issue-list.md+4/-2
internal/syncer
- Mintegration_test.go+120
157 unmodified lines
158
159
160
161
161
162
163
164
165
266 unmodified lines
432
433
434
434
435
436
437
438
439
157 unmodified lines
Current rewrite note:
- Ownership of stream lifecycle is clearer than on `main`, and the rewrite now has direct tests for key pack-stream close behavior on success and error paths.
- Direct strategy-level error-path tests now verify that relay bootstrap and incremental paths close source pack streams when pushes fail.
- This still wants a fuller close-audit around deeper batched failure paths before it should be considered fully done.
- Batched integration coverage now also exercises a failed checkpoint pack push followed by a resume-from-temp-ref retry.
- This still wants a fuller close-audit around lower-level transport interruption paths before it should be considered fully done.
### 6. Protocol v2 tag fetches request `include-tag` without capability gating
266 unmodified lines
- Batched bootstrap resume mismatch and final-tip cutover paths now have direct integration coverage.
- Batched bootstrap reruns now also cover the "target ref already created, temp ref cleanup still pending" recovery path.
- Injected temp-ref delete failure during batched cutover is now covered end-to-end, including successful recovery on retry.
- Some harder injected pack-transfer failure paths still remain.
- Injected checkpoint pack failure after partial batched progress is now covered end-to-end, including successful resume on retry.
- Some harder transport-interruption and malformed mid-stream failure paths still remain.
### 22. No benchmark coverage for the expensive paths
Mdocs/rewrite-issue-list.md+4/-2
515 unmodified lines
... (truncated for brevity)
func TestBootstrap_IntegrationBatchedPackFailureResumesOnRetry(t *testing.T) { sourceRepo, sourceFS := newSourceRepo(t) makeLargeCommits(t, sourceRepo, sourceFS, 5, 200_000)
targetRepo, err := git.Init(memory.NewStorage()) if err != nil { t.Fatalf("init target repo: %v", err) }
sourceServer := newSmartHTTPRepoServerV2(t, sourceRepo) targetServer := newSmartHTTPRepoServer(t, targetRepo) defer sourceServer.Close() defer targetServer.Close()
cfg := Config{ Source: Endpoint{URL: sourceServer.RepoURL()}, Target: Endpoint{URL: targetServer.RepoURL()}, ProtocolMode: protocolModeAuto, BatchMaxPackBytes: 350_000, }
s, err := newSession(context.Background(), cfg, false) if err != nil { t.Fatalf("new session: %v", err) } desired, _, err := planner.BuildDesiredRefs(s.sourceRefMap, planConfig(cfg)) if err != nil { t.Fatalf("build desired refs: %v", err) } ref := desired[plumbing.NewBranchReferenceName(testBranch)] checkpoints, err := bstrap.PlanCheckpoints(context.Background(), bstrap.Params{ SourceConn: s.sourceConn, SourceService: s.sourceService, BatchMaxPack: cfg.BatchMaxPackBytes, }, ref) if err != nil { t.Fatalf("plan checkpoints: %v", err) } if len(checkpoints) < 2 { t.Fatalf("expected multiple checkpoints, got %d", len(checkpoints)) }
targetRef := plumbing.NewBranchReferenceName(testBranch) tempRef := planner.BootstrapTempRef(targetRef) packPushes := 0 targetServer.receivePackHook = func(req *packp.UpdateRequests, hasPack bool) *packp.ReportStatus { if !hasPack || len(req.Commands) == 0 || req.Commands[0].Name != tempRef { return nil } packPushes++ if packPushes != 2 { return nil } report := packp.NewReportStatus() report.UnpackStatus = "ok" for _, cmd := range req.Commands { report.CommandStatuses = append(report.CommandStatuses, &packp.CommandStatus{ ReferenceName: cmd.Name, Status: "ng simulated checkpoint pack failure", }) } return report }
if _, err := Bootstrap(context.Background(), cfg); err == nil { t.Fatal("expected first bootstrap attempt to fail on checkpoint pack push") }
targetTemp, err := targetRepo.Reference(tempRef, true) if err != nil { t.Fatalf("resolve temp ref after failed checkpoint push: %v", err) } if targetTemp.Hash() != checkpoints[0] { t.Fatalf("expected temp ref at first checkpoint %s after failure, got %s", checkpoints[0], targetTemp.Hash()) } if _, err := targetRepo.Reference(targetRef, true); err == nil { t.Fatalf("expected target ref %s to remain absent after failed checkpoint push", targetRef) }
result, err := Bootstrap(context.Background(), cfg) if err != nil { t.Fatalf("bootstrap retry after checkpoint pack failure failed: %v", err) } if !result.Relay || result.RelayMode != "bootstrap-batch" { t.Fatalf("expected batched bootstrap result, got %+v", result) } if result.BatchCount >= result.PlannedBatchCount { t.Fatalf("expected resumed retry to execute fewer batches than planned, got %+v", result) } targetHead, err := targetRepo.Reference(targetRef, true) if err != nil { t.Fatalf("resolve target ref after retry: %v", err) } if targetHead.Hash() != ref.SourceHash { t.Fatalf("expected target head %s after retry, got %s", ref.SourceHash, targetHead.Hash()) } if _, err := targetRepo.Reference(tempRef, true); err == nil { t.Fatalf("expected temp ref %s to be deleted after retry", tempRef) } }
func TestBootstrap_IntegrationBatchedLightweightTagCreatesWithoutExtraPack(t *testing.T) { sourceRepo, sourceFS := newSourceRepo(t) makeLargeCommits(t, sourceRepo, sourceFS, 5, 200_000) }