Documentation
¶
Overview ¶
Package pubsub provides typed, ephemeral publication with explicit loss and ownership.
Index ¶
- Constants
- Variables
- func Module(name foundation.ProviderID, key foundation.Key[*Broker], config Config, ...) foundation.Module
- func ValidatePublish(ctx context.Context, channel Channel, data []byte) error
- func ValidateSubscribe(ctx context.Context, channels []Channel, limits Limits) error
- type Backend
- type Broker
- type Brokers
- type Channel
- type Config
- type ConnectionName
- type Declaration
- type Delivery
- type Gap
- type KeyedSubscription
- type Limits
- type Message
- type Name
- type NamedBroker
- type Stats
- type Stream
- type Subscription
- type SupervisePolicy
- type Topic
- func (t Topic[K, V]) Name() Name
- func (t Topic[K, V]) Publish(ctx context.Context, key K, input V) (uint64, error)
- func (t Topic[K, V]) Subscribe(ctx context.Context, key K) (*Subscription[V], error)
- func (t Topic[K, V]) SubscribeKeys(ctx context.Context, first K, rest ...K) (*KeyedSubscription[K, V], error)
- func (t Topic[K, V]) Supervise(ctx context.Context, key K, policy SupervisePolicy, ...) error
- func (t Topic[K, V]) Validate() error
- func (t Topic[K, V]) Version() Version
- type Version
Constants ¶
const ChannelFeature keyspace.Feature = "pubsub"
ChannelFeature is the physical address family owned by this adapter.
const MaxSuperviseBackoff = 10 * time.Minute
MaxSuperviseBackoff bounds the delay between re-subscription attempts.
Variables ¶
var ErrClosed = safeerror.Sentinel(fault.Closed, "pub/sub subscription closed")
var ErrDisconnected = safeerror.Sentinel(fault.Conflict, "pub/sub subscription disconnected; delivery may have been lost")
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 ¶
ValidatePublish checks cancellation, channel validity, and nonempty transport payload bounds.
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 ¶
NewBroker validates configuration and constructs a broker borrowing backend without performing I/O.
func (*Broker) Close ¶
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.
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) 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.
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 ¶
DefaultConfig returns the default broker and buffer bounds for namespace.
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 (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 ¶
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.
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.
type Message ¶
Message owns its payload. Adapter streams may multiplex exact channels; the typed application API retains its concrete topic and resource key boundary.
type NamedBroker ¶
type NamedBroker struct {
Name ConnectionName
Value *Broker
}
NamedBroker borrows one existing instance. Selection never creates another owner.
type Stats ¶
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.
type SupervisePolicy ¶
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]) Publish ¶
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.