Documentation
¶
Overview ¶
Package outbox provides transaction-time recording and relay primitives for reliable integration message publishing.
Record messages through Recorder inside the same transaction as the business write. Publish them through Relay after commit. Delivery is at-least-once, so consumers must deduplicate by the message ID.
Index ¶
- Variables
- type ClaimOptions
- type Codec
- type FixedBackoffPolicy
- type NoRetryPolicy
- type ProtoCodec
- type ProtoCodecOption
- type Record
- type Recorder
- type RecorderOption
- type Relay
- type RelayOption
- type RetryDecision
- type RetryPolicy
- type RunOptions
- type RunResult
- type Status
- type Store
- type StoreRecorder
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrNilStore = errors.New("outbox: nil store") ErrNilCodec = errors.New("outbox: nil codec") ErrNilPublisher = errors.New("outbox: nil publisher") ErrInvalidClaimOptions = errors.New("outbox: invalid claim options") ErrInvalidRunOptions = errors.New("outbox: invalid run options") ErrUnknownKind = errors.New("outbox: unknown message kind") ErrNilFactory = errors.New("outbox: nil protobuf factory") )
Functions ¶
This section is empty.
Types ¶
type ClaimOptions ¶
func NormalizeClaimOptions ¶
func NormalizeClaimOptions(opts ClaimOptions, now func() time.Time) (ClaimOptions, error)
NormalizeClaimOptions fills default claim timestamps and validates the claim window before a store attempts to lock records.
type FixedBackoffPolicy ¶
func (FixedBackoffPolicy) NextAttempt ¶
func (p FixedBackoffPolicy) NextAttempt(record Record, err error, now time.Time) RetryDecision
type NoRetryPolicy ¶
type NoRetryPolicy struct{}
func (NoRetryPolicy) NextAttempt ¶
func (NoRetryPolicy) NextAttempt(_ Record, err error, _ time.Time) RetryDecision
type ProtoCodec ¶
type ProtoCodec struct {
// contains filtered or unexported fields
}
func NewProtoCodec ¶
func NewProtoCodec(opts ...ProtoCodecOption) *ProtoCodec
NewProtoCodec creates a protobuf-backed outbox codec.
type ProtoCodecOption ¶ added in v0.9.1
type ProtoCodecOption func(*ProtoCodec)
ProtoCodecOption configures a protobuf outbox codec.
func WithPayloadResolver ¶ added in v0.9.1
func WithPayloadResolver(resolver message.PayloadResolver) ProtoCodecOption
WithPayloadResolver makes the codec use resolver when decoding records.
The default resolver is the codec's internal registry populated through Register. Supplying a shared resolver lets outbox and broker adapters reuse the same Kind-to-protobuf mapping.
type Record ¶
type RecorderOption ¶
type RecorderOption func(*recorderConfig)
type Relay ¶
type Relay struct {
// contains filtered or unexported fields
}
func (*Relay) RunOnce ¶
func (r *Relay) RunOnce(ctx context.Context, opts ClaimOptions) RunResult
Example ¶
package main
import (
"context"
"fmt"
"time"
"github.com/go-jimu/components/ddd/message"
"github.com/go-jimu/components/ddd/message/outbox"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/wrapperspb"
)
type exampleOutboxStore struct {
records []outbox.Record
published []string
}
func (s *exampleOutboxStore) Append(_ context.Context, records ...outbox.Record) error {
for _, record := range records {
s.records = append(s.records, record.Clone())
}
return nil
}
func (s *exampleOutboxStore) Claim(_ context.Context, opts outbox.ClaimOptions) ([]outbox.Record, error) {
limit := opts.Limit
if limit > len(s.records) {
limit = len(s.records)
}
claimed := make([]outbox.Record, 0, limit)
for i := 0; i < limit; i++ {
record := s.records[i].Clone()
record.Status = outbox.StatusProcessing
record.LockedUntil = opts.LockedUntil
record.ClaimedBy = opts.ClaimedBy
claimed = append(claimed, record)
}
return claimed, nil
}
func (s *exampleOutboxStore) MarkPublished(_ context.Context, records ...outbox.Record) error {
for _, record := range records {
s.published = append(s.published, record.MessageID)
}
return nil
}
func (s *exampleOutboxStore) MarkFailed(_ context.Context, record outbox.Record, reason string, nextAttemptAt time.Time) error {
return nil
}
type exampleOutboxPublisher struct {
kinds []message.Kind
}
func (p *exampleOutboxPublisher) Publish(_ context.Context, msg message.Message) error {
p.kinds = append(p.kinds, msg.Kind())
return nil
}
func main() {
codec := outbox.NewProtoCodec()
if err := codec.Register("customer.created", func() proto.Message {
return &wrapperspb.StringValue{}
}); err != nil {
panic(err)
}
msg, err := message.New(
"customer.created",
wrapperspb.String("alice"),
message.WithID("msg-1"),
message.WithKey("customer-1"),
message.WithOccurredAt(time.Unix(0, 0).UTC()),
)
if err != nil {
panic(err)
}
store := &exampleOutboxStore{}
recorder, err := outbox.NewRecorder(store, codec)
if err != nil {
panic(err)
}
if err := recorder.Record(context.Background(), msg); err != nil {
panic(err)
}
publisher := &exampleOutboxPublisher{}
relay, err := outbox.NewRelay(store, codec, publisher, outbox.WithClock(func() time.Time {
return time.Unix(10, 0).UTC()
}))
if err != nil {
panic(err)
}
result := relay.RunOnce(context.Background(), outbox.ClaimOptions{
Limit: 10,
LockedUntil: time.Unix(20, 0).UTC(),
ClaimedBy: "relay-1",
})
fmt.Println(len(store.records), result.Claimed, result.Published, publisher.kinds[0], store.published[0])
}
Output: 1 1 1 customer.created msg-1
type RelayOption ¶
type RelayOption func(*relayConfig)
func WithClock ¶
func WithClock(now func() time.Time) RelayOption
func WithRetryPolicy ¶
func WithRetryPolicy(policy RetryPolicy) RelayOption
type RetryPolicy ¶
type RetryPolicy interface {
NextAttempt(record Record, err error, now time.Time) RetryDecision
}
type RunOptions ¶
type RunOptions struct {
Claim ClaimOptions
Interval time.Duration
OnResult func(RunResult)
}
type StoreRecorder ¶
type StoreRecorder struct {
// contains filtered or unexported fields
}
func NewRecorder ¶
func NewRecorder(store Store, codec Codec, opts ...RecorderOption) (*StoreRecorder, error)