81 lines
2.7 KiB
Go
81 lines
2.7 KiB
Go
package nats
|
|
|
|
import (
|
|
"caatsm/internal/app"
|
|
obsmetrics "caatsm/internal/infra/metrics"
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// handleMessageError handles errors that occur during message processing.
|
|
//
|
|
//nolint:unused // Reserved for potential future use or alternative implementation
|
|
func (c *Consumer) handleMessageError(ctx context.Context, msg *nats.Msg, err error, elapsed time.Duration) {
|
|
c.logger.Error("Failed to process message",
|
|
zap.String("subject", msg.Subject),
|
|
zap.Error(err),
|
|
zap.Bool("permanent", app.IsPermanent(err)),
|
|
)
|
|
|
|
result := obsmetrics.ResultFail
|
|
if app.IsPermanent(err) {
|
|
result = obsmetrics.ResultPermanentFail
|
|
}
|
|
c.telemetry.RecordMessageHandled(ctx, c.config.streamName, c.config.consumerName, result, elapsed)
|
|
|
|
processingResult := c.errorHandler.HandleProcessingError(c.consecutiveProcessErrors, err, c.logger, msg.Subject)
|
|
|
|
if processingResult.IsPermanent {
|
|
c.handlePermanentError(ctx, msg, err)
|
|
return
|
|
}
|
|
|
|
c.handleTransientError(ctx, msg, processingResult)
|
|
}
|
|
|
|
// handlePermanentError handles permanent/poison messages.
|
|
//
|
|
//nolint:unused // Reserved for potential future use or alternative implementation
|
|
func (c *Consumer) handlePermanentError(ctx context.Context, msg *nats.Msg, err error) {
|
|
c.consecutiveProcessErrors = 0
|
|
// Poison/permanent message: route to DLQ if configured, then ACK
|
|
if dlqErr := c.routeToDLQ(ctx, msg, err); dlqErr != nil {
|
|
c.logger.Error("Failed to route permanent-error message to DLQ", zap.Error(dlqErr))
|
|
}
|
|
if ackErr := msg.Ack(); ackErr != nil {
|
|
c.logger.Error("Failed to ACK permanent-error message", zap.Error(ackErr))
|
|
}
|
|
}
|
|
|
|
// handleTransientError handles transient errors with backpressure and redelivery.
|
|
//
|
|
//nolint:unused // Reserved for potential future use or alternative implementation
|
|
func (c *Consumer) handleTransientError(ctx context.Context, msg *nats.Msg, processingResult ProcessingErrorResult) {
|
|
// Increment error streak
|
|
if c.consecutiveProcessErrors < 0 {
|
|
c.consecutiveProcessErrors = 0
|
|
}
|
|
c.consecutiveProcessErrors++
|
|
|
|
if processingResult.ShouldApplyBackpressure {
|
|
c.logger.Warn("Applying backpressure due to consecutive processing errors",
|
|
zap.Int("consecutive_errors", c.consecutiveProcessErrors),
|
|
zap.Duration("sleep", processingResult.BackpressureDelay),
|
|
)
|
|
// Use context-aware sleep instead of blocking time.Sleep
|
|
if !sleepWithContext(ctx, processingResult.BackpressureDelay) {
|
|
// Context canceled, stop processing
|
|
return
|
|
}
|
|
}
|
|
|
|
// Transient error: request redelivery with optional delay
|
|
c.telemetry.RecordRetry(ctx, c.config.streamName, c.config.consumerName, obsmetrics.RetryReasonProcessorError)
|
|
if nakErr := c.nakWithStrategy(msg); nakErr != nil {
|
|
c.logger.Error("Failed to NAK message", zap.Error(nakErr))
|
|
}
|
|
}
|