Artifact 14b6de248928aa77a8082db88b3977b78fb801df180d81ac14f8c7941c7ed7f9:
- File
conformance/sparse_round_walk_test.go
— part of check-in
[2909f2a676]
at
2026-10-21 12:33:15
on branch feat/submanifest-reader
— feat(sync): walk a sparse clone's keep set per round
A cone-scoped clone no longer classifies the whole held store before its first round. Each round reads only the rows it reaches: structural artifacts go out at their row, and a check-in's row walks its tree, descending only nodes whose key range meets the cone and that its primary parent's tree does not carry over the same range, then sends the in-cone nodes and blobs that walk reaches. Content and sub-manifest rows wait for those walks and are charged as excluded rows are, so a round's work tracks its batch. A check-in whose walk outgrows a round resumes in the next one from progress kept in the clone-cap cookie. Delta bases the round did not reach are served in full form. The walk keeps the depth and visited-node bounds per request.
A round spent wholly inside an excluded run sends nothing yet advances its cursor; a coned clone now continues on that progress instead of reporting that the server stopped sending.
HandleEnd reports the artifacts a cone request classified. (user: [email protected], size: 2689) [annotate] [blame] [check-ins using]
package conformance import ( "fmt" "strings" "testing" "github.com/danmestas/go-libfossil/internal/content" "github.com/danmestas/go-libfossil/internal/hash" "github.com/danmestas/go-libfossil/internal/repo" "github.com/danmestas/go-libfossil/internal/sync" "github.com/danmestas/go-libfossil/internal/xfer" ) // scOneArtifactRounds shrinks every clone batch to one artifact, so each // check-in and each artifact its walk reaches lands in a round of its own. type scOneArtifactRounds struct{} func (scOneArtifactRounds) Check(site string, _ float64) bool { return site == "handler.emitCloneBatch.smallBatch" } func scProtocolParentDiffFlat(t *testing.T) error { return scProtocolParentDiff(t, newSCSyncRepo(t, scSyncFiles()), scSyncFiles()) } func scProtocolParentDiffTree(t *testing.T) error { return scProtocolParentDiff(t, newSCSyncTreeRepo(t), scSyncTreeFiles()) } // A check-in sends only what its primary parent's tree does not hold: an // unchanged in-cone file crosses the wire once however many check-ins name // it, while a check-in whose walk outgrows a round resumes in the next. func scProtocolParentDiff(t *testing.T, origin *scRepo, base map[string]string) error { parent := origin.c1 for i := 0; i < 3; i++ { base[fmt.Sprintf("src/v%d", i)] = fmt.Sprintf("version %d\n", i) base[fmt.Sprintf("zzz/v%d", i)] = fmt.Sprintf("outside %d\n", i) parent = origin.commit(t, base, parent) } peer := &scSyncPeer{r: origin.r, opts: sync.HandleOpts{Buggify: scOneArtifactRounds{}}} r, _ := scSyncClone(t, peer, sync.CloneOpts{ StoreCone: scCone(t, []string{"src"}, nil), PullPhantoms: true, }) unchanged := scTextUUID(origin.r, "inside\n") sent, resumed := 0, false for _, resp := range peer.replies { if _, ok := scSyncPayloads(resp)[unchanged]; ok { sent++ } for _, card := range resp.Cards { if c, ok := card.(*xfer.CookieCard); ok && strings.Count(c.Value, ":") == 3 { resumed = true } } } if sent != 1 || !resumed { return fmt.Errorf("unchanged in-cone file sent %d times, walk resumed=%v; want once, true", sent, resumed) } for text, want := range map[string]bool{"inside\n": true, "version 2\n": true, "outside 2\n": false} { _, held, err := content.CheckAvailableByUUID(r.DB(), scTextUUID(origin.r, text)) if err != nil || held != want { return fmt.Errorf("holding %q = %v, want %v (error %v)", text, held, want, err) } } return nil } // scTextUUID names text as origin stored it, under either hash. func scTextUUID(origin *repo.Repo, text string) string { uuid := hash.SHA3([]byte(text)) if _, ok := content.AvailableByUUID(origin.DB(), uuid); ok { return uuid } return hash.SHA1([]byte(text)) }