Keep builds on the bus under their module, so no lookup reads the bucket whole (issue 1121, ADR 0427)
mesh/merge-gate head c79591531575 merged into trunk aa02e7bf4e78 — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head c79591531575 merged into trunk aa02e7bf4e78 — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/merge-gate head c79591531575 merged into trunk aa02e7bf4e78 — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head c79591531575 merged into trunk aa02e7bf4e78 — pass: its merge-check.sh passed
mesh/delivery delivered
Milestone 5 moved 8,599 build records onto the bus under bare build ids, each carrying its module's whole manifest. Every build lookup then read the whole bucket, and a delivery plan's send did that per module per machine while it held the plans and the machine's declaration: the send of build-agent never finished, and every other act and the status waited behind it. Builds now live under <module>.build.<id> (requests under req.<id>, id.<id> names a build's module), with pointer keys newest, worked, held, commit.<sha> and source.<reuse key> that move only to a newer build and are read as hints, repaired when wrong. Manifests are kept once in the same bucket under manifest.sha256-<hex>, so no grant the running bus lacks is needed. The old keys are laid out once at start, idempotently, and left unread. The calls a plan look makes on the silent clocks while it holds the plans are bounded, since the vendored kv.Keys ignores its context.
This commit is contained in:
@@ -179,3 +179,32 @@ func TestKeysThatCannotBeReadAreWatchedByTimersAndTheLookFails(t *testing.T) {
|
||||
|
||||
// A plan moved by a process the serving controller did not spawn — a verb run at the control node's terminal — is
|
||||
// looked at: the controller follows its own plan-moved (issue 974, round 1).
|
||||
|
||||
// A clock whose bus answers nothing, ever: a consumer lost when the broker reloaded its user list, a key listing that
|
||||
// never reaches its end.
|
||||
type hungClock struct{ silentClock }
|
||||
|
||||
func (hungClock) Held(context.Context) (map[silentWait]time.Time, error) {
|
||||
select {} // never answers, whatever the context says (the vendored Keys reads its watcher without it)
|
||||
}
|
||||
|
||||
// issue 1121: the look holds the plans while it keeps the silent keys, so a key listing that never ends held the plans
|
||||
// and every node for good. Every call to the clock has a bound, and the wait is looked at by a timer of its own.
|
||||
func TestAClockThatNeverAnswersDoesNotHoldTheLookForever(t *testing.T) {
|
||||
was := silentCallBound
|
||||
silentCallBound = 50 * time.Millisecond
|
||||
t.Cleanup(func() { silentCallBound = was })
|
||||
w := silentWait{Plan: "plan-hung", Tier: 0, Node: "ace"}
|
||||
sent := time.Now().UTC()
|
||||
t.Cleanup(func() { silentTimers.mu.Lock(); delete(silentTimers.timers, w); silentTimers.mu.Unlock() })
|
||||
done := make(chan error, 1)
|
||||
go func() { done <- keepSilentKeys(t.Context(), hungClock{}, map[silentWait]time.Time{w: sent}, sent) }()
|
||||
select {
|
||||
case err := <-done:
|
||||
if err == nil {
|
||||
t.Fatal("a clock that never answered was reported as fine")
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("the look is still waiting for a clock that never answers")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user