diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go index 2bafb7b..846879f 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go @@ -271,7 +271,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro known.Delete(e.Key()) } if ix := started.Load(); ix != nil && strings.HasPrefix(e.Key(), index.BuilderBuiltBy+".build.") { - if err := ix.Recompute(ctx); err != nil { + if err := ix.RecomputeForBuild(ctx); err != nil { logger.Printf("computing the new-form fingerprints again after %s: %v", e.Key(), err) } } @@ -284,10 +284,6 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro go func() { errs <- follow(ctx, w, each) }() ix := &index.Indexer{Store: kvStore{candidates}, Modules: kvStore{modules}, Manifests: kvStore{manifests}, Builds: known, Logf: logger.Printf} - started.Store(ix) - if err := ix.Recompute(ctx); err != nil { // the builds heard before this, which moved no one - logger.Printf("computing the new-form fingerprints at start: %v", err) - } // The gate's source bucket (mesh-delivery-gate_source) comes with the gate (release 3e, novox/hq ADR 0450 §1): its key // main is the mesh's source revision, which stamps the modules (issue 1344). Where it does not exist yet the indexer does // not wait for it (that needs a timer, ADR 0437 §8): it says so, and is started again with the gate. @@ -313,6 +309,12 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro if err := tools.start(); err != nil { return err } + // Only now, with the toolchains read, are the builds heard to compute from (a build before it would write every Go + // module as having no toolchain reported, and back); the builds heard until now are computed once. + started.Store(ix) + if err := ix.RecomputeForBuild(ctx); err != nil { + logger.Printf("computing the new-form fingerprints at start: %v", err) + } // A build module says each time it wrote its toolchains key: its stream, and the indexer's durable consumer on it, are // made by the controller as the forge's are. Where the stream is not there yet the indexer says so and is started again // with the build module, as with the gate's source. diff --git a/modules/mesh-delivery-indexer/internal/index/toolchains.go b/modules/mesh-delivery-indexer/internal/index/toolchains.go index e699e2f..9b4dc04 100644 --- a/modules/mesh-delivery-indexer/internal/index/toolchains.go +++ b/modules/mesh-delivery-indexer/internal/index/toolchains.go @@ -397,3 +397,18 @@ func (ix *Indexer) builderInput() (src, none string) { } return "", fmt.Sprintf("%s, which builds it, has no successful trunk build that recorded a source fingerprint", BuilderBuiltBy) } + +// RecomputeForBuild is Recompute for a build of BuilderBuiltBy heard on the builds bucket, or the one at start (issue 1727). +// While the indexer has read no toolchain at all (it has just started and the toolchains buckets are not settled), every +// Go module would be written as having no toolchain reported and then back: it computes nothing then, and the settling of +// the toolchains computes them. +func (ix *Indexer) RecomputeForBuild(ctx context.Context) error { + ix.mu.Lock() + none := len(ix.toolchains) == 0 + ix.mu.Unlock() + if none { + ix.logf("the builds changed before any toolchain was read: the new-form fingerprints are computed when they are") + return nil + } + return ix.Recompute(ctx) +} diff --git a/modules/mesh-delivery-indexer/internal/index/toolchains_test.go b/modules/mesh-delivery-indexer/internal/index/toolchains_test.go index b47f2af..a60fac1 100644 --- a/modules/mesh-delivery-indexer/internal/index/toolchains_test.go +++ b/modules/mesh-delivery-indexer/internal/index/toolchains_test.go @@ -46,7 +46,11 @@ const ( ) func agentBuild(id, src, at string) [2]any { - raw := `{"id":"` + id + `","repository":"` + agentCloneURL + `","ref":"main","module":"build-agent","commit":"cada7e3c",` + + commit := "cada7e3c" + if id == "build-1792587792086548572" { + commit = "f10fbda1" + } + raw := `{"id":"` + id + `","repository":"` + agentCloneURL + `","ref":"main","module":"build-agent","commit":"` + commit + `",` + `"path":"modules/build-agent","at":"` + at + `","source-fingerprint":"` + src + `","source-inputs":` + aInputs + `}` return [2]any{delivery.BuildKey("build-agent", id), []byte(raw)} } @@ -63,10 +67,7 @@ func newFormIndexer(t *testing.T, mods ...mod) (*Indexer, *memStore) { if err := ix.Apply(context.Background(), f); err != nil { t.Fatal(err) } - // build-agent's value of today's form, from a merge of the controller (modules.build-agent holds its fingerprint). - if err := ix.Apply(context.Background(), mergedMsg(buildAgentRepo, map[string]string{"modules/build-agent": "agent-tree-1"})); err != nil { - t.Fatal(err) - } + // build-agent holds no fingerprint of today's form, as live (no merge of the controller is applied; issue 1727). return ix, store } @@ -189,6 +190,25 @@ func TestBasesAndTheBuilderAreComputedBeforeTheModulesTakingThem(t *testing.T) { } } +func TestABuildHeardBeforeAnyToolchainIsReadDropsNoFingerprint(t *testing.T) { // 1727 (review: a restart flapped every FP2) + ix, mods := newFormIndexer(t, builderModules(t)...) + reportToolchain(t, ix, "mesh-build-go", "novox", "go", "go1.27.1") + before, _ := moduleOf(t, mods, "mesh-tools") + if before.FP2 == "" { + t.Fatalf("mesh-tools has no fp2 to begin with: %+v", before) + } + // a restarted indexer has read no toolchains bucket yet + ix.mu.Lock() + ix.toolchains = nil + ix.mu.Unlock() + if err := ix.RecomputeForBuild(context.Background()); err != nil { + t.Fatal(err) + } + if m, _ := moduleOf(t, mods, "mesh-tools"); m.FP2 != before.FP2 || m.None2 != "" { + t.Errorf("mesh-tools: fp2 = %q none2 = %q, want fp2 %q kept", m.FP2, m.None2, before.FP2) + } +} + func TestTheBuilderHasNoFingerprintWithoutATrunkBuildOfBuildAgentAndSaysSo(t *testing.T) { // 1727 ix, mods := newFormIndexer(t, builderModules(t)...) reportToolchain(t, ix, "mesh-build-go", "novox", "go", "go1.27.1")