Give each caller of a pass its own nodes' errors and write the grants whenever asked; a wake during a take-in makes it look once more (issue 1945)
mesh/delivery closed: a newer head of the same pull request
mesh/check pass 8114cee2af47 on 0fff3efd4e0b | unit 959 run, 172 kept, 2 skipped | integration 1615 run, 236 kept, 7 skipped | over bound: integration…

This commit is contained in:
2026-10-25 12:44:28 +01:00
parent baa8f58473
commit 8114cee2af
5 changed files with 175 additions and 33 deletions
+45 -16
View File
@@ -26,29 +26,52 @@ import (
// every refusal is named in the error. It answers the keys it wrote, by node. Under the lease only: a controller that
// does not hold it writes nothing and says so.
func composeModules(ctx context.Context, open *stores, nodes []string) (map[string][]string, error) {
out, byNode := composeModulesByNode(ctx, open, nodes)
names := make([]string, 0, len(byNode))
for n := range byNode {
names = append(names, n)
}
sort.Strings(names)
var errs []error
for _, n := range names {
errs = append(errs, byNode[n])
}
return out, errors.Join(errs...)
}
// composeModulesByNode is composeModules with each node's error apart: a failure before any node is composed is every
// node's (the composer gives each caller only the errors of the nodes it asked for, issue 1945).
func composeModulesByNode(ctx context.Context, open *stores, nodes []string) (map[string][]string, map[string]error) {
all := func(err error) (map[string][]string, map[string]error) {
byNode := map[string]error{}
for _, n := range nodes {
byNode[n] = err
}
return nil, byNode
}
if _, err := theLease.epoch(ctx); err != nil {
return nil, fmt.Errorf("nothing is composed: %w", err)
return all(fmt.Errorf("nothing is composed: %w", err))
}
bus, err := aBus()
if err != nil {
return nil, fmt.Errorf("nothing is composed: the bus: %w", err)
return all(fmt.Errorf("nothing is composed: the bus: %w", err))
}
defer bus.Close()
js, err := jetstream.New(bus.Conn())
if err != nil {
return nil, err
return all(err)
}
sign, err := controllerSigner(ctx, open)
if err != nil {
return nil, fmt.Errorf("nothing is composed: the controller's signing key: %w", err)
return all(fmt.Errorf("nothing is composed: the controller's signing key: %w", err))
}
fps, err := currentFingerprints(ctx, open.inventory)
if err != nil {
return nil, err
return all(err)
}
entries, err := open.inventory.Catalogued(ctx)
if err != nil {
return nil, err
return all(err)
}
manifests := make(map[string]catalogue.Manifest, len(entries))
for _, e := range entries {
@@ -56,7 +79,7 @@ func composeModules(ctx context.Context, open *stores, nodes []string) (map[stri
}
upgrades, err := open.inventory.Upgrades(ctx)
if err != nil {
return nil, err
return all(err)
}
policy := func(module string) (string, string, string) {
u, ok := upgrades[module]
@@ -67,7 +90,7 @@ func composeModules(ctx context.Context, open *stores, nodes []string) (map[stri
}
sort.Strings(nodes)
out := map[string][]string{}
var errs []error
errsOf := map[string][]error{}
for _, node := range nodes {
// One composition of a node at a time in this process: two at once collided on its keys' revisions (issue 1945).
unlock := lockComposing(node)
@@ -75,7 +98,7 @@ func composeModules(ctx context.Context, open *stores, nodes []string) (map[stri
defer unlock()
at, err := openComposedPlace(ctx, js, bus.Conn(), node)
if err != nil {
errs = append(errs, fmt.Errorf("%s: %w", node, err))
errsOf[node] = append(errsOf[node], fmt.Errorf("%s: %w", node, err))
return
}
at.sign, at.policy = sign, policy
@@ -84,21 +107,21 @@ func composeModules(ctx context.Context, open *stores, nodes []string) (map[stri
}
declared, err := composeSend(ctx, open, node)
if err != nil {
errs = append(errs, fmt.Errorf("%s: %w", node, err))
errsOf[node] = append(errsOf[node], fmt.Errorf("%s: %w", node, err))
return
}
body, err := compositionBody(declared)
if err != nil {
errs = append(errs, fmt.Errorf("%s: %w", node, err))
errsOf[node] = append(errsOf[node], fmt.Errorf("%s: %w", node, err))
return
}
// The provisioner's plans of the modules the node's composition hands to it, for the assignments mirror.
if err := open.inventory.RecordComposedPlans(ctx, node, plansKept(declared)); err != nil {
errs = append(errs, fmt.Errorf("%s: its provisioner's plans: %w", node, err))
errsOf[node] = append(errsOf[node], fmt.Errorf("%s: its provisioner's plans: %w", node, err))
}
comps, moved, err := compositionsOf(node, body, fps, manifests, declared.modules)
if err != nil {
errs = append(errs, fmt.Errorf("%s: %w", node, err))
errsOf[node] = append(errsOf[node], fmt.Errorf("%s: %w", node, err))
return
}
if len(moved) > 0 {
@@ -116,15 +139,21 @@ func composeModules(ctx context.Context, open *stores, nodes []string) (map[stri
// And where a data-keeping provision bound each consumer: what the next resolution keeps it at (novox/hq ADR 0232).
if len(declared.Bindings) > 0 {
if err := open.inventory.RecordBindings(ctx, node, declared.Bindings); err != nil {
errs = append(errs, fmt.Errorf("%s: where its consumers are bound to their data: %w", node, err))
errsOf[node] = append(errsOf[node], fmt.Errorf("%s: where its consumers are bound to their data: %w", node, err))
}
}
if err != nil {
errs = append(errs, fmt.Errorf("%s: %w", node, err))
errsOf[node] = append(errsOf[node], fmt.Errorf("%s: %w", node, err))
}
}()
}
return out, errors.Join(errs...)
byNode := map[string]error{}
for node, errs := range errsOf {
if len(errs) > 0 {
byNode[node] = errors.Join(errs...)
}
}
return out, byNode
}
// composeAndSay is composeModules for an act that has nothing to do with its answer but say it: what each former send