Both caught a failed write and took the event, losing it silently; the SDK's rule is to throw when the work was not done. A failed write now throws so the bus offers the event again, and is spooled on disk at once; on its last delivery the spooled event is taken, and a background pass replays the spool once writing works. The runtime does not pass the delivery count, so the spool counts failed deliveries itself, across restarts. Over its bound (1000 events or 30 minutes) the last delivery is no longer taken, so the bus gives it up and the controller raises max-deliveries - the one existing condition that names a consumer which cannot keep up - while the spool still holds it. Writes are idempotent by event: the trail skips an id it already wrote; the usage upsert keeps the reading observed latest (migration 2), so a late replay never overwrites a newer one. Each module has a status tool for the spool, declared as valuable data (ADR 0233). model-usage moves to the bundle shape (ADR 0198) with its schema in a prepare step and numbered migrations; its old container shape had no image. Both on mesh-sdk 0.1.13. The log-only handlers of redis, mssql, mosquitto, mongodb, mesh-vault, showcase and the catalogue no longer throw a TypeError on an event without a body.
27 lines
1.3 KiB
TypeScript
27 lines
1.3 KiB
TypeScript
// The audit-logger's entrypoint. The per-node module runtime imports this once the broker is
|
|
// bound; the on("#") subscription is the whole handshake — it consumes every event on the mesh
|
|
// (module.*, mesh.*, node.*) and writes each to the trail.
|
|
//
|
|
// **No event is lost** (novox/hq issue 276). A line that cannot be written is thrown, so the bus offers
|
|
// the event again; the first failure spools it on disk, its last delivery is taken from the spool, and
|
|
// the spool is replayed into the trail once writing works again (spool.ts). The trail skips an event id
|
|
// it already holds, so a redelivery or a replay never writes it twice (audit.ts).
|
|
|
|
import { on } from "@novox/mesh-sdk/events";
|
|
import { auditLogPath, auditSpoolPath, Trail } from "./audit.js";
|
|
import { replayEvery, Spool, takeOrSpool } from "./spool.js";
|
|
|
|
const say = (line: string) => console.error(`[audit-logger] ${line}`);
|
|
const trail = await Trail.open(auditLogPath());
|
|
const spool = await Spool.open(auditSpoolPath());
|
|
const write = (event: Parameters<Trail["record"]>[0]) => trail.record(event);
|
|
|
|
replayEvery(spool, write, 30_000, say);
|
|
|
|
await on("#", (event) => takeOrSpool(event, write, spool, say));
|
|
|
|
console.log(
|
|
`[audit-logger] recording all mesh events to ${trail.path}` +
|
|
(spool.size > 0 ? `; ${spool.size} spooled event(s) wait in ${spool.dir}` : ""),
|
|
);
|