Artifact 307500c22cd70008ace2e555c6e1192ac30b7fb3a460aa35793486906f50d5a3:
- File
internal/sync/clone_cone_rounds_test.go
— part of check-in
[4536dae44c]
at
2026-10-28 12:47:44
on branch perf/cone-clone-rounds
— fix(content): classify a fossil-written store's file versions from its tables
A store this library crosslinked records each blob it found to be no artifact, and classification skips the file versions among them. A store fossil wrote has no such record, so classification read every file version raw and exceeded its byte budget: on a copy of fossil-scm.org's repository it failed on every request. A cone clone's walk then kept no base it had not reached yet, sent every in-cone version whole, and fit two or three 2-4 MB sqlite3.c texts into a 16 MB round: about 390 rounds and 30 minutes, where a dense clone takes 8 rounds.
Without the record, a version that mlink names and no table an artifact's crosslink writes claims is file content too, the proof the cone walk's knownFile already relies on; a table the store lacks claims nothing. That clone now takes 12 rounds and 9 minutes, verifying the same.
Related to #465. (user: [email protected], size: 3543) [annotate] [blame] [check-ins using] [more...]
package sync import ( "bytes" "context" "fmt" "path/filepath" "testing" "time" "github.com/danmestas/go-libfossil/db" "github.com/danmestas/go-libfossil/internal/blob" "github.com/danmestas/go-libfossil/internal/cone" "github.com/danmestas/go-libfossil/internal/content" "github.com/danmestas/go-libfossil/internal/deck" libfossil "github.com/danmestas/go-libfossil/internal/fsltype" "github.com/danmestas/go-libfossil/internal/manifest" ) // TestConeCloneOfAFossilWrittenStoreSendsDeltas cone-clones a store as // fossil writes it, with no record of which blobs the crosslinker found to be // no artifact, whose in-cone file is large and has many versions, each a // delta of the next. The walk sends each version as a delta, its base ahead // of it, when the store's classification keeps the base; classifying such a // store read every file version and exceeded its byte budget, so every // version went out whole and a round held two. A cone clone of // fossil-scm.org's repository took about 390 rounds where a dense clone // takes 8. func TestConeCloneOfAFossilWrittenStoreSendsDeltas(t *testing.T) { const ( versions = 66 fileBytes = 8 << 20 // versions*fileBytes exceeds classification's expanded budget ) origin := setupSyncTestRepo(t) // SHA1 names: hashing 528 MB of versions under SHA3 cost seconds here. if _, err := origin.DB().Exec("REPLACE INTO config(name,value,mtime) VALUES('hash-policy','0',0)"); err != nil { t.Fatal(err) } var buf bytes.Buffer for n := 0; buf.Len() < fileBytes; n++ { fmt.Fprintf(&buf, "line %07d of a large in-cone file whose every version is kept\n", n) } text := buf.Bytes() when := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) var parent string var files []libfossil.FslID for i := range versions { copy(text[i*64:], fmt.Sprintf("version %04d", i)) // each version differs from the last frid, fuuid, err := blob.Store(origin.DB(), text) if err != nil { t.Fatal(err) } _, out, err := blob.Store(origin.DB(), fmt.Appendf(nil, "outside %d\n", i)) if err != nil { t.Fatal(err) } d := &deck.Deck{Type: deck.Checkin, C: fmt.Sprintf("version %d", i), U: deck.User("u"), D: when.Add(time.Duration(i) * time.Hour), F: []deck.FileCard{{Name: "doc/out.txt", UUID: out}, {Name: "src/big.c", UUID: fuuid}}} if parent != "" { d.P = []string{parent} } data, err := d.Marshal() if err != nil { t.Fatal(err) } if _, parent, err = blob.Store(origin.DB(), data); err != nil { t.Fatal(err) } files = append(files, frid) } err := origin.WithTx(func(tx *db.Tx) error { for i := 0; i+1 < versions; i++ { if _, err := content.Deltify(tx, files[i], files[i+1]); err != nil { return err } } return nil }) if err != nil { t.Fatal(err) } if _, err := manifest.Crosslink(origin); err != nil { t.Fatal(err) } if _, err := origin.DB().Exec("DROP TABLE IF EXISTS crosslink_nonartifact_v2"); err != nil { t.Fatal(err) } c, err := cone.New([]string{"src"}, nil) if err != nil { t.Fatal(err) } tr := &cfileCounter{r: origin, sent: map[string]int{}} clone, _, err := Clone(context.Background(), filepath.Join(t.TempDir(), "clone.fossil"), tr, CloneOpts{StoreCone: c}) if err != nil { t.Fatal(err) } defer clone.Close() t.Logf("%d rounds, %d artifacts", tr.rounds, len(tr.sent)) // The versions' deltas and one whole text fit a round; the clone ends // with the round that carries the clone cursor's end. if tr.rounds > 3 { t.Fatalf("cone clone took %d rounds, want at most 3", tr.rounds) } }