46 lines
1.4 KiB
Go
46 lines
1.4 KiB
Go
package integration
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
roomeventsv1 "hyapp.local/api/proto/events/room/v1"
|
|
)
|
|
|
|
// CompositeOutboxPublisher fans one committed room outbox event to every
|
|
// asynchronous consumer. Consumers must be idempotent by event_id because a
|
|
// partial failure retries the full fanout on the next worker pass.
|
|
type CompositeOutboxPublisher struct {
|
|
publishers []OutboxPublisher
|
|
}
|
|
|
|
// NewCompositeOutboxPublisher removes nil publishers and returns a no-op when
|
|
// no asynchronous consumers are configured.
|
|
func NewCompositeOutboxPublisher(publishers ...OutboxPublisher) OutboxPublisher {
|
|
filtered := make([]OutboxPublisher, 0, len(publishers))
|
|
for _, publisher := range publishers {
|
|
if publisher != nil {
|
|
filtered = append(filtered, publisher)
|
|
}
|
|
}
|
|
if len(filtered) == 0 {
|
|
return NewNoopOutboxPublisher()
|
|
}
|
|
return &CompositeOutboxPublisher{publishers: filtered}
|
|
}
|
|
|
|
// PublishOutboxEvent calls every configured consumer before returning. Returning
|
|
// a joined error keeps the event retryable until every consumer has accepted it.
|
|
func (p *CompositeOutboxPublisher) PublishOutboxEvent(ctx context.Context, envelope *roomeventsv1.EventEnvelope) error {
|
|
if p == nil || len(p.publishers) == 0 {
|
|
return nil
|
|
}
|
|
var joined error
|
|
for _, publisher := range p.publishers {
|
|
if err := publisher.PublishOutboxEvent(ctx, envelope); err != nil {
|
|
joined = errors.Join(joined, err)
|
|
}
|
|
}
|
|
return joined
|
|
}
|