Documentation
¶
Overview ¶
Package streams publishes to and consumes RabbitMQ streams over the native stream protocol (default port 5552, rabbitmq_stream plugin).
Importing this package (a blank import is enough) registers the lane with app so a configured messaging.streams.uri starts the manager. A process that never imports it carries none of the vendor client (ADR-091).
It complements the AMQP 0.9.1 lane in the parent messaging package: streams declared as AMQP queues (x-queue-type: stream) can be consumed there, but server-side offset tracking and single active consumer need this protocol. Publishing is synchronous and confirmed — see wiki/streams.md.
Index ¶
- Constants
- Variables
- func DeclareTypedConsumer[T any](decls *Declarations, opts *ConsumerOptions, fn func(context.Context, T) error)
- func DeclareTypedConsumerWithMeta[T any](decls *Declarations, opts *ConsumerOptions, ...)
- func DeclareTypedSuperStreamConsumer[T any](decls *Declarations, opts *SuperStreamConsumerOptions, ...)
- func DeclareTypedSuperStreamConsumerWithMeta[T any](decls *Declarations, opts *SuperStreamConsumerOptions, ...)
- func Permanent(err error) error
- type ConsumerOptions
- type Declarations
- func (d *Declarations) DeclareConsumer(opts *ConsumerOptions)
- func (d *Declarations) DeclarePublisher(opts *PublisherOptions) *Publisher
- func (d *Declarations) DeclareStream(name string, spec *StreamSpec)
- func (d *Declarations) DeclareSuperStream(name string, partitions int, spec *StreamSpec)
- func (d *Declarations) DeclareSuperStreamConsumer(opts *SuperStreamConsumerOptions)
- func (d *Declarations) DeclareSuperStreamPublisher(opts *SuperStreamPublisherOptions) *Publisher
- func (d *Declarations) IsEmpty() bool
- func (d *Declarations) Stats() Stats
- func (d *Declarations) Validate() error
- type Handler
- type HeldMessage
- type HoldLedger
- type HoldReplayer
- type Manager
- func (m *Manager) Close() error
- func (m *Manager) HoldConsumers() []string
- func (m *Manager) Ready() bool
- func (m *Manager) ReloadHeld(ctx context.Context, consumer string) error
- func (m *Manager) Replay(ctx context.Context, consumer string, msg *HeldMessage) error
- func (m *Manager) SetTenantStamps(enabled bool)
- func (m *Manager) Start(ctx context.Context, decls *Declarations) error
- func (m *Manager) Stats() map[string]any
- func (m *Manager) StopConsumers()
- type ManagerOptions
- type Message
- type OffsetStart
- type PayloadError
- type PayloadStage
- type PublishMessage
- type Publisher
- type PublisherOptions
- type RetryOptions
- type Stats
- type StreamDeclarer
- type StreamSpec
- type SuperStreamConsumerOptions
- type SuperStreamPublisherOptions
Constants ¶
const ( // MaxRetryAttempts and MaxRetryWait bound what a declared policy may ask of one // partition. The waits happen inside that partition's own delivery callback, so // a long policy is a stall every OTHER tenant on the partition pays for; work // that needs more patience than this belongs in the hold, which parks one // tenant and lets the partition move. MaxRetryAttempts = 10 MaxRetryWait = time.Minute )
const ( // The span attributes this lane adds on top of the four the pipeline sets. // AttrConsumerName is exported because a hold's gauges carry the same key: an // operator joins the backlog to the consumer's own spans and logs by it, so the // two must never drift apart silently. AttrConsumerName = "messaging.consumer.name" )
const TenantStampProperty = tenantstamp.Header
TenantStampProperty is the application property the framework writes the publishing context's tenant into, and the one a consumer reads it back from. It is the same entry the classic lane uses as an AMQP 0.9.1 header.
Variables ¶
var ( // ErrPayloadUndecodable reports a message body that could not be decoded into // the consumer's payload type. Match it with errors.Is. ErrPayloadUndecodable = errors.New("streams: payload could not be decoded") // ErrPayloadInvalid reports a payload that decoded but failed struct // validation. Match it with errors.Is. ErrPayloadInvalid = errors.New("streams: payload failed validation") )
var ( // ErrPublisherNotStarted reports a publish on a publisher Manager.Start has // not bound to a client producer yet. ErrPublisherNotStarted = errors.New("streams: publisher not started; Manager.Start binds it") // ErrPublisherClosed reports a publish on a publisher whose manager has shut // down, and is what an in-flight publish is resolved with when that happens. ErrPublisherClosed = errors.New("streams: publisher closed") )
var DefaultHoldRetry = RetryOptions{ MaxAttempts: 3, InitialBackoff: 200 * time.Millisecond, MaxBackoff: 2 * time.Second, }
DefaultHoldRetry is the policy a holding consumer gets when it declares none. A consumer that does not hold keeps today's single attempt unless it asks for a policy itself.
var ErrTenantStampConflict = tenantstamp.ErrConflict
ErrTenantStampConflict reports a publish whose tenant stamp was supplied by the caller. The framework is the stamp's only writer on both lanes, so this is the same error value messaging.ErrTenantStampConflict names — errors.Is holds whichever lane raised it.
Functions ¶
func DeclareTypedConsumer ¶ added in v0.62.0
func DeclareTypedConsumer[T any](decls *Declarations, opts *ConsumerOptions, fn func(context.Context, T) error)
DeclareTypedConsumer registers a stream consumer whose handler decodes the message body into T and validates it against the struct's `validate` tags before calling fn. It is the streams-lane mirror of messaging.DeclareTypedConsumer, and T is inferred from fn.
There is deliberately no exported NewTypedHandler counterpart on this lane. The declaration carries a poison screen alongside the handler, and a handler handed to DeclareConsumer as a plain Handler could not carry one — a typed consumer assembled that way would park undecodable bodies in the hold, which is exactly what ADR-092 forbids.
Failure semantics: a body that does not decode, or decodes but fails validation, is deterministic poison. It is not retried in place whatever Retry says, it is never parked when Hold is set, and its offset is not committed — the lane skips it, exactly as ADR-059 settles any failure on a consumer that does not hold. Match the two modes with errors.Is against ErrPayloadUndecodable and ErrPayloadInvalid.
It panics on a nil decls, a nil opts, or an opts that already carries a Handler — all three are declaration-time wiring mistakes, which this lane already fails startup on for duplicate registrations.
func DeclareTypedConsumerWithMeta ¶ added in v0.62.0
func DeclareTypedConsumerWithMeta[T any](decls *Declarations, opts *ConsumerOptions, fn func(context.Context, T, *Message) error)
DeclareTypedConsumerWithMeta is DeclareTypedConsumer for an fn that also needs the delivery itself — msg.Offset, msg.Stream and msg.Properties. Same panics, same poison semantics; msg.Data is the body fn's payload was decoded from.
func DeclareTypedSuperStreamConsumer ¶ added in v0.62.0
func DeclareTypedSuperStreamConsumer[T any](decls *Declarations, opts *SuperStreamConsumerOptions, fn func(context.Context, T) error)
DeclareTypedSuperStreamConsumer is DeclareTypedConsumer over every partition of a super stream. fn is called CONCURRENTLY across partitions — see Handler — so it must be safe for concurrent use.
func DeclareTypedSuperStreamConsumerWithMeta ¶ added in v0.62.0
func DeclareTypedSuperStreamConsumerWithMeta[T any](decls *Declarations, opts *SuperStreamConsumerOptions, fn func(context.Context, T, *Message) error)
DeclareTypedSuperStreamConsumerWithMeta is DeclareTypedSuperStreamConsumer for an fn that also needs the delivery. msg.Stream names the PARTITION the message arrived on, not the super stream.
Types ¶
type ConsumerOptions ¶
type ConsumerOptions struct {
// Stream is the stream to consume; it must be declared in the same Declarations.
Stream string
// Name is the consumer/group name. Required: it is the key the broker stores
// offsets under and, with SAC, the group identity.
Name string
// Start is where to begin when the broker holds no stored offset for Name.
Start OffsetStart
// SAC enables single active consumer: only one member of the group receives
// messages at a time (RabbitMQ 3.11+).
SAC bool
// Handler processes each message. Required.
Handler Handler
// Retry bounds in-place re-invocation of Handler after it returns an error.
// Nil is one attempt, unless Hold is set — see DefaultHoldRetry. A policy may
// ask for at most MaxRetryAttempts attempts and MaxRetryWait of total waiting,
// because the waits run inside the partition's own delivery callback; a failure
// that needs more patience than that belongs in the hold.
Retry *RetryOptions
// Hold parks a failed delivery per tenant so the tenant's later messages wait
// behind it while the rest of the partition keeps flowing.
//
// SECURITY: the tenant is read from the producer-written stamp, which is
// identification and not authorization (ADR-039's rule for HTTP resolvers,
// applied here). A producer that can publish to this stream therefore chooses
// which tenant a message is held under, and a failure it induces holds THAT
// tenant until the hold drains. Isolating producers — per-tenant credentials
// or vhosts — stays the deployment's job, and it matters more here than for a
// delivery that is merely mis-attributed.
Hold bool
// TenantOptional lets this consumer run a delivery that carries no tenant stamp
// (a control-plane consumer). Default false: a consumer that needs a tenant fails
// the delivery closed rather than running the handler without one. It never
// admits a stamp that is present but unusable.
TenantOptional bool
}
ConsumerOptions declares one stream consumer.
type Declarations ¶
type Declarations struct {
// contains filtered or unexported fields
}
Declarations collects the stream infrastructure and consumers a set of modules declares. It is populated once at startup, validated, then replayed by Manager.
func NewDeclarations ¶
func NewDeclarations() *Declarations
NewDeclarations creates an empty declaration store.
func (*Declarations) DeclareConsumer ¶
func (d *Declarations) DeclareConsumer(opts *ConsumerOptions)
DeclareConsumer registers a stream consumer. Panics on a nil options pointer, and if the same (stream, name) pair was already declared. Both are programming errors: a nil declaration would consume nothing at all, and a duplicate would otherwise start two members of the same offset group inside one process.
func (*Declarations) DeclarePublisher ¶
func (d *Declarations) DeclarePublisher(opts *PublisherOptions) *Publisher
DeclarePublisher registers a publisher on a plain stream and returns the handle to publish through. The handle is inert until Manager.Start binds it.
Panics on a nil options pointer, and if the same stream already has a publisher. Both are programming errors: a nil declaration would publish nowhere, and one publisher per target per process is the same contract the consumer side enforces — a second one is a wiring mistake, not a fan-out.
func (*Declarations) DeclareStream ¶
func (d *Declarations) DeclareStream(name string, spec *StreamSpec)
DeclareStream registers a stream. A nil spec leaves retention to the broker. Re-declaring the same name with an identical spec is a no-op; a conflicting spec is reported by Validate.
func (*Declarations) DeclareSuperStream ¶
func (d *Declarations) DeclareSuperStream(name string, partitions int, spec *StreamSpec)
DeclareSuperStream registers a super stream of partitions partitions. A nil spec leaves retention to the broker; a non-nil one applies to every partition. Re-declaring the same name identically is a no-op; a conflicting partition count or spec is reported by Validate.
func (*Declarations) DeclareSuperStreamConsumer ¶
func (d *Declarations) DeclareSuperStreamConsumer(opts *SuperStreamConsumerOptions)
DeclareSuperStreamConsumer registers one consumer over every partition of a super stream. Panics on the same two programming errors as DeclareConsumer.
The declaration is always a single active consumer group — see SuperStreamConsumerOptions for why the choice is not the caller's.
func (*Declarations) DeclareSuperStreamPublisher ¶
func (d *Declarations) DeclareSuperStreamPublisher(opts *SuperStreamPublisherOptions) *Publisher
DeclareSuperStreamPublisher registers a publisher across every partition of a super stream and returns the handle to publish through. Panics on the same two programming errors as DeclarePublisher.
Every PublishMessage sent through the returned handle must carry a non-empty RoutingKey: it is what picks the partition.
func (*Declarations) IsEmpty ¶
func (d *Declarations) IsEmpty() bool
IsEmpty reports whether nothing was declared.
func (*Declarations) Stats ¶
func (d *Declarations) Stats() Stats
Stats returns the declaration counts.
func (*Declarations) Validate ¶
func (d *Declarations) Validate() error
Validate reports every problem in the store at once. Each declaration kind contributes its own errors, in declaration order, so a caller sees the whole picture rather than the first thing that went wrong.
type Handler ¶
Handler processes one stream message. An error is terminal for the MESSAGE, not for the stream: the failure is logged and counted, the offset is NOT committed, and consumption continues with the next message. Streams have no nack or redelivery, so handlers must be idempotent.
Calls are sequential within one stream — and within one partition of a super stream — but CONCURRENT across the partitions of a super stream, because each partition is a separate connection with its own delivery loop. A handler registered with DeclareSuperStreamConsumer must therefore be goroutine-safe.
type HeldMessage ¶ added in v0.61.0
type HeldMessage = streamruntime.HeldMessage
The hold port lives on the streamruntime seam so inbox can implement it without importing this package (ADR-091). The aliases keep the streams spelling for lane callers.
type HoldLedger ¶ added in v0.61.0
type HoldLedger = streamruntime.HoldLedger
The hold port lives on the streamruntime seam so inbox can implement it without importing this package (ADR-091). The aliases keep the streams spelling for lane callers.
type HoldReplayer ¶ added in v0.61.0
type HoldReplayer = streamruntime.HoldReplayer
The hold port lives on the streamruntime seam so inbox can implement it without importing this package (ADR-091). The aliases keep the streams spelling for lane callers.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager owns the single stream-protocol Environment of a single-tenant service and the consumers and publishers started from its declarations. It is a plain struct on purpose (ADR-045): app/ consumes it concretely, so there is no interface to export and no second implementation to abstract over.
func NewManager ¶
func NewManager(opts ManagerOptions) *Manager
Panics on a nil Logger. Every consumer lifecycle, handler-failure and shutdown path dereferences it unguarded, so a wiring error would otherwise surface as a nil dereference on the first log line — mid-consumption, in production, from a goroutine the client owns and nothing recovers. Same fail-fast as httpclient.NewBuilder, and it keeps this constructor's single return value. pointer here is an incompatible change (apidiff), and the copy costs one allocation per manager, at startup.
func (*Manager) HoldConsumers ¶ added in v0.61.0
HoldConsumers names the running consumers that hold. A consumer that does not hold has nothing parked, so the drain never asks about it.
func (*Manager) Ready ¶
Ready reports whether every started consumer and publisher is currently connected.
func (*Manager) ReloadHeld ¶ added in v0.61.0
ReloadHeld refreshes one consumer's held set from the ledger. A consumer this replica does not run is a no-op: the ledger is shared and deployments differ in which consumers they start.
The read happens here rather than in the caller because the generation that makes the replace safe must be taken BEFORE it. A caller that read the ledger first could only hand back a token taken after its own read, which compares equal to itself and erases any park that landed in between.
func (*Manager) Replay ¶ added in v0.61.0
Replay puts a held message back through the lane. It returns the handler's own error untouched: the drain decides what a failed replay means — defer the tenant — and this call settles nothing, because the row's fate is the drain's to write.
func (*Manager) SetTenantStamps ¶ added in v0.61.0
NewManager creates a Manager. It performs no I/O: the environment is dialed by Start, so a service that declares no streams never opens a connection.
SetTenantStamps tells this manager's consumers to read the tenant stamp off each delivery and seed the handler context with it — true only under multitenant.enabled together with messaging.tenancy: shared.
It is a setter rather than a ManagerOptions field because that struct already sits at gocritic's hugeParam limit; one more field would force NewManager to take a pointer, which is an incompatible change to a shipped exported function. Call it before StartConsumers: a runner reads the flag when it is built.
func (*Manager) Start ¶
func (m *Manager) Start(ctx context.Context, decls *Declarations) error
Start dials the broker, replays the stream declarations, binds one reliable producer per declared publisher and starts one reliable consumer per declared consumer. Empty declarations are a no-op that dials nothing. Anything that fails to start stops what already came up and returns an error — the caller makes that fatal. A failure leaves nothing to dispose: the connection is closed before the error returns, so a caller that never calls Close does not leak it, and a retried Start cannot orphan the previous environment.
Start is not a resume: once it has dialed, it refuses to run again until Close disposes the environment. StopConsumers deliberately leaves that environment open, so redialing over it would orphan a connection the registered closer still owns.
ctx contributes its VALUES to every handler invocation, never its cancellation: consumers outlive the startup call that created them, and StopConsumers is what ends them. See consumeContext.
func (*Manager) StopConsumers ¶
func (m *Manager) StopConsumers()
StopConsumers stops every consumer, then closes every publisher. Each consumer flushes its pending offset BEFORE closing, so a clean shutdown does not replay successfully handled messages. Publishers close AFTER them — a handler may publish on its way out — and every publish still awaiting a confirmation is resolved with ErrPublisherClosed rather than left to hang. Idempotent.
This is shutdown phase one, not a pause: the environment stays open for Close to dispose, and Start stays refused until then.
type ManagerOptions ¶
type ManagerOptions struct {
// URI is the stream-protocol endpoint (rabbitmq-stream:// or rabbitmq-stream+tls://).
URI string
// AddressResolverHost and AddressResolverPort pin every connection to one
// entry point. Required behind a load balancer, NAT, or Docker port mapping,
// where the address the broker advertises is not reachable by the client.
AddressResolverHost string
AddressResolverPort int
// OffsetStoreCount is how many successfully handled messages accumulate
// before an offset is committed server-side.
OffsetStoreCount int
// OffsetStoreInterval is how long after the last commit a pending offset is
// committed anyway.
OffsetStoreInterval time.Duration
// Logger receives consumer lifecycle and handler-failure events. Required.
Logger logger.Logger
// Hold is the ledger consumers declaring Hold park into. Nil means no consumer
// may declare one — the framework wires it from the inbox's hold, which lives
// on the control-plane database.
Hold HoldLedger
}
ManagerOptions configures the stream-protocol Manager. The zero value of every tuning field applies its default.
type Message ¶
type Message struct {
// Data is the first data section of the AMQP 1.0 message body.
Data []byte
// Offset is this message's offset within Stream.
Offset int64
// Stream is the stream (or, for super streams, the partition) it arrived on.
Stream string
// Properties carries the AMQP 1.0 application properties, nil when absent.
Properties map[string]any
}
Message is the framework view of a stream delivery.
type OffsetStart ¶
type OffsetStart struct {
// contains filtered or unexported fields
}
OffsetStart is where a consumer begins when the broker holds no stored offset for its name. A stored offset always wins over it — see Manager.Start. The zero value is OffsetNext().
func OffsetAt ¶
func OffsetAt(offset int64) OffsetStart
OffsetAt starts at an absolute stream offset.
func OffsetFirst ¶
func OffsetFirst() OffsetStart
OffsetFirst starts at the oldest message still retained in the stream.
func OffsetLast ¶
func OffsetLast() OffsetStart
OffsetLast starts at the beginning of the last chunk written to the stream.
func OffsetNext ¶
func OffsetNext() OffsetStart
OffsetNext starts at the next message written after the consumer attaches.
func OffsetSince ¶
func OffsetSince(t time.Time) OffsetStart
OffsetSince starts at the first message stored at or after t.
type PayloadError ¶ added in v0.62.0
type PayloadError struct {
// Consumer is the declared consumer name the message was routed to. The
// stream and offset are on the delivery's own log line and span; naming the
// consumer is what tells two typed consumers of the same stream apart.
Consumer string
// Stage is where the failure happened. It is exported to label logs and
// metrics with "decode" or "validate"; for control flow, match errors.Is
// against ErrPayloadUndecodable or ErrPayloadInvalid instead.
Stage PayloadStage
// contains filtered or unexported fields
}
PayloadError describes why a stream message could not be turned into a typed payload. It is this lane's thin surface over payloaderr.Body, which owns the rendering rules and the SECURITY rationale behind them: Error() and Fields() are safe to log, Unwrap() is not.
It is also the lane's poison marker. A body that does not decode or does not validate fails the same way for every attempt and every replica, so a delivery carrying one is never retried in place and never parked in the hold — see ADR-092 and consumerRunner.parks.
func (*PayloadError) Error ¶ added in v0.62.0
func (e *PayloadError) Error() string
func (*PayloadError) Fields ¶ added in v0.62.0
func (e *PayloadError) Fields() []string
Fields returns the validator field namespaces that failed, e.g. ["OrderPayload.Amount"], with every bracketed span redacted. It is empty for decode failures and for a nil receiver.
func (*PayloadError) Is ¶ added in v0.62.0
func (e *PayloadError) Is(target error) bool
Is maps the stage onto its sentinel so consumers can discriminate the two failure modes without reading Stage.
func (*PayloadError) Unwrap ¶ added in v0.62.0
func (e *PayloadError) Unwrap() error
Unwrap exposes the underlying decode or validation error so errors.As can reach the cause.
SECURITY: the returned error MAY carry payload-derived text. It is the deliberate escape hatch for a caller that needs the raw diagnostic; logging it is opt-in and on the caller.
type PayloadStage ¶ added in v0.62.0
type PayloadStage string
PayloadStage names the half of the typed-payload pipeline that failed.
const ( PayloadStageDecode PayloadStage = PayloadStage(payloaderr.StageDecode) PayloadStageValidate PayloadStage = PayloadStage(payloaderr.StageValidate) )
Stage values carried by PayloadError.Stage. A PayloadError whose Stage is none of these matches neither sentinel.
type PublishMessage ¶
type PublishMessage struct {
// Data becomes the AMQP 1.0 data section of the message body.
Data []byte
// Properties carries AMQP 1.0 application properties; nil is fine. The map is
// copied, so the caller's own map is never written to.
Properties map[string]any
// RoutingKey selects the partition of a super stream (murmur3 hash — the
// RabbitMQ cross-client default). Required non-empty on a publisher declared
// with DeclareSuperStreamPublisher, and must be empty on one declared with
// DeclarePublisher, which targets a plain stream with no partitions to pick.
RoutingKey string
}
PublishMessage is the framework view of an outbound stream message.
type Publisher ¶
type Publisher struct {
// contains filtered or unexported fields
}
Publisher publishes to one declared stream or super stream. It is obtained from Declarations.DeclarePublisher or DeclareSuperStreamPublisher at declaration time and is inert until Manager.Start binds it to a client producer. Safe for concurrent use.
func (*Publisher) Closed ¶ added in v0.64.0
Closed reports whether this publisher's Close has run. A closed publisher is never ready and every Publish through it returns ErrPublisherClosed, so a caller draining a queue can tell a shutdown apart from a delivery failure without publishing to find out. Unlike Ready, this is not a broker-connectivity answer: a Close-then-Start cycle clears it (see bind).
func (*Publisher) Publish ¶
func (p *Publisher) Publish(ctx context.Context, msg *PublishMessage) error
Publish sends one message and blocks until the broker confirms it, ctx expires, or the publisher closes.
A ctx expiry is NOT proof of failure: the send may still be in flight and may still land. Delivery is at-least-once, so consumers must be idempotent. A nil return from the client's own Send proves nothing either — it swallows write errors — which is why the confirmation is what this waits for.
func (*Publisher) Ready ¶ added in v0.64.0
Ready reports whether this publisher's producer is connected to the broker, as the client's HA layer reports it: only ha.StatusOpen is ready, so a producer that is reconnecting, closed, or not bound yet is not. A true answer is a snapshot, not a delivery guarantee — a broker failure can invalidate it before the next Publish.
type PublisherOptions ¶
type PublisherOptions struct {
// Stream is the stream to publish to; it must be declared in the same Declarations.
Stream string
}
PublisherOptions declares one plain-stream publisher.
type RetryOptions ¶ added in v0.61.0
type RetryOptions struct {
MaxAttempts int
// InitialBackoff is the wait before the second attempt.
InitialBackoff time.Duration
// MaxBackoff caps the doubling. Zero means uncapped — the waits keep doubling
// for the whole bound, which is what MaxRetryWait then has to contain.
MaxBackoff time.Duration
}
RetryOptions bounds how often a failed delivery's handler is re-invoked in place before the lane settles on the failure. MaxAttempts counts the first attempt, so 1 retries nothing; the wait before attempt n (n >= 2) is InitialBackoff doubled n-2 times, capped at MaxBackoff.
type Stats ¶
type Stats struct {
// Streams counts every declared stream, super streams included.
Streams int
// SuperStreams counts how many of Streams are partitioned super streams.
SuperStreams int
// Consumers counts every declared consumer, of either kind.
Consumers int
// Publishers counts every declared publisher.
Publishers int
}
Stats summarizes a declaration store.
type StreamDeclarer ¶ added in v0.61.0
type StreamDeclarer interface {
DeclareStreams(decls *Declarations)
}
StreamDeclarer is the optional module interface the framework detects at startup. Modules that implement it have DeclareStreams called automatically. This used to live on app.StreamDeclarer; that name was removed so app does not import this package (ADR-091).
type StreamSpec ¶
type StreamSpec struct {
// MaxAge discards segments older than this duration, truncated to whole
// seconds. A non-zero sub-second value floors to 1s: second granularity is
// RabbitMQ's, so anything briefer is inexpressible and would otherwise
// discard the retention the caller asked for. Renders identically to
// messaging.StreamQueueSpec.MaxAge, so both lanes treat a given value the
// same way.
MaxAge time.Duration
// MaxLengthBytes caps the total retained size of the stream.
MaxLengthBytes int64
// MaxSegmentSizeBytes caps the size of one segment file.
MaxSegmentSizeBytes int64
}
StreamSpec configures retention for a declared stream. Zero-value fields are omitted, leaving the broker's own defaults in place; a negative value is a declaration error that Declarations.Validate reports.
type SuperStreamConsumerOptions ¶
type SuperStreamConsumerOptions struct {
// SuperStream is the super stream to consume; it must be declared in the same
// Declarations.
SuperStream string
// Name is the consumer/group name. Required: the broker stores an offset per
// partition under it, and it is the group identity partitions are distributed by.
Name string
// Start is where a partition begins when the broker holds no stored offset for
// Name on that partition.
Start OffsetStart
// Handler processes each message. Required, and called concurrently across
// partitions — see Handler.
Handler Handler
// Retry bounds in-place re-invocation of Handler after it returns an error.
// Nil is one attempt, unless Hold is set — see DefaultHoldRetry. A policy may
// ask for at most MaxRetryAttempts attempts and MaxRetryWait of total waiting,
// because the waits run inside the partition's own delivery callback; a failure
// that needs more patience than that belongs in the hold.
Retry *RetryOptions
// Hold parks a failed delivery per tenant so the tenant's later messages wait
// behind it while the rest of the partition keeps flowing.
//
// SECURITY: the tenant is read from the producer-written stamp, which is
// identification and not authorization (ADR-039's rule for HTTP resolvers,
// applied here). A producer that can publish to this stream therefore chooses
// which tenant a message is held under, and a failure it induces holds THAT
// tenant until the hold drains. Isolating producers — per-tenant credentials
// or vhosts — stays the deployment's job, and it matters more here than for a
// delivery that is merely mis-attributed.
Hold bool
// TenantOptional lets this consumer run a delivery that carries no tenant stamp
// (a control-plane consumer). Default false: fail closed. It never admits a
// stamp that is present but unusable.
TenantOptional bool
}
SuperStreamConsumerOptions declares one consumer over every partition of a super stream.
There is deliberately no SAC field: super-stream consumption is ALWAYS a single active consumer group. The client attaches every partition with one shared offset specification, so the SAC promotion callback — which the broker fires once per partition — is the only place a per-partition stored offset can be restored. Without it a restart would replay every partition from Start, which contradicts the stored-offset-wins contract the plain lane documents. A lone member is promoted on every partition, so a single-instance deployment loses nothing. See ADR-059.
type SuperStreamPublisherOptions ¶
type SuperStreamPublisherOptions struct {
// SuperStream is the super stream to publish to; it must be declared in the
// same Declarations.
SuperStream string
}
SuperStreamPublisherOptions declares one super-stream publisher, which routes each message to a partition by the murmur3 hash of its RoutingKey.