Add NATS JetStream publish + trigger activities
NatsPublish (CodeActivity) publishes a UTF-8 body to a subject with optional Nats-Msg-Id dedup and ExpectedStream assertion. NatsTrigger (Trigger<string>) starts a workflow per message on a specified stream/subject. A single BackgroundService scans the trigger store for indexed NatsTriggers, groups by (stream, subject), and runs one durable pull consumer per pair with Explicit ack, so multiple Elsa instances share the workload at-least-once. A mediator handler on WorkflowTriggersIndexed refreshes subscriptions when workflows are published. Wired via Nats:Url/User/Password (env-var overrides Nats__*, or short NATS_URL/NATS_USER/NATS_PASSWORD fallbacks). Uses the NATS.Net v2 client so wss:// works transparently.
This commit is contained in:
35
CLAUDE.md
35
CLAUDE.md
@@ -169,6 +169,41 @@ to its DSN.
|
||||
at create time. `POST /applications/dockercompose` rejects it otherwise.
|
||||
- Deploys are fire-and-forget — never poll from Claude. User's global rule.
|
||||
|
||||
## NATS activities
|
||||
|
||||
Three files under `src/Elsa.CustomServer/`:
|
||||
|
||||
- `Activities/NatsPublish.cs` — publisher `CodeActivity`. Inputs: `Subject`,
|
||||
`Body`, optional `MessageId` (sets the `Nats-Msg-Id` header for JetStream
|
||||
server-side dedup), optional `ExpectedStream` (fail-fast assertion). Outputs
|
||||
`Sequence` and `Duplicate`.
|
||||
- `Activities/NatsTrigger.cs` — trigger `Trigger<string>`. Inputs: `Stream`,
|
||||
`Subject` (wildcards allowed). Outputs: `Result` = message body (UTF-8),
|
||||
`ReceivedSubject`, `Headers`. Its trigger name / stimulus name is
|
||||
`NATS.NatsTrigger`.
|
||||
- `Nats/NatsTriggerHostedService.cs` — one `BackgroundService` that scans
|
||||
`ITriggerStore` for stored triggers named `NATS.NatsTrigger`, spins up one
|
||||
durable pull consumer per unique `(stream, subject)` pair, and dispatches
|
||||
workflows via `IWorkflowDispatcher.DispatchAsync(DispatchTriggerWorkflowsRequest)`
|
||||
as messages arrive. The mediator handler
|
||||
`NatsTriggerIndexChangedHandler` re-runs the scan whenever
|
||||
`WorkflowTriggersIndexed` fires (i.e. after publishing a workflow).
|
||||
|
||||
Consumer model: shared durable pull consumer with `DurableName =
|
||||
elsa-<sanitizedStream>-<sanitizedSubject>` and `AckPolicy.Explicit`. That means
|
||||
if multiple Elsa instances are running, exactly one processes each message
|
||||
(load-balanced), at-least-once. `MaxDeliver = 5`, `AckWait = 30s`,
|
||||
`MaxAckPending = 256`. Failed dispatches NAK with a 5-second delay.
|
||||
|
||||
Configuration: `Nats:Url`, `Nats:User`, `Nats:Password` in appsettings
|
||||
(env-var overrides `Nats__Url`, `Nats__User`, `Nats__Password`, or the
|
||||
short `NATS_URL` / `NATS_USER` / `NATS_PASSWORD` fallbacks). Leave `Nats:Url`
|
||||
empty to disable NATS entirely — the activities still register in the toolbox
|
||||
but publishing throws and no consumers are started.
|
||||
|
||||
Packages: `NATS.Net` + `NATS.Client.Hosting` (v2 client). The v1 `NATS.Client`
|
||||
package does not support `wss://` from .NET — do not use it.
|
||||
|
||||
## Session-specific reminders
|
||||
|
||||
- Custom activities added under `Activities/` in the SERVER project (not the
|
||||
|
||||
Reference in New Issue
Block a user