From 4258f0161472e41574c5d2c45a815e06dbf60674 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 28 Sep 2026 15:40:28 +0200 Subject: [PATCH] The catalogue prepares its own schema instead of migrating at start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It brought its schema up inside its runtime, on every start. That made a schema it could not reach a crash loop rather than a stop, with the module graph keeping a gap and nothing saying so — which is how a whole morning's builds went unrecorded. The mesh now prepares this module's state before it starts this version and does not start it if that failed (novox/hq ADR 0135): the work moves to an entrypoint the image names in MESH_PREPARE, beside the entrypoints it already names. The reason it was at start — that a step blocking the apply would block the very apply bringing the overlay up — stopped being true when a step's failure became its module's business rather than the machine's (ADR 0136). --- modules/mesh-catalog/Dockerfile | 7 ++++++- modules/mesh-catalog/index.ts | 10 ++++++---- modules/mesh-catalog/module.json | 1 + modules/mesh-catalog/prepare/index.ts | 17 +++++++++++++++++ modules/mesh-catalog/tsconfig.json | 3 ++- 5 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 modules/mesh-catalog/prepare/index.ts diff --git a/modules/mesh-catalog/Dockerfile b/modules/mesh-catalog/Dockerfile index f470101..ed67a61 100644 --- a/modules/mesh-catalog/Dockerfile +++ b/modules/mesh-catalog/Dockerfile @@ -22,7 +22,7 @@ COPY . . # The compiler is invoked by its real path rather than through node_modules/.bin, whose entries are # symlinks to a launcher that requires its library relatively — resolved away when the base image # was assembled. -RUN node /app/node_modules/typescript/bin/tsc pg.d.ts store.ts index.ts tools/index.ts \ +RUN node /app/node_modules/typescript/bin/tsc pg.d.ts store.ts index.ts tools/index.ts prepare/index.ts \ --module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist # **A module may need something the base image does not carry.** The base holds what every module @@ -48,3 +48,8 @@ COPY --from=build /deps/node_modules /app/modules/mesh-catalog/node_modules # to listen for what the builder announces. Serve binds the broker first, then imports these, so # `on()` has something to subscribe to. ENV MESH_TOOL_MODULES=/app/modules/mesh-catalog/dist/index.js,/app/modules/mesh-catalog/dist/tools/index.js + +# And what prepares this module's state, for the runtime's `prepare` mode (novox/hq ADR 0135). Named +# here, beside the entrypoints above, because the module knows which of its files prepares its state +# and nothing else could: the mesh asks one word and this says what answers it. +ENV MESH_PREPARE=/app/modules/mesh-catalog/dist/prepare/index.js diff --git a/modules/mesh-catalog/index.ts b/modules/mesh-catalog/index.ts index 0c703a2..416f31a 100644 --- a/modules/mesh-catalog/index.ts +++ b/modules/mesh-catalog/index.ts @@ -14,10 +14,12 @@ import { Graph, type Made } from "./store.js"; const graph = Graph.fromEnv(); -// Before subscribing, and idempotent. The runtime is restarted until its store is reachable, which -// is the same arrangement model-usage uses: a schema step that had to reach the provider over the -// overlay would block the very apply that brings the overlay up. -await graph.migrate(); +// The schema is not brought up here. The mesh prepares this module's state before it starts this +// version, and does not start it if that failed (novox/hq ADR 0135) — see prepare/index.ts. Doing it +// at start made a schema that could not be reached a crash loop instead of a stop, with the graph +// keeping a gap and nothing saying so. The reason it used to be here — that a step blocking the apply +// would block the very apply that brings the overlay up — stopped being true when a step's failure +// became this module's business and not the machine's (ADR 0136). /** What the builder says when it has built something. */ interface Built { diff --git a/modules/mesh-catalog/module.json b/modules/mesh-catalog/module.json index 310bc05..60316c3 100644 --- a/modules/mesh-catalog/module.json +++ b/modules/mesh-catalog/module.json @@ -37,6 +37,7 @@ "rebuild-needed", "catching-up" ], + "prepares": true, "resources": [ { "id": "mesh-state", diff --git a/modules/mesh-catalog/prepare/index.ts b/modules/mesh-catalog/prepare/index.ts new file mode 100644 index 0000000..ed05f93 --- /dev/null +++ b/modules/mesh-catalog/prepare/index.ts @@ -0,0 +1,17 @@ +// The catalogue's state, brought to the shape this version needs (novox/hq ADR 0135). +// +// **The mesh runs this before the version that needs it, and does not start that version if it +// fails** — and the refusal reaches this module and nothing else on the machine +// (novox/hq ADR 0136). That is the whole difference from where this used to happen: at start, inside +// the runtime, a schema that could not be brought up was a crash loop, the graph kept a gap, and +// nothing anywhere said so. +// +// Nothing here connects to the broker. Preparation runs before the version that would use it, so +// there is nothing yet to talk to; the runtime's `prepare` mode imports this and awaits it, and this +// process exiting non-zero is how the host knows not to start the runtime. +import { Graph } from "../store.js"; + +const graph = Graph.fromEnv(); +await graph.migrate(); +console.log("[mesh-catalog] the module graph's schema is what this version needs"); +await graph.close(); diff --git a/modules/mesh-catalog/tsconfig.json b/modules/mesh-catalog/tsconfig.json index 2528f05..4e95d4f 100644 --- a/modules/mesh-catalog/tsconfig.json +++ b/modules/mesh-catalog/tsconfig.json @@ -12,6 +12,7 @@ "pg.d.ts", "store.ts", "index.ts", - "tools/index.ts" + "tools/index.ts", + "prepare/index.ts" ] }