Help us shape the future of Go! Take the 2026 Go Developer Survey

pubsub

package
v0.5.3 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package pubsub provides typed, ephemeral publication with explicit loss and ownership.

Index

Constants

View Source
const ChannelFeature keyspace.Feature = "pubsub"

ChannelFeature is the physical address family owned by this adapter.

View Source
const MaxSuperviseBackoff = 10 * time.Minute

MaxSuperviseBackoff bounds the delay between re-subscription attempts.

Variables

View Source
var ErrClosed = safeerror.Sentinel(fault.Closed, "pub/sub subscription closed")
View Source
var ErrDisconnected = safeerror.Sentinel(fault.Conflict, "pub/sub subscription disconnected; delivery may have been lost")
View Source
var ErrOverflow = safeerror.Sentinel(fault.Conflict, "pub/sub subscription lost messages because its buffer filled")

Functions

func Module

func Module(name foundation.ProviderID, key foundation.Key[*Broker], config Config, requires []foundation.ProviderID, backend func(foundation.Resolver) (Backend, error)) foundation.Module

Module owns one broker per application. Requires identifies providers owning its borrowed backend, keeping adapters alive until subscriptions have drained.

func ValidatePublish

func ValidatePublish(ctx context.Context, channel Channel, data []byte) error

ValidatePublish checks cancellation, channel validity, and nonempty transport payload bounds.

func ValidateSubscribe

func ValidateSubscribe(ctx context.Context, channels []Channel, limits Limits) error

ValidateSubscribe checks cancellation, limits, channel validity, and duplicate channels.

Types

type Backend

type Backend interface {
	// Publish sends data to channel and returns the authority's subscriber count, without confirming processing.
	Publish(context.Context, Channel, []byte) (uint64, error)
	// Subscribe establishes a ready stream over the exact channels using limits to bound buffering.
	Subscribe(context.Context, []Channel, Limits) (Stream, error)
}

Backend exposes ephemeral fan-out, separate from jobs or durable events. Publish returns the authority's subscriber count, not a processing acknowledgement: memory counts subscriptions, Redis counts subscribed connections (one client multiplexes all of its subscriptions over one connection). Subscribe's context bounds establishment only; the returned stream is explicitly owned until Close or adapter shutdown. No implicit publish retry or fallback.

type Broker

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

Broker borrows its adapter and owns callbacks and typed subscriptions. Pure construction performs no I/O. Close cancels work and waits for actual exit.

func NewBroker

func NewBroker(backend Backend, config Config) (*Broker, error)

NewBroker validates configuration and constructs a broker borrowing backend without performing I/O.

func (*Broker) Close

func (b *Broker) Close(ctx context.Context) error

Close begins shutdown even when its wait context has expired. Canceled callers can wait again; no resource slot is released before the owned callback exits.

func (*Broker) Done

func (b *Broker) Done() <-chan struct{}

Done closes after shutdown drains operations and subscriptions. A nil broker returns nil.

func (*Broker) Namespace

func (b *Broker) Namespace() keyspace.Namespace

Namespace returns the broker's configured application and environment scope.

func (*Broker) Stats

func (b *Broker) Stats() Stats

Stats returns a synchronized snapshot of active operations, retained subscriptions, and shutdown state.

type Brokers

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

Brokers is immutable; default and named selection alias the same instance.

func NewBrokers

func NewBrokers(selected ConnectionName, entries ...NamedBroker) (*Brokers, error)

NewBrokers validates the named entries and selected default without taking ownership of brokers.

func (*Brokers) Broker

func (r *Brokers) Broker(name ConnectionName) (*Broker, error)

Broker returns the broker registered under name; unknown names return an error.

func (*Brokers) Default

func (r *Brokers) Default() (*Broker, error)

Default returns the existing broker selected as the default.

func (*Brokers) DefaultName

func (r *Brokers) DefaultName() ConnectionName

DefaultName returns the selected default name, or an empty name for a nil registry.

func (*Brokers) Names

func (r *Brokers) Names() []ConnectionName

Names returns an owned, sorted snapshot of registered names.

type Channel

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

Channel is an opaque, versioned adapter address. Redis database numbers do not isolate pub/sub; application/environment identity is always part of this address.

func NewChannel

func NewChannel(namespace keyspace.Namespace, name Name, version Version, logical string) (Channel, error)

NewChannel constructs a namespaced, versioned adapter address from a logical resource key.

func (Channel) Namespace

func (c Channel) Namespace() keyspace.Namespace

Namespace returns the channel's application and environment scope.

func (Channel) String

func (c Channel) String() string

String returns the physical versioned address, or an empty string for an invalid channel.

func (Channel) Validate

func (c Channel) Validate() error

Validate checks the address and positive schema version.

type Config

type Config struct {
	Namespace                                                     keyspace.Namespace
	MaxDeclarations, MaxKeyBytes, MaxConcurrent, MaxSubscriptions int
	Timeout                                                       time.Duration
	Buffer                                                        Limits
}

Config bounds publication/setup callbacks and separately bounds subscriptions, each of which permits one active receive/decode operation at a time.

func DefaultConfig

func DefaultConfig(namespace keyspace.Namespace) Config

DefaultConfig returns the default broker and buffer bounds for namespace.

func (Config) Validate

func (c Config) Validate() error

Validate checks the namespace, positive broker bounds, and subscription buffer limits.

type ConnectionName

type ConnectionName string

ConnectionName selects an instance, distinct from names of other service families.

func (ConnectionName) Validate

func (n ConnectionName) Validate() error

Validate checks that the connection name is a valid named-service identifier.

type Declaration

type Declaration[K, V any] struct {
	// contains filtered or unexported fields
}

Declaration preserves both resource-key and JSON payload types. Increment Version for incompatible payload contracts; define once and reuse across callers.

func Define

func Define[K, V any](name Name, version Version, keys keyspace.Codec[K]) Declaration[K, V]

Define constructs a typed, versioned topic family using keys to encode resource keys.

func (Declaration[K, V]) Bind

func (d Declaration[K, V]) Bind(b *Broker) (Topic[K, V], error)

Bind registers this declaration with b and returns a typed topic. A different declaration with the same name and version fails with fault.Duplicate.

func (Declaration[K, V]) Name

func (d Declaration[K, V]) Name() Name

Name returns the topic family name, or an empty name for a zero declaration.

func (Declaration[K, V]) Validate

func (d Declaration[K, V]) Validate() error

Validate checks the name, positive schema version, and resource key codec.

func (Declaration[K, V]) Version

func (d Declaration[K, V]) Version() Version

Version returns the schema version, or zero for a zero declaration.

type Delivery

type Delivery[K, V any] struct {
	Key   K
	Value V
}

Delivery pairs a received payload with the typed key it was published under.

type Gap

type Gap struct {
	Cause    error
	Attempts int
}

Gap reports one interruption after which delivery has resumed. Payloads published between Cause and the new subscription's readiness were lost: Redis pub/sub is at-most-once. Attempts counts failed re-subscriptions.

type KeyedSubscription

type KeyedSubscription[K, V any] struct {
	// contains filtered or unexported fields
}

KeyedSubscription is one typed stream over several keys of the same topic. It shares Subscription's ownership, sequential receive and failure semantics.

func (*KeyedSubscription[K, V]) Close

func (s *KeyedSubscription[K, V]) Close(ctx context.Context) error

Close starts shutdown and waits for the underlying typed subscription's cleanup.

func (*KeyedSubscription[K, V]) Done

func (s *KeyedSubscription[K, V]) Done() <-chan struct{}

Done closes after the underlying subscription drains and cleans up. A nil subscription returns nil.

func (*KeyedSubscription[K, V]) Err

func (s *KeyedSubscription[K, V]) Err() error

Err returns the terminal cause from the underlying subscription.

func (*KeyedSubscription[K, V]) Receive

func (s *KeyedSubscription[K, V]) Receive(ctx context.Context) (Delivery[K, V], error)

Receive returns the next payload together with the key it was published to.

type Limits

type Limits struct{ Messages, Bytes, PayloadBytes, Channels int }

Limits bound a subscription's payloads and pending messages. Queue bytes count payload bytes; fixed-size message metadata is additionally bounded by Messages.

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits returns the default per-subscription buffer and channel bounds.

func (Limits) Validate

func (l Limits) Validate() error

Validate checks buffer sizes, payload size, and channel count against transport bounds.

type Message

type Message struct {
	Channel Channel
	Data    []byte
}

Message owns its payload. Adapter streams may multiplex exact channels; the typed application API retains its concrete topic and resource key boundary.

type Name

type Name string

Name identifies a pub/sub topic family.

type NamedBroker

type NamedBroker struct {
	Name  ConnectionName
	Value *Broker
}

NamedBroker borrows one existing instance. Selection never creates another owner.

type Stats

type Stats struct {
	Operations, Subscriptions int
	Closing                   bool
}

Stats includes setup, live and draining subscriptions until their resources and active callbacks have finished. Reading it performs no I/O.

type Stream

type Stream interface {
	// Next waits for the next owned message. Cancellation ends this wait without closing the stream.
	Next(context.Context) (Message, error)
	// Close ends the subscription and unblocks reads; ctx bounds the shutdown wait.
	Close(context.Context) error
	// Done closes after the stream's adapter work exits.
	Done() <-chan struct{}
	// Err returns the terminal subscription error.
	Err() error
}

Stream is ready when Subscribe returns. Next cancellation stops that wait; Close owns the subscription lifetime and must unblock pending reads. Done closes only after adapter work exits. Errors never imply an uninterrupted subscription. Implementations must not silently drop messages or hide reconnection gaps.

type Subscription

type Subscription[V any] struct {
	// contains filtered or unexported fields
}

Subscription owns one typed stream. Receive is sequential, including JSON decoding; concurrent receives fail instead of creating unbounded waiting work. Terminal stream/decoding failures discard pending delivery and close the stream.

func (*Subscription[V]) Close

func (s *Subscription[V]) Close(ctx context.Context) error

Close starts subscription shutdown and waits for cleanup and active receive work. Cancellation ends only the wait; shutdown continues.

func (*Subscription[V]) Done

func (s *Subscription[V]) Done() <-chan struct{}

Done closes after adapter cleanup and active receive work finish. A zero subscription returns nil.

func (*Subscription[V]) Err

func (s *Subscription[V]) Err() error

Err returns the terminal subscription cause, or nil while no terminal cause is recorded.

func (*Subscription[V]) Receive

func (s *Subscription[V]) Receive(ctx context.Context) (V, error)

Receive waits for and decodes the next typed payload. Concurrent receives fail with fault.Conflict; cancellation of a wait preserves the subscription.

type SupervisePolicy

type SupervisePolicy struct {
	InitialBackoff, MaxBackoff time.Duration
}

SupervisePolicy bounds automatic re-subscription after an interruption. The delay starts at InitialBackoff, doubles (with jitter) after each failed attempt up to MaxBackoff, and resets once delivery has resumed.

func DefaultSupervisePolicy

func DefaultSupervisePolicy() SupervisePolicy

DefaultSupervisePolicy returns the default initial and maximum resubscription delays.

func (SupervisePolicy) Validate

func (p SupervisePolicy) Validate() error

Validate checks positive, ordered delays within MaxSuperviseBackoff.

type Topic

type Topic[K, V any] struct {
	// contains filtered or unexported fields
}

Topic retains concrete keys and payloads through publication and subscription.

func (Topic[K, V]) Name

func (t Topic[K, V]) Name() Name

Name returns the topic family name.

func (Topic[K, V]) Publish

func (t Topic[K, V]) Publish(ctx context.Context, key K, input V) (uint64, error)

Publish snapshots typed JSON using Foundry's shared value validation. The return count describes subscriptions at the authority, not completed application work. A lost acknowledgement is an error even if publication already happened.

func (Topic[K, V]) Subscribe

func (t Topic[K, V]) Subscribe(ctx context.Context, key K) (*Subscription[V], error)

Subscribe waits for confirmed readiness. Its context bounds establishment only; the Broker owns the subscription afterwards. Close it when no longer needed. Receive waits use their own contexts and do not implicitly resubscribe.

func (Topic[K, V]) SubscribeKeys

func (t Topic[K, V]) SubscribeKeys(ctx context.Context, first K, rest ...K) (*KeyedSubscription[K, V], error)

SubscribeKeys confirms readiness for every key in one subscription, whose Receive reports which typed key each payload was published under. Duplicate keys are rejected; the count is bounded by Config.Buffer.Channels. Keys are retained for delivery and must stay immutable while the subscription lives.

func (Topic[K, V]) Supervise

func (t Topic[K, V]) Supervise(ctx context.Context, key K, policy SupervisePolicy, receive func(context.Context, V) error, gap func(context.Context, Gap) error) error

Supervise keeps one key subscribed until ctx ends, the broker closes, or a callback returns an error. receive handles each payload in order. After any terminal interruption (disconnect, overflow, undecodable payload) Supervise re-subscribes with backoff and, once the NEW subscription is confirmed ready, calls gap before delivering anything else, so the application can reconcile state from its source of truth without missing a later change. Changes made while gap runs are queued (within Buffer limits) and delivered afterwards, so receive must tolerate a payload that the reconciliation already reflected. Invalid keys, cycles and contained callback panics end supervision instead of retrying; every ErrDisconnected (including one during establishment) is retried. It returns ctx.Err() on cancellation and ErrClosed on shutdown, the only permanent adapter failure.

func (Topic[K, V]) Validate

func (t Topic[K, V]) Validate() error

Validate checks that the topic has a broker and a valid declaration.

func (Topic[K, V]) Version

func (t Topic[K, V]) Version() Version

Version returns the payload schema version.

type Version

type Version uint32

Version identifies a payload schema. Zero is invalid.

Directories

Path Synopsis
Package memory provides explicit local ephemeral fan-out for pub/sub contracts.
Package memory provides explicit local ephemeral fan-out for pub/sub contracts.

Jump to

Keyboard shortcuts

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