Files
go-caatsm/internal/infra/nats/stream_manager.go
T

97 lines
2.6 KiB
Go

package nats
import (
"errors"
"fmt"
"github.com/nats-io/nats.go"
"go.uber.org/zap"
)
// StreamManager handles JetStream stream lifecycle management
type StreamManager struct {
js nats.JetStreamContext
streamName string
subjects []string
logger *zap.Logger
}
// NewStreamManager creates a new stream manager
func NewStreamManager(js nats.JetStreamContext, streamName string, subjects []string, logger *zap.Logger) *StreamManager {
return &StreamManager{
js: js,
streamName: streamName,
subjects: subjects,
logger: logger,
}
}
// EnsureStream ensures that the configured JetStream stream exists
func (sm *StreamManager) EnsureStream() error {
streamConfig := &nats.StreamConfig{
Name: sm.streamName,
Subjects: sm.subjects,
Retention: nats.LimitsPolicy,
Storage: nats.FileStorage,
}
info, err := sm.js.StreamInfo(sm.streamName)
if err != nil {
if errors.Is(err, nats.ErrStreamNotFound) {
if shouldBootstrapStream() {
if _, err = sm.js.AddStream(streamConfig); err != nil {
sm.logger.Error("failed to create stream",
zap.String("stream", sm.streamName),
zap.Strings("subjects", sm.subjects),
zap.Error(err),
)
return fmt.Errorf("failed to create stream %s: %w", sm.streamName, err)
}
sm.logger.Info("Created JetStream stream",
zap.String("stream", sm.streamName),
zap.Strings("subjects", sm.subjects),
)
return nil
}
sm.logger.Error("stream not found and auto-creation disabled",
zap.String("stream", sm.streamName),
zap.Strings("expected_subjects", sm.subjects),
)
return fmt.Errorf("stream %s not found and auto-creation disabled", sm.streamName)
}
sm.logger.Error("failed to fetch stream info",
zap.String("stream", sm.streamName),
zap.Error(err),
)
return fmt.Errorf("failed to fetch stream info for %s: %w", sm.streamName, err)
}
// Stream exists: validate subjects but do not fail hard if they differ.
sm.validateStreamConfig(info)
return nil
}
// validateStreamConfig validates the stream configuration
func (sm *StreamManager) validateStreamConfig(info *nats.StreamInfo) {
if info == nil {
return
}
missing := make([]string, 0)
for _, subj := range sm.subjects {
if subj == "" {
continue
}
if !containsSubject(info.Config.Subjects, subj) {
missing = append(missing, subj)
}
}
if len(missing) > 0 {
sm.logger.Warn("JetStream stream subjects missing expected entries",
zap.String("stream", info.Config.Name),
zap.Strings("stream_subjects", info.Config.Subjects),
zap.Strings("missing_subjects", missing),
)
}
}