Artifact 069aec0e9f89ab78dada6dc9f46e522f9b64e387a29336eecf845c7f880c1abf:
- File
internal/sync/clone_corpus_test.go
— part of check-in
[b9ec3c086e]
at
2026-10-23 06:03:21
on branch feat/horizon-cones
— perf(serve): memoize a clone request's forwardable sources
forwardableSource reads a row, its shun and private state and the tree gate each time it is asked, and a clone round asks about the same rows from every chain walk and every sentEarlier search: a twelfth of a dense clone's CPU. A request reads one snapshot, so the verdicts are kept for its duration.
TestCorpusClone logs an origin error, which its client sees only as an HTTP status. (user: [email protected], size: 4176) [annotate] [blame] [check-ins using]
package sync import ( "context" "net/http/httptest" "os" "path/filepath" "strings" "syscall" "testing" "time" "github.com/danmestas/go-libfossil/internal/auth" "github.com/danmestas/go-libfossil/internal/cone" "github.com/danmestas/go-libfossil/internal/repo" "github.com/danmestas/go-libfossil/internal/xfer" ) // TestCorpusClone clones a copy of the repository LIBFOSSIL_CORPUS names over // HTTP from an in-process handler, once per mode LIBFOSSIL_CORPUS_CLONE lists // (comma separated: dense, cone, content, structural; default dense), and // logs each clone's wall and CPU time. It exists to be run with -cpuprofile. // CPU is the process's, server and client together. LIBFOSSIL_CORPUS_DEADLINE // (a duration) bounds each clone, so a slow one still writes its profile; // LIBFOSSIL_CORPUS_OUT keeps the clones in that directory for comparison. func TestCorpusClone(t *testing.T) { corpus := os.Getenv("LIBFOSSIL_CORPUS") if corpus == "" { t.Skip("set LIBFOSSIL_CORPUS to a .fossil repository to run") } modes := strings.Split(os.Getenv("LIBFOSSIL_CORPUS_CLONE"), ",") if modes[0] == "" { modes = []string{"dense"} } var deadline time.Duration if s := os.Getenv("LIBFOSSIL_CORPUS_DEADLINE"); s != "" { d, err := time.ParseDuration(s) if err != nil { t.Fatalf("LIBFOSSIL_CORPUS_DEADLINE: %v", err) } deadline = d } out := os.Getenv("LIBFOSSIL_CORPUS_OUT") if out == "" { out = t.TempDir() } work := filepath.Join(t.TempDir(), "origin.fossil") if err := corpusCopy(work, corpus); err != nil { t.Fatalf("copy corpus: %v", err) } origin, err := repo.Open(work) if err != nil { t.Fatalf("open corpus: %v", err) } t.Cleanup(func() { origin.Close() }) if err := auth.UpdateCaps(origin.DB(), "nobody", "gijoryz"); err != nil { t.Fatal(err) } var tip string if err := origin.DB().QueryRow(`SELECT b.uuid FROM event e JOIN blob b ON b.rid=e.objid WHERE e.type='ci' ORDER BY e.mtime DESC LIMIT 1`).Scan(&tip); err != nil { t.Fatal(err) } horizon := corpusAncestor(t, origin, tip, 1000) // A failed request reaches the client only as an HTTP status. serve := func(ctx context.Context, r *repo.Repo, req *xfer.Message) (*xfer.Message, error) { resp, err := HandleSync(ctx, r, req) if err != nil { t.Logf("origin: %v", err) } return resp, err } server := httptest.NewServer(XferHandler(origin, serve)) t.Cleanup(server.Close) for _, mode := range modes { t.Run(mode, func(t *testing.T) { storeCone := corpusCloneCone(t, mode, horizon) ctx := context.Background() if deadline > 0 { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, deadline) defer cancel() } path := filepath.Join(out, mode+".fossil") cpu0, wall0 := processCPU(t), time.Now() r, res, err := Clone(ctx, path, &HTTPTransport{URL: server.URL}, CloneOpts{StoreCone: storeCone}) cpu, wall := processCPU(t)-cpu0, time.Since(wall0) if err != nil { t.Fatalf("%s clone after %s wall, %s CPU: %v", mode, wall, cpu, err) } defer r.Close() t.Logf("%s clone: %d rounds, %d blobs, %s wall, %s CPU", mode, res.Rounds, res.BlobsRecvd, wall.Round(time.Millisecond), cpu.Round(time.Millisecond)) }) } } // corpusCloneCone is the store cone a TestCorpusClone mode clones with. func corpusCloneCone(t *testing.T, mode, horizon string) *cone.Cone { t.Helper() var c *cone.Cone var err error switch mode { case "dense": return nil case "cone": c, err = cone.New([]string{"src"}, nil) case "content", "structural": depth := cone.DepthContent if mode == "structural" { depth = cone.DepthStructural } var h cone.Horizon if h, err = cone.NewHorizon(horizon, cone.RuleDescendants, depth); err == nil { c, err = cone.NewWithHorizon(nil, nil, h) } default: t.Fatalf("unknown clone mode %q", mode) } if err != nil { t.Fatal(err) } return c } // processCPU is the user and system time this process has used. func processCPU(t *testing.T) time.Duration { t.Helper() var ru syscall.Rusage if err := syscall.Getrusage(syscall.RUSAGE_SELF, &ru); err != nil { t.Fatal(err) } return time.Duration(ru.Utime.Nano() + ru.Stime.Nano()) }