compose-candidate reads each manifest once and answers within the bus's reply window
Off the control-node an ask of compose-candidate ran into its 60 s bound: each of its two compositions read every manifest in the mesh one key at a time over the bus, on scratch copies of the shadow that kept nothing, so each ask read them all twice again (about 35 s per composition on ace, g14 and shanks; 2 s on novox). The answer at 60 s then came after the bus's one-minute reply window (allow_responses ttl), so the bus refused it and the checker heard nothing. Now the manifests are read eight at a time into the shadow before either composition, a scratch copy keeps what never changes (manifest, archive, done build) in the shadow too, the candidate is laid over a copy taken after the composition without it, and the engine answers within 50 s: unready, naming what it waits on, when not done. Every answer is said in one journal line with its reads and its longest wait. Issue 1126. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -97,6 +97,8 @@ type Env struct {
|
||||
// ComposedState is this node's composition state `composed-<node>` (novox/hq ADR 0593 §6), where every composition
|
||||
// a pass makes is published for its readers (publishComposed). Nil keeps none.
|
||||
ComposedState ComposedStore
|
||||
// Now is the clock what the composer waits on is timed by; nil is time.Now.
|
||||
Now func() time.Time
|
||||
}
|
||||
|
||||
// VaultSecretChanged is the vault's event that a secret changed: a name and its generation, never a value.
|
||||
@@ -141,10 +143,16 @@ type Composer struct {
|
||||
held []string
|
||||
// doing is what the composer waits on now (waiting), for a running answer to say (issue 1126, T2); its own lock, so
|
||||
// it is read while c.mu is held.
|
||||
doingMu sync.Mutex
|
||||
doing string
|
||||
caught bool
|
||||
rev uint64
|
||||
doingMu sync.Mutex
|
||||
doing string
|
||||
doingSince time.Time
|
||||
// waits is what the composer waited on, under doingMu: for compose-candidate's line (issue 1126).
|
||||
waits waitLog
|
||||
// origin is the composer a scratch copy was taken of (scratch): what is read once by its digest or key and never
|
||||
// changes (a manifest, a templates archive, a done build) is kept there too, so the next copy reads it no more.
|
||||
origin *Composer
|
||||
caught bool
|
||||
rev uint64
|
||||
// missed counts the reads the bus did not answer (BusFailed), for a pass or an answer to tell whether it met one:
|
||||
// a read that failed on the bus is tried again after a bound, never kept as an answer (issue 1126, 2026-10-28).
|
||||
missed atomic.Uint64
|
||||
@@ -1060,19 +1068,56 @@ func (c *Composer) keepRunning(comp delivery.Composed, running []delivery.Compos
|
||||
return delivery.KeepSeals(comp, kept, c.sameValue)
|
||||
}
|
||||
|
||||
// waiting notes what the composer waits on until the returned func is called, which restores what it waited on before.
|
||||
// waitLog is what a composer waited on: how many waits ended, the longest and what it was, and the last.
|
||||
type waitLog struct {
|
||||
n int
|
||||
longest time.Duration
|
||||
longestOn string
|
||||
lastOn string
|
||||
}
|
||||
|
||||
// now is the composer's clock (Env.Now).
|
||||
func (c *Composer) now() time.Time {
|
||||
if c.env.Now != nil {
|
||||
return c.env.Now()
|
||||
}
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// waiting notes what the composer waits on until the returned func is called, which restores what it waited on before
|
||||
// and counts the wait, timed, in waits.
|
||||
func (c *Composer) waiting(what string) func() {
|
||||
began := c.now()
|
||||
c.doingMu.Lock()
|
||||
before := c.doing
|
||||
c.doing = what
|
||||
before, beforeSince := c.doing, c.doingSince
|
||||
c.doing, c.doingSince = what, began
|
||||
c.doingMu.Unlock()
|
||||
return func() {
|
||||
took := c.now().Sub(began)
|
||||
c.doingMu.Lock()
|
||||
c.doing = before
|
||||
c.doing, c.doingSince = before, beforeSince
|
||||
c.waits.n++
|
||||
c.waits.lastOn = what
|
||||
if took > c.waits.longest || c.waits.longestOn == "" {
|
||||
c.waits.longest, c.waits.longestOn = took, what
|
||||
}
|
||||
c.doingMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// keepRead keeps, in c and in the composer c is a scratch copy of, what set writes: a read that never changes for its
|
||||
// digest or key. Each under its own lock, one after the other.
|
||||
func (c *Composer) keepRead(set func(*Composer)) {
|
||||
c.mu.Lock()
|
||||
set(c)
|
||||
c.mu.Unlock()
|
||||
if o := c.origin; o != nil {
|
||||
o.mu.Lock()
|
||||
set(o)
|
||||
o.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// waitingOn is what the composer waits on now, in words.
|
||||
func (c *Composer) waitingOn() string {
|
||||
c.doingMu.Lock()
|
||||
@@ -1303,9 +1348,7 @@ func (r reader) Manifest(ctx context.Context, digest string) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.c.mu.Lock()
|
||||
r.c.manifests[digest] = []byte(m.JSON)
|
||||
r.c.mu.Unlock()
|
||||
r.c.keepRead(func(k *Composer) { k.manifests[digest] = []byte(m.JSON) })
|
||||
return []byte(m.JSON), nil
|
||||
}
|
||||
|
||||
@@ -1330,9 +1373,7 @@ func (r reader) Build(ctx context.Context, module, fp string) (*delivery.Builder
|
||||
return nil, fmt.Errorf("the builder's %s: %w", key, err)
|
||||
}
|
||||
if b.State == delivery.BuildDone {
|
||||
r.c.mu.Lock()
|
||||
r.c.builds[key] = b
|
||||
r.c.mu.Unlock()
|
||||
r.c.keepRead(func(k *Composer) { k.builds[key] = b })
|
||||
}
|
||||
return &b, nil
|
||||
}
|
||||
@@ -1363,9 +1404,7 @@ func (r reader) Archive(ctx context.Context, url, digest string) ([]byte, error)
|
||||
if got := "sha256:" + hex.EncodeToString(sum[:]); got != digest {
|
||||
return nil, fmt.Errorf("%s was recorded as %s and what arrived is %s", url, digest, got)
|
||||
}
|
||||
r.c.mu.Lock()
|
||||
r.c.archives[digest] = body
|
||||
r.c.mu.Unlock()
|
||||
r.c.keepRead(func(k *Composer) { k.archives[digest] = body })
|
||||
return body, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user