mesh/check failed: unit: modules/gitea.test: [gitea] where the poll of merges last looked from could not be read, so none is kept — no runtime | 49a28…
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate head 49a28f85378b merged into trunk c770f7947d95 — pass: builds gitea → novox; no bus step; every machine composes with the change as it di…
mesh/repo-check head 49a28f85378b merged into trunk c770f7947d95 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
45 lines
2.3 KiB
TypeScript
45 lines
2.3 KiB
TypeScript
// **A consumer that cannot be bound yet is bound again, never fatal** (novox/hq issue 1793). When the tool runtime
|
|
// starts this bundle, the runtime's answer to "bind this module's consumer" can be "no responders" for a few seconds (the
|
|
// runtime restarted by a delivery, the bus not yet serving the stream). That rejection, left unhandled at the top of
|
|
// index.ts, ended the bundle: nothing heard `mesh-controller.checked` again, and a verdict that landed later never
|
|
// reached the forge's statuses. A bind that fails is said and asked again with a growing wait until it holds.
|
|
|
|
/** How long to wait before the nth new attempt (1-based): 2 s, 5 s, 10 s, then every 30 s. */
|
|
export function bindWait(attempt: number): number {
|
|
return [2_000, 5_000, 10_000][attempt - 1] ?? 30_000;
|
|
}
|
|
|
|
/**
|
|
* Binds with `bind`, asking again after each failure, until it holds. `sleep` is the wait; `say` how a failure is said.
|
|
* Never rejects; resolves once the bind holds.
|
|
*/
|
|
export async function bindUntilHeld(what: string, bind: () => Promise<unknown>,
|
|
sleep: (ms: number) => Promise<void> = (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
say: (line: string) => void = (line) => console.error(line)): Promise<void> {
|
|
for (let attempt = 1; ; attempt++) {
|
|
try {
|
|
await bind();
|
|
if (attempt > 1) say(`[gitea] ${what} is bound after ${attempt} attempts`);
|
|
return;
|
|
} catch (err) {
|
|
const wait = bindWait(attempt);
|
|
say(`[gitea] ${what} could not be bound (${err instanceof Error ? err.message : String(err)}); asking again in ${wait / 1000}s`);
|
|
await sleep(wait);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* **Starts the binds without making the bundle wait for them** (novox/hq issue 1793). The launcher imports the bundle
|
|
* and then answers the runtime's handshake, which the runtime gives 10 s and then kills the bundle without starting it
|
|
* again; a bind awaited at the top of the module that stays refused longer ends the bundle all the same. So this
|
|
* returns at once: the binds run in the order given, each until it holds, and `done` is called after the last.
|
|
*/
|
|
export function bindInBackground(binds: [what: string, bind: () => Promise<unknown>][], done: () => void,
|
|
sleep?: (ms: number) => Promise<void>, say?: (line: string) => void): void {
|
|
void (async () => {
|
|
for (const [what, bind] of binds) await bindUntilHeld(what, bind, sleep, say);
|
|
done();
|
|
})();
|
|
}
|