What answers a requirement is applied before what asked for it
The host does not sort, so the order written here is the order a machine applies. Selection walks outward from what was assigned, which puts a consumer before the thing it pulled in — and a service that reads a file another module writes then starts before the file exists. It fails, and the next reconcile fixes it. That is the worst shape a fault can take: what gets remembered is that it works, and nobody looks again. It is 04-ISSUES/013 one level up from where that was found — there, the mesh's own computed files came after a module's resources; here, a whole module comes after the one that needed it. Nothing had hit it because no module until now both required something with resources of its own and had a resource depending on it. Writing the resolver module was what made it reachable, and it would have shown up as dnsmasq failing once on every fresh machine and working ever after. Unrelated modules keep the order selection gave them — assigned first, then what they pulled in. That order is meaningful, and reshuffling it would make every declaration's diff unreadable for no gain. Two modules requiring each other are both applied rather than refused: a cycle is not a machine that cannot work, and refusing would make a cooperating pair impossible to assign.
This commit is contained in:
@@ -422,7 +422,7 @@ func Resolve(catalogue map[string]Manifest, assigned []string, node Node, world
|
||||
}
|
||||
|
||||
resolution := Resolution{Node: node.Name, At: node.At, Because: because, Needs: needs}
|
||||
for _, n := range order {
|
||||
for _, n := range providersFirst(order, catalogue) {
|
||||
resolution.Modules = append(resolution.Modules, catalogue[n])
|
||||
}
|
||||
|
||||
@@ -569,3 +569,68 @@ func FromAnywhere(names ...string) []Offer {
|
||||
// ships and this package must not depend on the thing it resolves. The name being wrong would
|
||||
// show up as a refusal naming a module nobody can assign, which a test checks.
|
||||
const meshNetwork = "networking"
|
||||
|
||||
// providersFirst orders a node's modules so that what answers a requirement comes before what
|
||||
// asked for it.
|
||||
//
|
||||
// **The host does not sort** (novox/hq ADR 0005), so the order written here is the order a machine
|
||||
// applies. Selection walks outward from what was assigned, which puts a consumer before the thing
|
||||
// it pulled in — and a module whose service reads a file another module writes then starts before
|
||||
// the file exists.
|
||||
//
|
||||
// It fails, and the next reconcile fixes it. That is the worst shape a fault can take: what gets
|
||||
// remembered is that it works, and nobody looks again
|
||||
// ([04-ISSUES/013](novox/hq)). This is the same fault one level up from where that was found.
|
||||
//
|
||||
// **Stable where nothing requires anything.** Modules with no relation keep the order selection
|
||||
// gave them, because that order is meaningful — assigned first, then what they pulled in — and a
|
||||
// sort that reshuffled unrelated modules would make every declaration's diff unreadable.
|
||||
func providersFirst(order []string, shelf map[string]Manifest) []string {
|
||||
// Who offers what, among the modules actually chosen.
|
||||
offeredBy := map[string]string{}
|
||||
for _, name := range order {
|
||||
for _, offered := range shelf[name].Offers() {
|
||||
if _, taken := offeredBy[offered]; !taken {
|
||||
offeredBy[offered] = name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
needs := map[string][]string{}
|
||||
for _, name := range order {
|
||||
for _, want := range shelf[name].Wants() {
|
||||
provider, known := offeredBy[want]
|
||||
if !known || provider == name {
|
||||
continue
|
||||
}
|
||||
needs[name] = append(needs[name], provider)
|
||||
}
|
||||
}
|
||||
|
||||
var out []string
|
||||
placed := map[string]bool{}
|
||||
var place func(string, map[string]bool)
|
||||
place = func(name string, visiting map[string]bool) {
|
||||
if placed[name] {
|
||||
return
|
||||
}
|
||||
if visiting[name] {
|
||||
// Two modules requiring each other. Left in the order selection gave them rather
|
||||
// than refused: a cycle here is not a machine that cannot work — both are applied,
|
||||
// and one of them starts before the other is ready and settles on the next pass.
|
||||
// Refusing would make a pair of modules that cooperate impossible to assign.
|
||||
return
|
||||
}
|
||||
visiting[name] = true
|
||||
for _, provider := range needs[name] {
|
||||
place(provider, visiting)
|
||||
}
|
||||
delete(visiting, name)
|
||||
placed[name] = true
|
||||
out = append(out, name)
|
||||
}
|
||||
for _, name := range order {
|
||||
place(name, map[string]bool{})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user