Judge the ask and the settle by one say of the builder (issue 1886, merge check hang)
mesh/merge-gate head bbadbb2af021 merged into trunk 289bbf68e4f9 — pass: builds mesh-controller → novox; no bus step; the change touches no module of the m…
mesh/repo-check head bbadbb2af021 merged into trunk 289bbf68e4f9 — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group fix/1886-builder-says-references delivered: every member is delivered
mesh/check failed: integration: github.com/novox/mesh-controller/internal/identity.TestACertificateChainsToTheMeshsOwnAuthority: authority_test.go:45:…

The settle read the say from the builder's bucket while the ask read it through
builderSaysReferences; where they differed (a test's say against a bus with no
bucket) a module was handed back and taken from its key again at every turn,
and the look's loop never ended.
This commit is contained in:
2026-10-23 01:59:18 +02:00
parent 1414ed7f7b
commit bbadbb2af0
3 changed files with 25 additions and 9 deletions
+22 -2
View File
@@ -127,7 +127,6 @@ func TestAnOpenWalkWaitingOnAnUnreferencedKeyIsAskedOfBuildAgent(t *testing.T) {
// Not said: handed back, and the next look asks build-agent. // Not said: handed back, and the next look asks build-agent.
p, byName, keys := walk() p, byName, keys := walk()
keys.notSaid = true
theBuilderSays(t, false, nil) theBuilderSays(t, false, nil)
asked := askingBuildAgent(t) asked := askingBuildAgent(t)
changed, err := settleKeys(t.Context(), records, keys, p, p.Tiers[0], take) changed, err := settleKeys(t.Context(), records, keys, p, p.Tiers[0], take)
@@ -154,7 +153,8 @@ func TestAFailedKeyOfABuilderThatDoesNotSayFailsNoWalk(t *testing.T) {
askModule(t.Context(), p, "sudo", byName) askModule(t.Context(), p, "sudo", byName)
_ = byName _ = byName
fp := "src2:" + strings.Repeat("f", 64) fp := "src2:" + strings.Repeat("f", 64)
keys := planKeysOf{notSaid: true, values: map[string]sdkdelivery.Module{"sudo@c0ffee0123456789": {FP2: fp}}, theBuilderSays(t, false, nil)
keys := planKeysOf{values: map[string]sdkdelivery.Module{"sudo@c0ffee0123456789": {FP2: fp}},
keys: map[string]builderkeys.Key{"sudo": {Module: "sudo", Fingerprint: fp, Found: true, keys: map[string]builderkeys.Key{"sudo": {Module: "sudo", Fingerprint: fp, Found: true,
Build: sdkdelivery.BuilderBuild{State: sdkdelivery.BuildFailed, Error: "not stored: tools: no reference"}}}} Build: sdkdelivery.BuilderBuild{State: sdkdelivery.BuildFailed, Error: "not stored: tools: no reference"}}}}
if _, err := settleKeys(t.Context(), recordsOf{}, keys, p, p.Tiers[0], func(string, sdkdelivery.Module, string) {}); err != nil { if _, err := settleKeys(t.Context(), recordsOf{}, keys, p, p.Tiers[0], func(string, sdkdelivery.Module, string) {}); err != nil {
@@ -221,3 +221,23 @@ func TestTheAskGuardUsesTheSayTheWalkRead(t *testing.T) {
} }
} }
} }
// 1886 merge check hang: the look must judge an ask and a settle by one say. A settle that read the say from another
// source than the ask (the bus's bucket, against the walk's read) handed a module back, the next ask took it from its key
// again, and the look's loop never ended: walk_from_keys_integration_test hung.
func TestTheSettleJudgesByTheSameSayAsTheAsk(t *testing.T) {
theBuilderSays(t, true, nil)
p, _ := aWalkOfOne("sudo")
p.Modules["sudo"] = &inventory.PlanModule{}
askModule(t.Context(), p, "sudo", map[string]inventory.Entry{"sudo": {Source: inventory.Source{Repository: "novox/mesh-catalog", Path: "modules/sudo", Ref: "main"}}})
fp := "src2:" + strings.Repeat("1", 64)
keys := planKeysOf{values: map[string]sdkdelivery.Module{"sudo@c0ffee0123456789": {FP2: fp}},
keys: map[string]builderkeys.Key{"sudo": {Module: "sudo", Fingerprint: fp, Found: true,
Build: sdkdelivery.BuilderBuild{State: sdkdelivery.BuildBuilding, By: "shanks"}}}}
if _, err := settleKeys(t.Context(), recordsOf{}, keys, p, p.Tiers[0], func(string, sdkdelivery.Module, string) {}); err != nil {
t.Fatal(err)
}
if s := p.Modules["sudo"]; !s.Key || s.State != "asked" {
t.Fatalf("the settle judged by another say than the ask's: %+v", s)
}
}
+3 -3
View File
@@ -47,8 +47,6 @@ var buildersOwn = func(module string) bool { return buildAgentsOwn[module] }
type planKeyReads interface { type planKeyReads interface {
ModuleAt(ctx context.Context, module, commit string) (sdkdelivery.Module, error) ModuleAt(ctx context.Context, module, commit string) (sdkdelivery.Module, error)
Build(ctx context.Context, module, fp string) (builderkeys.Key, error) Build(ctx context.Context, module, fp string) (builderkeys.Key, error)
// SaysReferences is whether a running builder says it writes references (builderkeys.Reader.SaysReferences).
SaysReferences(ctx context.Context) (bool, error)
} }
// builderSayKey carries, on an ask's context, the builder's say the walk read before asking (withBuilderSay). // builderSayKey carries, on an ask's context, the builder's say the walk read before asking (withBuilderSay).
@@ -135,7 +133,9 @@ func settleKeys(ctx context.Context, inv keyRecords, keys planKeyReads, p *inven
changed = true changed = true
} }
} }
says, err := keys.SaysReferences(ctx) // One say for the ask and the settle (builderSaysReferences): two sources that differ hand a module back and take it
// from its key again at every turn of the look, which then never ends.
says, err := builderSaysReferences(ctx)
if err != nil { if err != nil {
return changed, fmt.Errorf("whether the builder writes references cannot be read: %w", err) return changed, fmt.Errorf("whether the builder writes references cannot be read: %w", err)
} }
-4
View File
@@ -16,12 +16,8 @@ import (
type planKeysOf struct { type planKeysOf struct {
values map[string]sdkdelivery.Module // by module + "@" + commit values map[string]sdkdelivery.Module // by module + "@" + commit
keys map[string]builderkeys.Key keys map[string]builderkeys.Key
// notSaid is a builder that does not say it writes references (issue 1886); the zero value says it.
notSaid bool
} }
func (k planKeysOf) SaysReferences(context.Context) (bool, error) { return !k.notSaid, nil }
func (k planKeysOf) ModuleAt(_ context.Context, module, commit string) (sdkdelivery.Module, error) { func (k planKeysOf) ModuleAt(_ context.Context, module, commit string) (sdkdelivery.Module, error) {
if v, ok := k.values[module+"@"+commit]; ok { if v, ok := k.values[module+"@"+commit]; ok {
return v, nil return v, nil