Documentation
¶
Overview ¶
Package events is the one door a module's events leave through.
A module writes an event with Publish, inside the same transaction as the state change that caused it, so the row and the change commit together or not at all: there is no window in which the state moved and the event was lost. The relay in the worker role reads those rows and hands them to a transport — in-process for a single-process run, JetStream for a fleet.
Delivery is at-least-once, and Consume is what turns that into exactly-once handling: it claims each (event, subscription) pair in platformkit_handled inside the handler's own transaction, so a redelivery of work already done finds the claim taken and skips the handler.
This is also the job queue: durable, retried, transactional background work is what an outbox is, and asking for it twice buys nothing. Periodic work is kit/jobs. Both arguments are docs/adr/0004.
Index ¶
- func Consume(ctx context.Context, conn *db.Conn, t Transport, subs []Subscription) error
- func Publish(ctx context.Context, tx db.Tx[db.Tenant], name string, payload any) error
- func PublishFor(ctx context.Context, tx db.Tx[db.System], tenantID uuid.UUID, name string, ...) error
- func Purge(ctx context.Context, conn *db.Conn) error
- func Relay(ctx context.Context, conn *db.Conn, t Transport) error
- func ValidName(name string) bool
- type Event
- type Handler
- type Sink
- type Subscription
- type Transport
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Consume ¶
Consume subscribes every handler in subs to its event. Each delivery opens a transaction in the event's own tenant, so a handler reaches the tenant's rows the same way a request handler does and can publish events of its own into the same transaction.
Each delivery is claimed before the handler runs, so a handler sees each event once however many times the transport delivers it. See claim.
func Publish ¶
Publish writes an event into the outbox inside tx. It is not a network call and it cannot fail because a broker is down: the row commits with the state change and the relay carries it from there.
It takes a context only to read the actor off it. A db.Tx carries none — a transaction is a handle, not a scope somebody can cancel through — so the caller's own context is the one thing that knows whose request this is.
func PublishFor ¶
func PublishFor(ctx context.Context, tx db.Tx[db.System], tenantID uuid.UUID, name string, payload any) error
PublishFor writes an event from a cross-tenant transaction, naming the tenant it belongs to.
It exists for one shape of work and is the only place in the program where a tenant is an argument to an event. The control plane creates a tenant, and the event that says so has to be written in the transaction that created it — a transaction that belongs to no tenant, about a tenant that did not exist a statement earlier. Publish cannot express that, and publishing afterwards in a second transaction would be an event that can be lost while its cause is kept, which is the exact failure the outbox exists to remove.
A caller needs a db.Tx[db.System] to reach it, so the audience is the modules that already hold the capability. See docs/adr/0006.
func Purge ¶
Purge removes published outbox history after a week. Unpublished rows remain recoverable regardless of age. An old handling claim remains while either its outbox row or terminal failure record exists: losing that claim would replay completed work when the pending row is relayed. Dead letters and their claims require explicit operator review; they are never automatically purged. The database clock supplies the cutoff for all workers.
func Relay ¶
Relay moves every unpublished row to the transport, a batch at a time, and returns when the queue is empty or ctx is done. kit/jobs calls it every second in the worker role.
It takes no lock, and that is deliberate: FOR UPDATE SKIP LOCKED is the concurrency control, so several workers relaying at once each take rows nobody else holds and none of them waits. An advisory lock around it would add nothing and would take something away — a transport that blocks inside one relay pass would hold that lock, and every other periodic job on every replica would stop behind it.
One transaction per batch rather than one for the whole drain, so the row locks are held for a batch and not for a backlog. The caller bounds the whole thing with a deadline; a pass that runs out of time leaves its rows unstamped and the next tick takes them.
Types ¶
type Handler ¶
Handler is what a module does with an event. It runs inside a transaction scoped to the event's tenant, so anything it writes commits with the acknowledgement of the event and rolls back with a redelivery.
type Subscription ¶
Subscription is one module's interest in one event. A module lists its subscriptions in its manifest; kit/app refuses to start when one names an event no module publishes.
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
delivery
Package delivery owns the retry ladder and retention shared by event adapters.
|
Package delivery owns the retry ladder and retention shared by event adapters. |
|
providers
|
|
|
memory
Package memory delivers committed events inside one process.
|
Package memory delivers committed events inside one process. |
|
nats
Package nats provides the existing JetStream event transport.
|
Package nats provides the existing JetStream event transport. |
|
Package transport describes committed event delivery without a database or broker.
|
Package transport describes committed event delivery without a database or broker. |