Judge a seat's holder silent only on verbs it must serve (hq issue 299)
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer delivery to the same trunk took over its walk
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer delivery to the same trunk took over its walk
node-uplink gained its first verbs in #116, both optional while its holders catch up (ADR 0246). D3 read any verb as one the holder must answer for, and raised a silent condition on every machine holding the seat. A seat whose verbs are all optional now asks nothing of its holders' silence.
This commit is contained in:
@@ -419,8 +419,9 @@ func askResolver(ctx context.Context, at, name string, kind dnsmessage.Type) ([]
|
||||
return addresses, answer.RCode, nil
|
||||
}
|
||||
|
||||
// probeHolders is D3: every seat that serves verbs has, on every machine that holds it and is heard
|
||||
// from, a holder answering the bus's discovery for that seat. A machine past its heartbeat's bound is
|
||||
// probeHolders is D3: every seat with a verb its holder must serve has, on every machine that holds it
|
||||
// and is heard from, a holder answering the bus's discovery for that seat. A seat whose verbs are all
|
||||
// still optional is not asked about (holdingNeedsAnAnswer). A machine past its heartbeat's bound is
|
||||
// S1's, and is not asked about here.
|
||||
func probeHolders(ctx context.Context, d *doctor) ([]conditions.Observation, error) {
|
||||
entries, err := d.open.inventory.Catalogued(ctx)
|
||||
@@ -436,44 +437,7 @@ func probeHolders(ctx context.Context, d *doctor) ([]conditions.Observation, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
now := time.Now()
|
||||
expected := map[string]map[string]bool{} // seat → machine
|
||||
add := func(seat, node string) {
|
||||
if expected[seat] == nil {
|
||||
expected[seat] = map[string]bool{}
|
||||
}
|
||||
expected[seat][node] = true
|
||||
}
|
||||
for _, s := range catalogue.SeatsWithAProtocol() {
|
||||
if len(s.Serves) == 0 || s.Name == catalogue.ControllerSeatName {
|
||||
continue // a seat with no verb has nothing to answer with; this controller is answering now
|
||||
}
|
||||
onRecord := false
|
||||
for _, h := range recorded {
|
||||
if h.Claim == s.Name && heard[h.Node] {
|
||||
add(s.Name, h.Node)
|
||||
onRecord = true
|
||||
}
|
||||
}
|
||||
if onRecord && s.Scope == catalogue.ScopeMesh {
|
||||
continue
|
||||
}
|
||||
for _, e := range entries {
|
||||
for _, c := range e.Manifest.Claims {
|
||||
if c.Name != s.Name {
|
||||
continue
|
||||
}
|
||||
for _, node := range e.On {
|
||||
// Assigned is not there yet: a holder is expected once its machine was sent it and
|
||||
// had time to report, never between `assign` and `push` (novox/hq issue 275).
|
||||
if heard[node] && (s.Scope == catalogue.ScopeNode || !onRecord) &&
|
||||
delivered[node].settled(e.Manifest.Module, now) {
|
||||
add(s.Name, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
expected := holdersToHear(catalogue.SeatsWithAProtocol(), recorded, entries, heard, delivered, time.Now())
|
||||
if len(expected) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -509,6 +473,11 @@ func probeHolders(ctx context.Context, d *doctor) ([]conditions.Observation, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return silentHolders(expected, answering), nil
|
||||
}
|
||||
|
||||
// silentHolders is every expected holder the discovery did not hear, as D3's observations.
|
||||
func silentHolders(expected, answering map[string]map[string]bool) []conditions.Observation {
|
||||
var out []conditions.Observation
|
||||
for seat, nodes := range expected {
|
||||
for node := range nodes {
|
||||
@@ -521,7 +490,71 @@ func probeHolders(ctx context.Context, d *doctor) ([]conditions.Observation, err
|
||||
Said: fmt.Sprintf("no answer for %s from %s to the bus's discovery", seat, node)})
|
||||
}
|
||||
}
|
||||
return sortedFound(out), nil
|
||||
return sortedFound(out)
|
||||
}
|
||||
|
||||
// holdersToHear is D3's expectation: seat → every machine whose holder of it must answer the bus's
|
||||
// discovery. A seat is listed only if holding it requires serving a verb (holdingNeedsAnAnswer); a holder
|
||||
// is expected on a machine that is heard from and that holds it on record or — for a node seat, or a mesh
|
||||
// seat nobody holds on record — was sent the module that claims it and had time to report.
|
||||
func holdersToHear(seats []catalogue.Seat, recorded []catalogue.Held, entries []inventory.Entry,
|
||||
heard map[string]bool, delivered map[string]lastSend, now time.Time) map[string]map[string]bool {
|
||||
expected := map[string]map[string]bool{} // seat → machine
|
||||
add := func(seat, node string) {
|
||||
if expected[seat] == nil {
|
||||
expected[seat] = map[string]bool{}
|
||||
}
|
||||
expected[seat][node] = true
|
||||
}
|
||||
for _, s := range seats {
|
||||
if s.Name == catalogue.ControllerSeatName {
|
||||
continue // this controller is answering now
|
||||
}
|
||||
if !holdingNeedsAnAnswer(s) {
|
||||
continue // nothing its holder must serve, so nothing its silence would say (novox/hq issue 299)
|
||||
}
|
||||
onRecord := false
|
||||
for _, h := range recorded {
|
||||
if h.Claim == s.Name && heard[h.Node] {
|
||||
add(s.Name, h.Node)
|
||||
onRecord = true
|
||||
}
|
||||
}
|
||||
if onRecord && s.Scope == catalogue.ScopeMesh {
|
||||
continue
|
||||
}
|
||||
for _, e := range entries {
|
||||
for _, c := range e.Manifest.Claims {
|
||||
if c.Name != s.Name {
|
||||
continue
|
||||
}
|
||||
for _, node := range e.On {
|
||||
// Assigned is not there yet: a holder is expected once its machine was sent it and
|
||||
// had time to report, never between `assign` and `push` (novox/hq issue 275).
|
||||
if heard[node] && (s.Scope == catalogue.ScopeNode || !onRecord) &&
|
||||
delivered[node].settled(e.Manifest.Module, now) {
|
||||
add(s.Name, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return expected
|
||||
}
|
||||
|
||||
// holdingNeedsAnAnswer says whether a seat's holder is judged silent when the bus's discovery hears
|
||||
// nothing from it: only when the seat has a verb its holder must serve. **Silence is judged on required
|
||||
// verbs alone** (novox/hq issue 299). An optional verb is one the seat's holders are still catching up
|
||||
// to (Verb.Optional; design 33 §7, novox/hq ADR 0246): a holder that does not serve it yet holds the seat
|
||||
// rightly, and a holder serving nothing because every verb is still optional is exactly that holder, not
|
||||
// a silent one. A seat with no verb at all has nothing to answer with either.
|
||||
func holdingNeedsAnAnswer(s catalogue.Seat) bool {
|
||||
for _, v := range s.Serves {
|
||||
if !v.Optional {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// reportGrace is how long a machine is given, after it was sent a declaration, to apply it and report
|
||||
|
||||
Reference in New Issue
Block a user