outbox

package
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 28, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package outbox implements at-least-once EXTERNAL event delivery: the unit of work writes envelopes into an outbox table in the same transaction as the change, and a relay dispatches them to the registered external hooks, retrying on failure. Without the outbox, a crash between commit and dispatch loses events; with it, every external consumer (webhooks, pub/sub) sees each committed change at least once. Internal projections (computed attributes, search index, GraphQL cache) do NOT ride the relay — they are maintained synchronously in the writing request regardless of the outbox (see application/uow and issue #211).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Relay

type Relay struct {
	// contains filtered or unexported fields
}

Relay drains the outbox: on a nudge (post-commit) or on the interval it fetches pending envelopes, dispatches them and records outcomes. Failed envelopes stay pending and retry on later passes.

func NewRelay

func NewRelay(store Store, dispatcher *events.Dispatcher, opts ...RelayOption) *Relay

NewRelay builds a relay over the store and dispatcher.

func (*Relay) DrainOnce

func (r *Relay) DrainOnce(ctx context.Context)

DrainOnce performs one drain pass — claiming, dispatching and finalizing batches until the outbox is empty. Run loops these on a nudge or the interval; DrainOnce is exposed for one-shot draining and tests.

func (*Relay) Nudge

func (r *Relay) Nudge()

Nudge wakes the relay immediately — called post-commit so delivery latency stays milliseconds in the happy path.

func (*Relay) Run

func (r *Relay) Run(ctx context.Context)

Run drains the outbox until ctx is cancelled.

type RelayOption

type RelayOption func(*Relay)

RelayOption customises a Relay.

func WithAfterExpand

func WithAfterExpand(fn func()) RelayOption

WithAfterExpand installs a hook invoked after a pass that expanded envelopes — the delivery worker's nudge, keeping webhook latency in milliseconds.

func WithBatchSize

func WithBatchSize(n int) RelayOption

WithBatchSize sets how many envelopes one pass claims (default 100).

func WithErrorObserver

func WithErrorObserver(fn func(err error)) RelayOption

WithErrorObserver receives relay-level failures (fetch errors — dispatch failures are recorded per envelope).

func WithInterval

func WithInterval(d time.Duration) RelayOption

WithInterval sets the poll interval (default 2s).

func WithLeaseTTL

func WithLeaseTTL(d time.Duration) RelayOption

WithLeaseTTL sets how long a claimed batch stays leased to this relay before another relay may reclaim it (default 1m). It should comfortably exceed the slowest expected dispatch of one batch.

func WithRelayID

func WithRelayID(id string) RelayOption

WithRelayID sets the identifier stamped on leases (default a random ULID). Only useful for deterministic tests.

type Result

type Result struct {
	EnvelopeID string
	Err        error
}

Result records one dispatch attempt.

type Store

type Store interface {
	// Write persists envelopes inside the caller's transaction — the unit
	// of work's pre-commit handler.
	Write(ctx context.Context, tx db.Tx, envs []events.Envelope) error

	// Claim leases up to limit undispatched envelopes for the given relay
	// and returns them for dispatch. Claiming takes a short row lease
	// (claimed_by/claimed_at) so no other relay grabs the same rows while
	// this relay dispatches them outside any transaction; a lease older
	// than leaseTTL is treated as abandoned (crashed relay) and reclaimed.
	// It does NOT hold the sequencer lock — no network I/O happens here.
	Claim(ctx context.Context, relayID string, limit int, leaseTTL time.Duration) ([]events.Envelope, error)

	// Finalize records the outcome of dispatching a claimed batch. Under
	// the single-sequencer advisory lock (DB-only, no network I/O) it
	// assigns feed_seq to each success in claim order, fans out one
	// webhook-delivery row per matching subscription and marks the
	// envelope dispatched; failures have their attempt counted and their
	// lease cleared so a later pass retries them.
	Finalize(ctx context.Context, results []Result) error
}

Store is the persistence port for the outbox.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL