Documentation
¶
Index ¶
- Variables
- func Migrate(ctx context.Context, pool *pgxpool.Pool, opts ...OutboxOpt) error
- type AddOpt
- type FlushContext
- type Flusher
- type MessageOpts
- type NopFlusher
- type Notifier
- type Outbox
- type OutboxOpt
- type PGPubSubOpt
- type ProcessOpt
- type PubSub
- type PubSubMessage
- type SubscribeOpt
- type TxPublisher
Constants ¶
This section is empty.
Variables ¶
var ErrExclusiveLeaseHeld = errors.New("exclusive lease held by another instance")
ErrExclusiveLeaseHeld is returned by ProcessMessages when another outbox instance currently holds a valid exclusive lease for the topic.
var ErrExclusiveLeaseRequired = errors.New("exclusive lease required: call AcquireTopic first")
ErrExclusiveLeaseRequired is returned by ProcessMessages when the topic has an exclusive-consumer record but this instance does not hold a live lease — either AcquireTopic was never called or the lease has since expired.
Functions ¶
func Migrate ¶
Migrate runs the embedded pgoutbox migrations against the given pool. It is the explicit alternative to NewOutbox's auto-migration: callers that want to control when DDL runs (separate startup phase, release pipeline, etc.) should construct the outbox with WithAutoMigrate(false) and invoke Migrate themselves.
Only WithSchema is consulted from opts; other options are accepted for API symmetry but ignored.
Types ¶
type AddOpt ¶ added in v0.4.0
type AddOpt func(*addOpts)
AddOpt is a per-call option for AddMessages.
func WithNotifier ¶ added in v0.4.0
WithNotifier has AddMessages collect its post-commit notification into n instead of dropping it. Only generic (non-TxPublisher) PubSubs need it — they have no way to defer a publish to commit time, so the caller carries the notification past the transaction and fires it with Notify. One Notifier can be shared by every AddMessages call in a transaction and fired once after commit.
type FlushContext ¶ added in v0.2.0
FlushContext is the context passed to Flusher.Flush. It embeds context.Context and exposes the transaction that ProcessMessages uses to lock and delete messages. Callers that want their writes to commit atomically with the outbox delete can enlist in that transaction via Tx().
type MessageOpts ¶
type MessageOpts struct {
Payload []byte
}
type NopFlusher ¶ added in v0.2.0
type NopFlusher struct{}
func NewNopFlusher ¶ added in v0.2.0
func NewNopFlusher() *NopFlusher
func (*NopFlusher) Flush ¶ added in v0.2.0
func (f *NopFlusher) Flush(_ FlushContext, _ []*sqlc.Message) error
type Notifier ¶ added in v0.4.0
type Notifier struct {
// contains filtered or unexported fields
}
Notifier accumulates the new-message notifications of the AddMessages calls it is passed to (via WithNotifier), so they can be published once the staging transaction has committed. The zero value is ready to use; it is not safe for concurrent use, mirroring the pgx.Tx it accompanies.
func (*Notifier) Notify ¶ added in v0.4.0
Notify publishes the accumulated notifications. Invoke it once, after the transaction commits successfully; after a rollback, simply discard the Notifier. It is a no-op when there is nothing to publish — no PubSub configured, no messages staged, or a TxPublisher transport that already published on the transaction. Publishing is best-effort: failures are logged to the WithLogger logger, not returned, since durably staged messages are picked up by Subscribe's polling fallback regardless. Calling it more than once just repeats the wake-ups (harmless, like any spurious notification).
type Outbox ¶
type Outbox interface {
AddFlusher(topic string, flusher Flusher)
// AddMessages stages msgs on the topic within the caller's transaction.
// When a PubSub is configured, it also arranges the new-message
// notification that wakes Subscribe callers: TxPublisher transports
// publish it on tx itself, and generic transports hand it to the Notifier
// passed via WithNotifier, for the caller to fire after commit (see
// Notifier). Skipping the option never loses messages, it only leaves
// generic transports waiting out Subscribe's poll interval.
AddMessages(ctx context.Context, tx pgx.Tx, topic string, msgs []MessageOpts, opts ...AddOpt) error
// ProcessMessages grabs a batch of messages for the given topic, flushes them using the registered Flusher for that
// topic, and deletes them from the outbox if the flush is successful. If the topic has an active exclusive consumer,
// the calling instance must hold the exclusive lease (via AcquireTopic) or an error is returned.
ProcessMessages(ctx context.Context, topic string, opts ...ProcessOpt) ([]*sqlc.Message, error)
// Subscribe blocks and continuously drains the topic: it runs
// ProcessMessages until the topic is empty, then waits for the poll
// interval to elapse — or, when the outbox was built with WithPubSub, for
// a new-message notification — and drains again. Processing errors are
// logged to the WithLogger logger and retried on the next wake-up; as
// with ProcessMessages, topics with an active exclusive consumer require
// AcquireTopic first — either call it beforehand, or pass WithExclusive
// to have Subscribe acquire, re-acquire, and release the lease itself.
// Returns ctx.Err() when ctx ends, or an error immediately if no flusher
// is registered for the topic, the PubSub subscription cannot be
// established, or the WithExclusive initial acquisition fails.
Subscribe(ctx context.Context, topic string, opts ...SubscribeOpt) error
// AcquireTopic blocks until this instance holds the exclusive processing lease
// for the named topic, then returns. A background goroutine automatically renews
// the lease until ctx is cancelled or ReleaseTopic is called, at which point the
// lease expires naturally and another instance can take over. AcquireTopic must
// be called before ProcessMessages for any topic that has an active exclusive
// consumer.
AcquireTopic(ctx context.Context, topic string) error
// ReleaseTopic stops renewing and immediately expires the exclusive lease
// this instance holds for topic, letting another instance acquire it right
// away instead of waiting out the lease duration. It is a no-op if this
// instance does not currently hold the lease. As with a naturally expired
// lease, a subsequent ProcessMessages call still requires an explicit
// AcquireTopic first.
ReleaseTopic(ctx context.Context, topic string) error
}
type OutboxOpt ¶
type OutboxOpt func(*outboxImplOpts)
func WithAutoMigrate ¶
WithAutoMigrate controls whether NewOutbox runs the embedded migrations on construction. Defaults to true. Set to false when the caller wants to run migrations explicitly via Migrate (for example, in a separate startup phase or release pipeline).
func WithDefaultExpiration ¶ added in v0.2.0
WithDefaultExpiration sets a fallback TTL used for topics that have no specific expiration configured via WithTopicExpiration. Any topic that appears in the topics table with a NULL expiration_nanos will be maintained using this TTL when Start is running.
func WithLogger ¶ added in v0.2.0
WithLogger attaches a zerolog logger that receives error-level messages from the background maintenance goroutines. Lease competition (another instance holding the lease) is not logged. If not set, maintenance errors are silent.
func WithPubSub ¶ added in v0.4.0
WithPubSub attaches a PubSub used to cut end-to-end latency: AddMessages publishes a notification for each staged topic and Subscribe wakes on those notifications instead of waiting out its poll interval. Delivery is best-effort — Subscribe's polling remains the fallback for lost notifications. If ps also implements TxPublisher (NewPGPubSub does), the notification is published inside the AddMessages transaction and delivered exactly when it commits; otherwise pass a Notifier to AddMessages via WithNotifier and invoke Notify after committing.
func WithSchema ¶
func WithTopicExpiration ¶ added in v0.2.0
WithTopicExpiration registers a TTL for the named topic. On Start, the TTL is written to the topics table so that any outbox instance can discover it. Messages older than ttl are eligible for deletion by the background maintenance goroutine launched by Start. Per-topic TTLs take precedence over WithDefaultExpiration.
type PGPubSubOpt ¶ added in v0.4.0
type PGPubSubOpt func(*pgPubSubOpts)
PGPubSubOpt configures the PubSub returned by NewPGPubSub.
func WithNotifyChannel ¶ added in v0.4.0
func WithNotifyChannel(name string) PGPubSubOpt
WithNotifyChannel overrides the Postgres NOTIFY channel the PubSub multiplexes over. All messages on a channel are broadcast to every listener of that channel, so two outboxes sharing a database (e.g. different schemas) should use distinct channels to avoid spurious wake-ups.
func WithNotifyLogger ¶ added in v0.4.0
func WithNotifyLogger(l zerolog.Logger) PGPubSubOpt
WithNotifyLogger attaches a zerolog logger that receives errors from the background listener (connection failures, malformed payloads). If not set, those errors are silent.
type ProcessOpt ¶ added in v0.2.0
type ProcessOpt func(*processOpts)
ProcessOpt is a per-call option for ProcessMessages.
func WithBatchSize ¶
func WithBatchSize(n int) ProcessOpt
WithBatchSize sets the maximum number of messages ProcessMessages will acquire and hand to the Flusher in a single call. Must be > 0. Values above math.MaxInt32 are ignored and the default (1000) is used instead.
type PubSub ¶ added in v0.4.0
type PubSub interface {
// Pub publishes payload to topic.
Pub(ctx context.Context, topic string, payload []byte) error
// Sub subscribes to topic and returns a channel of messages published to
// it. The subscription lasts until ctx ends (or the PubSub itself shuts
// down), at which point the channel is closed. The channel should be
// buffered; implementations may drop messages rather than block when a
// slow consumer's buffer is full.
Sub(ctx context.Context, topic string) (<-chan *PubSubMessage, error)
}
PubSub is a minimal publish/subscribe transport for small notification messages. The outbox uses it (via WithPubSub) to wake Subscribe callers as soon as new messages are staged, instead of waiting out a poll interval.
Delivery is expected to be best-effort: implementations may drop messages under load or while disconnected. The outbox tolerates both lost messages (Subscribe falls back to polling) and duplicate or spurious messages (an extra processing pass on an empty topic is a no-op).
func NewPGPubSub ¶ added in v0.4.0
NewPGPubSub returns a PubSub backed by Postgres LISTEN/NOTIFY on the given pool. The background listener starts lazily on the first Sub call and runs until ctx is cancelled; pass a context tied to your application lifetime.
The returned PubSub implements TxPublisher, so an outbox configured with it publishes new-message notifications transactionally: subscribers wake when the staging transaction commits, and not at all if it rolls back.
NOTIFY payloads are capped by Postgres at roughly 8000 bytes; Pub returns an error beyond that. The outbox's own notifications are empty.
type PubSubMessage ¶ added in v0.4.0
type PubSubMessage struct {
// Topic is the pub/sub topic the message was published to.
Topic string `json:"topic"`
// Payload is the opaque message body. It may be nil: the outbox's own
// new-message notifications carry no payload, since the notification
// itself is the signal to check the outbox.
Payload []byte `json:"payload,omitempty"`
}
PubSubMessage is a single message delivered by a PubSub subscription.
type SubscribeOpt ¶ added in v0.4.0
type SubscribeOpt func(*subscribeOpts)
SubscribeOpt is a per-call option for Subscribe.
func WithExclusive ¶ added in v0.4.0
func WithExclusive() SubscribeOpt
WithExclusive makes Subscribe manage the topic's exclusive-consumer lease for the duration of the call: it acquires the lease before the first processing pass (blocking, like AcquireTopic, while another instance holds it), re-acquires it if it is ever lost mid-subscribe, and releases it on return so a waiting instance can take over immediately instead of waiting out the lease's grace period. Several instances calling Subscribe with WithExclusive on the same topic therefore form a failover group: exactly one drains the topic while the rest block in line behind the lease.
func WithPollInterval ¶ added in v0.4.0
func WithPollInterval(d time.Duration) SubscribeOpt
WithPollInterval sets how long Subscribe waits between processing passes when no new-message notification arrives. Must be > 0.
func WithProcessOpts ¶ added in v0.4.0
func WithProcessOpts(popts ...ProcessOpt) SubscribeOpt
WithProcessOpts forwards per-call ProcessMessages options (e.g. WithBatchSize) to every processing pass Subscribe makes.
type TxPublisher ¶ added in v0.4.0
type TxPublisher interface {
PubInTx(ctx context.Context, tx pgx.Tx, topic string, payload []byte) error
}
TxPublisher is an optional interface a PubSub can implement to publish within a pgx transaction. When the PubSub configured via WithPubSub implements it (detected once, at NewOutbox), AddMessages publishes its new-message notification inside the caller's transaction, so the notification is delivered exactly when the insert commits — and never for a transaction that rolls back. Without it, the notification is deferred to a Notifier the caller passes via WithNotifier and invokes after commit.