postgres

package
v0.0.21 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package postgres is the PostgreSQL kind of the outbox store (stores/outbox): the relay's claim, lease and finalize operations over a product's own outbox table, named by configuration. Every product outbox table shares one column layout (below), so a service gets a production store by naming its table rather than re-writing the SKIP LOCKED claim per table.

The table must carry these columns:

id uuid, lane text, payload bytea, status text|enum ('pending','sent','parked'),
attempts int, next_attempt_at timestamptz, last_error text, created_at timestamptz,
sent_at timestamptz NULL

plus the key column (Config.KeyColumn), and optionally a tenant column and any attribute columns. Producers insert pending rows themselves, inside their business transaction; this store only drains them.

Every write (Claim, the finalizers, DeleteSentBefore) runs on the *sql.Tx a transaction manager (stores/sqltx) bound to the context, and fails INTERNAL without touching the database when none is bound, so a write never autocommits outside the workflow that owns it. Stats reads on the bound transaction when there is one.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// AttributeColumns maps each record attribute key to the column it is read from
	// (e.g. {"event_type": "event_type"}); empty reads no attributes.
	AttributeColumns map[string]string `yaml:"attribute_columns" mapstructure:"attribute_columns"`

	// Table is the outbox table's name.
	Table string `yaml:"table" mapstructure:"table"`

	// KeyColumn is the column a record's Key is read from (as text); empty means
	// "aggregate_id".
	KeyColumn string `yaml:"key_column" mapstructure:"key_column"`

	// TenantColumn is the column a record's Tenant is read from; empty means the table
	// has none and every record's Tenant is "".
	TenantColumn string `yaml:"tenant_column" mapstructure:"tenant_column"`

	// Lease is how long a claimed but unfinalized row is hidden from another claim. It
	// must outlast a relay pass, yet be short enough that a crashed relay's rows come
	// back promptly; it must be positive.
	Lease time.Duration `yaml:"lease" mapstructure:"lease"`

	// MaxErrorLen caps the recorded last_error, so a long delivery error cannot fail
	// the very update that records it; zero records it whole.
	MaxErrorLen int `yaml:"max_error_len" mapstructure:"max_error_len"`
}

Config names a store's table and the columns that vary between outbox tables.

func (Config) Validate

func (c Config) Validate() error

Validate rejects a non-identifier table or column and a non-positive lease with CodeInvalidInput.

type Store

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

Store is the OutboxStore over one PostgreSQL outbox table.

func New

func New(db *sql.DB, cfg Config) (*Store, error)

New returns the store for cfg over db. An invalid cfg is CodeInvalidInput; a nil db panics, as a wiring bug. It performs no I/O.

func (*Store) Claim

func (s *Store) Claim(
	ctx context.Context,
	lane string,
	limit int,
) ([]types.OutboxRecord, error)

Claim leases up to limit due pending rows of lane, oldest first; the returned Attempts counts this claim. A non-positive limit is CodeInvalidInput, since LIMIT 0 would silently claim nothing every pass.

func (*Store) DeleteSentBefore

func (s *Store) DeleteSentBefore(ctx context.Context, cutoff time.Time) (int, error)

DeleteSentBefore purges sent rows whose sent_at is before cutoff and returns how many went; parked rows stay for an operator. The claim index covers only pending rows, so a retention sweep calls it to keep sent rows from accumulating.

func (*Store) Enqueue added in v0.0.16

func (s *Store) Enqueue(ctx context.Context, rec types.OutboxRecord) error

Enqueue writes rec as a pending row due now on the transaction bound to ctx; see interfaces.OutboxWriter. An empty Key, Tenant or attribute is NULL.

func (*Store) MarkSent

func (s *Store) MarkSent(ctx context.Context, id uuid.UUID) error

MarkSent moves a pending row to sent, stamping sent_at on the database clock and clearing last_error. A row already sent or parked is left unchanged.

func (*Store) Park

func (s *Store) Park(ctx context.Context, id uuid.UUID, lastErr string) error

Park takes a pending row out of rotation and records lastErr; the row stays for an operator to inspect. A row already sent is left unchanged.

func (*Store) Retry

func (s *Store) Retry(
	ctx context.Context,
	id uuid.UUID,
	next time.Time,
	lastErr string,
) error

Retry makes a pending row due again at next and records lastErr. A row already sent or parked is left unchanged.

func (*Store) Stats

func (s *Store) Stats(ctx context.Context, lane string) (types.OutboxStats, error)

Stats counts lane's pending and parked rows and reports its oldest pending row's creation time, in one aggregate.

Jump to

Keyboard shortcuts

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