Ask build-agent until the builder says it writes references, so a plan never waits on a key nothing can reference (issue 1886, ADR 0522) #442

Merged
jschoubben merged 4 commits from fix/1886-builder-says-references into main 2026-10-23 00:27:01 +00:00
3 changed files with 25 additions and 9 deletions
Showing only changes of commit bbadbb2af0 - Show all commits
+22 -2
View File
@@ -127,7 +127,6 @@ func TestAnOpenWalkWaitingOnAnUnreferencedKeyIsAskedOfBuildAgent(t *testing.T) {
// Not said: handed back, and the next look asks build-agent.
p, byName, keys := walk()
keys.notSaid = true
theBuilderSays(t, false, nil)
asked := askingBuildAgent(t)
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)
_ = byName
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,
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 {
@@ -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 {
ModuleAt(ctx context.Context, module, commit string) (sdkdelivery.Module, 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).
@@ -135,7 +133,9 @@ func settleKeys(ctx context.Context, inv keyRecords, keys planKeyReads, p *inven
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 {
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 {
values map[string]sdkdelivery.Module // by module + "@" + commit
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) {
if v, ok := k.values[module+"@"+commit]; ok {
return v, nil