Repository navigation
[Design Discussion] Generic eventing framework #5590
Replies: 2 comments 1 reply
|
Hi @ThumulaPerera ,
Thanks and Regards |
|
@senthalan, @rajithacharith and I had a review of the design and following are the outcomes. Decisions
Open items
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Related Feature Issue
#5588
Problem Summary
Today every event has the same shape (a few common fields plus a key-value map), there's no taxonomy, and delivery is best-effort only.
Upcoming features such as audit and analytics change what events must do:
at-least-once. A lost trace is harmless, so observability needs onlybest-effort. Deployments may weigh this differently, so the guarantee must be configurable.High-Level Approach
Model
flowchart LR E["Event<br/>USER_CREATED"] E --> A["Category: audit<br/>at-least-once"] E --> O["Category: observability<br/>best-effort"] A --> S1["Audit store"] O --> S2["OTel exporter"] O --> S3["Console logger"]auditorobservability.An event in both an at-least-once and a best-effort category needs no special handling: each subscriber follows its own category's path.
Event
flowchart LR subgraph Event direction TB ENV["Envelope<br/>id · name · traceId · deploymentid · timestamp"] AP["Audit payload<br/>actor · target · action · outcome<br/>+ details"] OP["Observability payload<br/>component · status<br/>+ details"] end ENV ~~~ AP ~~~ OPreasononTOKEN_REVOKED. Without it, each payload would grow a field for every event's specifics.Delivery
flowchart LR P["Publisher"] -->|Publish| BUS{{"Event bus"}} BUS -->|best-effort| Q1["In-memory queue<br/><i>envelope + observability payload</i>"] --> W1["Worker"] --> S1["OTel exporter"] BUS -->|best-effort| Q2["In-memory queue<br/><i>envelope + observability payload</i>"] --> W2["Worker"] --> S2["Console logger"] BUS -->|at-least-once| T[("Delivery row<br/><i>envelope + audit payload</i>")] --> W3["Worker"] --> S3["Audit store"]The bus splits the event by category. Each queue item or delivery row carries the envelope plus only that subscriber's category payload. An observability subscriber never receives the audit payload, and the audit payload is the only one stored in an audit row.
Publishonly records the event and returns. Subscribers always run in the background, which keeps publishing async.Architecture Overview
1. Recording at-least-once events: two alternatives
A. Record after commit. Simple, but events can be lost.
sequenceDiagram participant Svc as User service participant DB as Entity DB participant ES as Event store Svc->>DB: commit(create user) Note over Svc,ES: Loss window Svc->>ES: Publish(USER_CREATED)Publishis called after commit.B. Transactional outbox. No events are lost, but it's complex.
sequenceDiagram participant Svc as User service box Entity DB, one transaction participant T as users table participant OB as outbox table end Svc->>T: insert user Svc->>OB: Publish(USER_CREATED) Svc->>T: commit (both or neither)What makes it complex:
Publish.2. Delivering at-least-once events
flowchart TD A["Claim next due row"] --> B["subscriber.Handle(...)"] B --> C{"Result?"} C -->|nil| D["Delete row"] C -->|"Permanent(err)"| F["Mark failed"] C -->|error| E{"Attempts left?"} E -->|yes| G["Retry after backoff"] E -->|no| FPermanent(err)skips retries for failures that can never succeed, such as a payload the sink rejects.Example: one
USER_CREATEDevent goes to two audit subscribers, an audit store and a SIEM forwarder.nilOnly the SIEM forwarder is retried, and the audit store never gets a duplicate.
3. Contracts
Publisher and bus
PublishPublishnever running subscriber codePublishafter commit (A) or inside the transaction (B)Subscriber and bus
"audit-store") must be stable across restarts and releases. At-least-once rows are stored, and the ID is how a worker finds the subscriber again after a restart or on another node.nil(delivered), an error (retry), orPermanent(err)(fail without retrying)ctxSecurity Considerations
Impacted Areas
internal/system/observability/: its subscribers becomeobservabilitysubscribers.providers.Eventandproviders.ObservabilityProvider.Alternatives Considered
Sub-categories (
audit.authentication)A subscriber spanning several categories
A cursor per subscriber over an event log, instead of one row per (event, subscriber)
An external broker (Kafka, NATS)
Questions for Community Input
All reactions