Ask build-agent for every module until a running builder says it writes references (issue 1886)

R4e's own delivery plan waited for ever in tier 1: the controller took every
module but build-agent's own from a referenced builder key, and only R4e's
builder and build modules, delivered by that same plan, write references.
The builder now says so in its bucket of instances; until it does, a walk asks
build-agent as before R4e, and an open walk waiting on a key the running
builder cannot reference is judged again at the next look.
This commit is contained in:
2026-10-23 00:47:15 +02:00
parent 289bbf68e4
commit fae7710bed
9 changed files with 444 additions and 4 deletions
+37
View File
@@ -42,6 +42,20 @@ 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)
}
// builderSaysReferences is whether a running builder instance says, in its own bucket of instances, that the keys it
// writes name references (issue 1886). Until one does, a walk asks build-agent for every module, as before R4e: the
// keys the running builder writes could never be delivered from. A variable so a test needs no bus.
var builderSaysReferences = func(ctx context.Context) (bool, error) {
keys, closeKeys, err := openBuilderKeys()
if err != nil {
return false, err
}
defer closeKeys()
return keys.SaysReferences(ctx)
}
// openPlanKeys opens what a walk reads of the indexer and the builder. A variable so a test needs no bus.
@@ -102,6 +116,18 @@ func settleKeys(ctx context.Context, inv keyRecords, keys planKeyReads, p *inven
changed = true
}
}
says, err := keys.SaysReferences(ctx)
if err != nil {
return changed, fmt.Errorf("whether the builder writes references cannot be read: %w", err)
}
// **A walk never waits on a key the running builder cannot reference** (issue 1886): while no builder says it writes
// references, a module that waits on its key, or whose key failed, is handed back unasked, and the look asks it of
// build-agent. A done key with every reference is still taken.
toAgent := func(s *inventory.PlanModule, why string) {
s.State, s.Key, s.AskedAt, s.KeyAt, s.Build, s.Fingerprint = "", false, nil, "", "", ""
s.Why = "the builder does not say it writes references, so build-agent is asked (" + why + ")"
changed = true
}
for _, m := range tier {
s := p.Modules[m]
if s == nil || !s.Key || s.State != "asked" {
@@ -109,6 +135,10 @@ func settleKeys(ctx context.Context, inv keyRecords, keys planKeyReads, p *inven
}
trunk, err := keys.ModuleAt(ctx, m, s.KeyAt)
if errors.Is(err, builderkeys.ErrNoValue) {
if !says {
toAgent(s, "the indexer holds no value of it")
continue
}
set(s, fmt.Sprintf("waits for the indexer's value of %s at %s", m, orNow(s.KeyAt)))
continue
}
@@ -120,6 +150,10 @@ func settleKeys(ctx context.Context, inv keyRecords, keys planKeyReads, p *inven
if why == "" {
why = "the indexer wrote no fingerprint of the new form"
}
if !says {
toAgent(s, why)
continue
}
set(s, fmt.Sprintf("waits: %s at %s has no fingerprint to take a build by: %s", m, orNow(s.KeyAt), why))
continue
}
@@ -131,6 +165,9 @@ func settleKeys(ctx context.Context, inv keyRecords, keys planKeyReads, p *inven
return changed, err
}
switch {
case !says && (k.Failed() || !k.Done() || len(k.Unreferenced()) > 0):
toAgent(s, k.Says())
continue
case k.Failed():
// **A failed key fails the walk in the builder's own words** (`digests differ:`, `not stored:`, `not pinned:`,
// `version taken:`), and a rebuild of it is a new revision the next walk takes.