Skip to content

feat(act): configurable correlation id generator (ACT-404)#726

Merged
Rotorsoft merged 1 commit into
masterfrom
feat/act-404-correlator
May 15, 2026
Merged

feat(act): configurable correlation id generator (ACT-404)#726
Rotorsoft merged 1 commit into
masterfrom
feat/act-404-correlator

Conversation

@Rotorsoft
Copy link
Copy Markdown
Owner

Summary

Closes #725.

Replaces randomUUID() for correlation ids with a configurable delegate plus a readable, time-monotonic default of the form {state[:4]}-{action[:4]}-{4 ms}{4 random} (18 chars, lowercase base36).

// default
await app.do("incrementBy", { stream: "counter-1", actor }, { by: 1 });
// → event.meta.correlation === "coun-incr-lwxk9p3a"

// custom (tenant-prefixed)
const correlator: Correlator = ({ state, action, actor }) =>
  \`${actor.tenantId}-${state.slice(0,4)}-${action.slice(0,4)}-${Date.now().toString(36)}\`;
act().withState(...).build({ correlator });

Why

  • Index locality — random UUIDs scatter across the correlation index causing page splits and bloat. The time-ordered prefix in the default groups contemporaneous inserts on the same B-tree pages.
  • Readability — `coun-incr-…` tells operators what the workflow is at a glance; `f47ac10b-58cc-…` doesn't.
  • No coordination required — 4-char random tail (1.68M values per ms) keeps competing-consumer workers collision-safe without DB sequence round-trips.

What changed

  • New `Correlator` type and `CorrelatorContext` exported from `@rotorsoft/act`
  • New `ActOptions.correlator?: Correlator` — defaults to the readable shape
  • `event-sourcing.action()` takes the correlator (default-fall-through for direct callers in tests)
  • `buildEs` binds the correlator into `EsOps.action` so the orchestrator's call sites stay unchanged
  • `runCloseCycle` accepts a pre-computed `correlation` (Act passes one minted via the configured correlator with a synthetic `$close` context, so close tombstones share the user's id scheme)
  • Reactions still propagate `reactingTo.meta.correlation` — the chain is untouched
  • Wolfdesk bootstrap demonstrates a tenant-prefixed correlator

Test plan

  • 10 new tests in `libs/act/test/correlator.spec.ts` cover: default format width, prefix truncation, lowercase invariant, 1000-iteration uniqueness, timestamp progression, delegate override, reaction chain propagation, context shape, and close-cycle integration
  • Full `@rotorsoft/act` suite passes (555 tests, no regressions)
  • Broader suite passes — `libs/act-sqlite`, `libs/act-tck`, `libs/act-pino` (1110+ tests total)
  • Wolfdesk tests pass with the custom correlator wired
  • Typecheck clean
  • Biome lint clean
  • `correlator.ts` at 100% coverage

Docs

  • `docs/docs/concepts/event-sourcing.md` — new "Correlation IDs" section with default format, common alternative shapes (tenant prefix, trace-id propagation, idempotency-key bridge, DB-issued monotonic, ULID/UUIDv7), and the close-cycle convention
  • Memory: `project_book_correlator.md` book notes covering why UUIDs hurt indexes, the readable default, and six common delegate patterns

🤖 Generated with Claude Code

Replaces the hardcoded `randomUUID()` correlation source with a
delegate on `ActOptions.correlator` and a readable, time-monotonic
default of the form `{state[:4]}-{action[:4]}-{4 ms}{4 random}` (18
chars, lowercase base36, e.g. `coun-incr-lwxk9p3a`).

Why:
- UUIDv4 is unreadable in logs and scatters across the correlation
  index — page splits and poor cache locality on PG.
- The default keeps competing-consumer workers from colliding (1.68M
  random values per ms) without coordination, while still time-ordering
  inserts inside ~28-minute windows for index page-fill behavior.
- Apps that want tenant prefixes, trace-id propagation, idempotency-key
  bridging, DB-issued monotonic ids, or ULID/UUIDv7 plug their own
  delegate via `act()...build({ correlator })`.

Threaded through `event-sourcing.action()` (originating commits) and
`runCloseCycle` (close transactions, with a synthetic `$close` context).
Reactions still inherit `reactingTo.meta.correlation` — the chain is
untouched. Wolfdesk demonstrates a tenant-prefixed correlator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Rotorsoft Rotorsoft merged commit 2f1f026 into master May 15, 2026
7 checks passed
@github-actions
Copy link
Copy Markdown

🎉 This PR is included in version @rotorsoft/act-v0.42.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ACT-404: configurable correlation id generator (correlator delegate + readable default)

1 participant