Conversation
…101) Closes #672. Adds an optional `Store.notify(handler)` subscription that the orchestrator auto-wires at build time. When a different process commits to the same backing store, the listener wakes `settle()` immediately instead of waiting for the polling/debounce floor — at the default `start_correlations` interval, that's a ~1000× p99 reduction. - `PostgresStore`: implemented via `LISTEN`/`NOTIFY` on a per-(schema, table) channel; `commit()` issues one NOTIFY per transaction with the full event batch as JSON. Self-filter via per-instance `_by` UUID so the `notified` lifecycle event surfaces only cross-process activity. - `InMemoryStore`, `SqliteStore`: leave `notify` undefined — single process / single node, no remote writers possible. - `Act` constructor wires the subscription when the store implements `notify` AND there are reactions to wake. New `notified` lifecycle event passes the payload through for SSE fan-out, dashboards, audit. - Build-time contract: inject the store via `store(adapter)` *before* `act()...build()` — wiring binds at construction. Coverage: 100% statements/branches/functions/lines workspace-wide. Benchmark in libs/act-pg/PERFORMANCE.md (notify p99 ≈25ms vs polling p99 ≈77ms at 50ms intervals). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The auto-wire of cross-process notifications added overhead even on single-instance deployments — one `pg_notify` SQL per commit and a dedicated LISTEN client per process for the lifetime of the orchestrator. Make the cost explicit and zero by default. - `PostgresStore` adds optional `notify?: boolean` to its config (default `false`). - When `false`: `commit()` skips the `pg_notify` SQL entirely, and `notify` is left undefined on the store — the orchestrator's `if (store.notify)` auto-wire short-circuits, so no LISTEN client is ever allocated. Existing callers see zero behavior change. - When `true`: same behavior as the previous always-on default. - `vitest.bench.config.ts` added so the perf bench can be invoked cleanly without polluting the default `pnpm test` run. - Docs updated everywhere (CLAUDE.md, READMEs, Docusaurus page, PERFORMANCE.md, scaffold-act-app skill, TSDoc). Coverage: 100% across the board (1024 tests). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The store-side `notify` implementation in PostgresStore was carrying
two kinds of code: adapter-specific (channel filter, JSON parse,
self-filter, payload validation) and generic (lifecycle emit, drain
wakeup, listener-error containment). The generic part now lives in
`Act._wireNotify` so every adapter inherits it without re-implementing.
- `Act._wireNotify` wraps `emit("notified")` + drain arm/settle in
try/catch so a buggy user `notified` listener can't tear down the
wiring.
- The PG adapter still wraps the framework-supplied handler in its
own try/catch — defense in depth, since direct callers of
`store.notify(handler)` (custom integrations, isolated tests)
also need their LISTEN client kept alive when their handler throws.
- New unit test confirms the orchestrator-level wrap.
Coverage: 100% across the board (1025 tests).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
🎉 This PR is included in version @rotorsoft/act-v0.34.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version @rotorsoft/act-pg-v0.19.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version @rotorsoft/act-sqlite-v0.4.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #672. Adds an optional
Store.notify(handler)subscription that the orchestrator auto-wires at build time, with the cost gated behind a per-adapter opt-in.PostgresStore: implementsnotifyviaLISTEN/NOTIFYon a per-(schema, table)channel (act_commit_<schema>_<table>). Opt-in vianotify: true(defaultfalse) — when off,commit()skipspg_notifyand thenotifymethod is undefined on the instance, so the orchestrator's auto-wireif (store.notify)short-circuits and no LISTEN client is allocated. Existing callers see zero behavior change after upgrading.InMemoryStore,SqliteStore:notifyleft undefined — single-process / single-node by design.Actorchestrator: subscribes once at construction when the store exposesnotifyAND reactions are registered. Newnotifiedlifecycle event passes the payload through for SSE fan-out, dashboards, audit. Per-instance_byUUID self-filter keeps the channel cross-process — local commits already arm drain viado().store(adapter)beforeact()...build()— wiring binds at construction.Performance
Benchmark in
libs/act-pg/PERFORMANCE.md. TwoPostgresStoreinstances on docker PG, 30 commits each:At the default
start_correlations10s interval the gap blows out to ~1000×.Run:
pnpm -F @rotorsoft/act-pg exec vitest run --config vitest.bench.config.tsCoverage
100% statements / branches / functions / lines workspace-wide. 1024 tests passing.
Docs
CLAUDE.md— new "Cross-Process Reactions" section,Storecontract amendment, troubleshooting bulletlibs/act/README.md,libs/act-pg/README.md,libs/act-sqlite/README.mdupdated with opt-in storydocs/docs/architecture/cross-process-reactions.md(new Docusaurus page).claude/skills/scaffold-act-app/server.md—notifiedlifecycle eventStore.notify,StoreNotification,NotifyDisposer,ActLifecycleEvents.notified,Config.notifyTest plan
pnpm test) — 1024/1024pnpm lint)pnpm build)libs/act-pg/test/notify.spec.ts)notify: falseand listener withnotify: truedeliver no notificationslibs/act/test/notify.spec.ts)libs/act-pg/test/store.error.spec.ts)pnpm -F @rotorsoft/act-pg exec vitest run --config vitest.bench.config.ts🤖 Generated with Claude Code