A gate judges its own send and the build it sent, and never puts the controller back behind its store (hq issue 352)
On 2026-10-09 a release's gate on the control node read the machine's
report against a newer send another plan had just made there, failed
three builds the machine had reported healthy, and put them back on
every machine to a controller older than the store's schema; that
controller then passed the newer plan's gate from its own health.
- A gate keeps what its send carried (digest, sequence) and reads the
report against it; a report on the last send is on it too.
- A gate judges only the build the machine was last sent: another build
there supersedes the judging — no verdict, nothing put back.
- A controller is told its build (MESH_CONTROLLER_VERSION, ${version}
in a process's env) and records how far it reads the store's schema;
a put-back to a build that reaches less, or never said, is refused
and the current build kept, said as urgent.
- A release's open gate holds other sends of its modules there, and a
plan's own first send waits on it.
This commit is contained in:
@@ -793,6 +793,15 @@ func advanceOnce(ctx context.Context, open *stores, p *inventory.Plan,
|
||||
case inventory.GateFailed:
|
||||
failFirstSend(ctx, open, p, m, state, state.Gate.Machines, state.Gate.Why, step.rest)
|
||||
return true, nil
|
||||
case inventory.GateSuperseded:
|
||||
// Another send moved this module on its first machine (novox/hq issue 352): the build is not
|
||||
// judged, not marked, not put back; the plan ends here, said, and a newer plan carries on.
|
||||
state.Why = "superseded: " + state.Gate.Why
|
||||
p.State = inventory.PlanSuperseded
|
||||
p.Note = fmt.Sprintf("%s's judging on %s was superseded: %s", m, strings.Join(state.Gate.Machines, ", "),
|
||||
state.Gate.Why)
|
||||
fmt.Printf("%s: %s\n", p.ID, p.Note)
|
||||
return true, nil
|
||||
case inventory.GatePassed:
|
||||
if !state.Gate.Kept {
|
||||
gatePassed(ctx, open, p, m, state)
|
||||
@@ -964,6 +973,9 @@ func firstSend(ctx context.Context, open *stores, p *inventory.Plan, node string
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
lead := modules[0]
|
||||
// What each machine was just sent, kept on the gate (novox/hq issue 352): its report is held against
|
||||
// this send, whatever it is sent after.
|
||||
sentWhat := sentNow(ctx, inv, sent)
|
||||
for _, m := range modules {
|
||||
s := p.Modules[m]
|
||||
// What the first machine ran before: what a failed gate puts back (ADR 0236).
|
||||
@@ -972,7 +984,7 @@ func firstSend(ctx context.Context, open *stores, p *inventory.Plan, node string
|
||||
}
|
||||
s.First, s.FirstAt = sent, &now
|
||||
s.Gate = &inventory.PlanGate{Component: coreComponent(m), Machines: firstRunning(sent, runningOf[m]),
|
||||
From: s.Previous, To: s.Commit, Since: &now}
|
||||
From: s.Previous, To: s.Commit, Since: &now, Sent: sentWhat}
|
||||
s.GatedBy = ""
|
||||
if m == lead {
|
||||
s.Gate.Carried = carried
|
||||
@@ -1131,8 +1143,15 @@ func nextRollout(s inventory.PlanModule, running []string, together bool, report
|
||||
var waiting, failed []string
|
||||
for _, n := range s.First {
|
||||
r, said := byNode[n]
|
||||
// Only a report about what it was last sent says anything about this build.
|
||||
if !said || r.At == nil || !r.Current {
|
||||
// Only a report about what this plan sent it — or what it was sent after that — says anything about
|
||||
// this build (novox/hq issue 352); a plan from before sends were kept on the gate reads the report
|
||||
// against the send made last, as before.
|
||||
reported := r.Current
|
||||
if s.Gate != nil && s.Gate.Sent != nil {
|
||||
sent, kept := s.Gate.Sent[n]
|
||||
reported = kept && sent.ReportsOn(r)
|
||||
}
|
||||
if !said || r.At == nil || !reported {
|
||||
waiting = append(waiting, n)
|
||||
continue
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user