From dd120b1c90566814cb8ebcbc8aba44e4ed76206a Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 12:12:01 +0100 Subject: [PATCH] CAT-RD3: mesh-build-go asks the indexer's read tool for the manifest a compose ask names, no reads of the indexer --- .../cmd/mesh-build-go/compose.go | 27 +++++++++++++++-- .../cmd/mesh-build-go/compose_test.go | 7 +++-- .../mesh-build-go/cmd/mesh-build-go/main.go | 2 +- modules/mesh-build-go/module.json | 30 ++++++++++++++----- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/modules/mesh-build-go/cmd/mesh-build-go/compose.go b/modules/mesh-build-go/cmd/mesh-build-go/compose.go index 524ada97d..f8b397402 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/compose.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/compose.go @@ -24,14 +24,16 @@ import ( "sync" "git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep" + stdio "git.novox.be/novox/mesh-sdk/go" sdkstep "git.novox.be/novox/mesh-sdk/go/buildstep" "git.novox.be/novox/mesh-sdk/go/composer" "git.novox.be/novox/mesh-sdk/go/delivery" ) -// ManifestsState is the delivery indexer's manifests by digest, which a compose ask's request names (the module reads -// it, module.json reads). -const ManifestsState = delivery.IndexerModule + ".manifests" +// ManifestsTool is the delivery indexer's read tool of its manifests by digest, which a compose ask's request names (the +// module invokes it, module.json invokes). A manifest by its digest never changes and a compose ask is rare, so the +// module asks for the one key it needs when it needs it, and keeps no picture of the indexer's whole state (ADR 0615). +const ManifestsTool = delivery.IndexerModule + ".indexer_read_manifests" // composerLocks hold one mutex per composer key, so a composer two asks need at once is built once. var composerLocks sync.Map @@ -69,6 +71,25 @@ func (m *Module) compose(ctx context.Context, fetch buildstep.StepFunc, a delive return delivery.ComposeAnswer{Built: built, Key: key, Verdict: verdict}, nil } +// askManifest asks the indexer's read tool for one manifest by its key: nil when it keeps none. +func askManifest(key string) (json.RawMessage, error) { + raw, err := stdio.Ask(ManifestsTool, map[string]any{"key": key}) + if err != nil { + return nil, err + } + var a struct { + Found bool `json:"found"` + Value json.RawMessage `json:"value"` + } + if err := json.Unmarshal(raw, &a); err != nil { + return nil, fmt.Errorf("%s answered something that is not a key's value: %w", ManifestsTool, err) + } + if !a.Found { + return nil, nil + } + return a.Value, nil +} + // manifestOf is the manifest the indexer keeps under digest, as its repository holds it. func (m *Module) manifestOf(digest string) ([]byte, error) { if m.ReadManifest == nil { diff --git a/modules/mesh-build-go/cmd/mesh-build-go/compose_test.go b/modules/mesh-build-go/cmd/mesh-build-go/compose_test.go index 17dda5cd8..bfe808f12 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/compose_test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/compose_test.go @@ -122,10 +122,11 @@ func TestAComposeAskFailsLoudly(t *testing.T) { } } -// 1622: the module reads the delivery indexer's manifests, which a compose ask names by digest. +// 1622, ADR 0615: the module asks the delivery indexer's read tool for the manifests a compose ask names by digest, and +// reads none of the indexer's state. func TestTheModuleReadsTheIndexersManifests(t *testing.T) { - if m := readManifest(t); !slices.Contains(m.Reads, ManifestsState) { - t.Errorf("reads = %v, lacks %s", m.Reads, ManifestsState) + if m := readManifest(t); !slices.Contains(m.Invokes, ManifestsTool) || slices.Contains(m.Reads, delivery.IndexerModule+".manifests") { + t.Errorf("invokes = %v, reads = %v: it asks %s", m.Invokes, m.Reads, ManifestsTool) } } diff --git a/modules/mesh-build-go/cmd/mesh-build-go/main.go b/modules/mesh-build-go/cmd/mesh-build-go/main.go index 4e96528b5..78b3ec250 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/main.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/main.go @@ -57,7 +57,7 @@ func main() { m.RegistryFile = m.Home + "/registry" m.Settings = m.Home + "/settings.json" m.ReadKept = readOf(stdio.State(KeptState)).Get - m.ReadManifest = readOf(stdio.State(ManifestsState)).Get + m.ReadManifest = askManifest m.Probe = ProbeGo(m.Home, m.Cache) m.Bubblewrap = ProbeBubblewrap // Outside a launch by the runtime — a build, a check — there is no node and no state to write. diff --git a/modules/mesh-build-go/module.json b/modules/mesh-build-go/module.json index 2c6289c6a..a964a6423 100644 --- a/modules/mesh-build-go/module.json +++ b/modules/mesh-build-go/module.json @@ -10,23 +10,33 @@ "state": [ "toolchains", "keys", - {"name": "steps", "ttl-seconds": 172800}, "pauses" + { + "name": "steps", + "ttl-seconds": 172800 + }, + "pauses" ], "claims": [ { "name": "node-build-go", "scope": "node", - "serves": ["current", "pause", "resume"] + "serves": [ + "current", + "pause", + "resume" + ] } ], "reads": [ - "mesh-delivery-checker.kept", - "mesh-delivery-indexer.manifests" + "mesh-delivery-checker.kept" + ], + "invokes": [ + "mesh-delivery-indexer.indexer_read_manifests" ], "config": { "capacity": { "type": "number", - "description": "How many asks of the Go build seat this module runs at once on this node (novox/hq ADR 0525 §5); 0 pulls nothing. It is set per node in the mesh's configuration; a node with no value builds nothing.", + "description": "How many asks of the Go build seat this module runs at once on this node (novox/hq ADR 0525 \u00a75); 0 pulls nothing. It is set per node in the mesh's configuration; a node with no value builds nothing.", "minimum": 0, "default": 0 }, @@ -60,7 +70,9 @@ "streams": [ { "name": "toolchains-go", - "events": ["toolchain.**"] + "events": [ + "toolchain.**" + ] } ], "requires": [ @@ -120,7 +132,11 @@ "path": "${dir:state}/settings.json", "mode": "0600", "owner": "mesh-build-go", - "takes-config": ["capacity", "cores-per-step", "build-hours"], + "takes-config": [ + "capacity", + "cores-per-step", + "build-hours" + ], "merge": "json", "template": "files/settings.json" },