39 KiB
NATS Integration Architecture
Overview
The NATS integration provides a robust, production-ready message processing system built on Clean Architecture principles. It supports both JetStream (persistent) and Core NATS (fire-and-forget) modes with comprehensive error handling, observability, and resilience features.
Key Concepts
- Consumer: Pulls messages from NATS JetStream in batches, processes them, and handles ACKs/NAKs
- Publisher: Publishes messages to NATS with automatic deduplication via UUID headers
- Batch Processing: Fetches multiple messages at once (configurable size) for efficiency
- Error Classification: Distinguishes between transient (retry) and permanent (DLQ) errors
- Dead Letter Queue (DLQ): Routes failed messages to a separate queue for analysis
- Backpressure: Automatically slows down processing when errors accumulate
- Self-Healing: Automatically recreates missing streams/consumers in development
- Observability: Built-in metrics, tracing, and structured logging
Quick Start Flow
1. Configure NATS connection and consumer settings
2. Create Consumer with dependencies (processor, logger, telemetry)
3. Start Consumer - begins fetching and processing messages
4. Messages flow: Fetch → Process → ACK/NAK/DLQ
5. Errors handled automatically with retries and backoff
6. Graceful shutdown on context cancellation
Architecture
Clean Architecture Layers
The NATS integration follows Clean Architecture principles, separating concerns into distinct layers:
┌─────────────────────────────────────┐
│ Port Interfaces │
│ (Publisher, Consumer contracts) │
│ - Define contracts, not impl │
│ - Enable dependency inversion │
├─────────────────────────────────────┤
│ Application Layer │
│ (Message processing logic) │
│ - Business logic │
│ - Use case orchestration │
├─────────────────────────────────────┤
│ Infrastructure Layer │
│ (NATS implementation details) │
│ │
│ ┌─────────────────────────────┐ │
│ │ Consumer │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ MessageFetcher │ │ │
│ │ │ MessageProcessor │ │ │
│ │ │ ErrorHandler │ │ │
│ │ │ DLQHandler │ │ │
│ │ └─────────────────────┘ │ │
│ └─────────────────────────────┘ │
│ │
│ ┌─────────────────────────────┐ │
│ │ Publisher │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ MessageSerializer │ │ │
│ │ │ HeaderEnricher │ │ │
│ │ └─────────────────────┘ │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
Component Interaction Diagram
┌──────────────┐
│ Publisher │
│ │
│ 1. Serialize │
│ 2. Add UUID │
│ 3. Publish │
└──────┬───────┘
│
│ Publish to Subject
▼
┌─────────────────────────────────────┐
│ NATS JetStream │
│ │
│ ┌──────────────┐ │
│ │ Stream │ │
│ │ (TELEGRAM) │ │
│ └──────┬───────┘ │
│ │ │
│ ┌──────▼───────┐ │
│ │ Consumer │ │
│ │ (Pull Sub) │ │
│ └──────┬───────┘ │
└─────────┼───────────────────────────┘
│
│ Fetch Batch
▼
┌─────────────────────────────────────┐
│ Consumer │
│ │
│ ┌──────────────────────────────┐ │
│ │ MessageFetcher │ │
│ │ - FetchBatch() │ │
│ │ - HandleFetchError() │ │
│ └──────────┬───────────────────┘ │
│ │ │
│ ┌──────────▼───────────────────┐ │
│ │ MessageProcessor │ │
│ │ - ProcessBatch() │ │
│ │ - ProcessSingleMessage() │ │
│ └──────────┬───────────────────┘ │
│ │ │
│ ┌──────────▼───────────────────┐ │
│ │ ErrorHandler │ │
│ │ - Classify errors │ │
│ │ - Apply backpressure │ │
│ └──────────┬───────────────────┘ │
│ │ │
│ ┌──────────▼───────────────────┐ │
│ │ DLQHandler │ │
│ │ - RouteToDLQ() │ │
│ │ - AdvisoryDLQHandler │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────┘
│
│ ACK/NAK
▼
┌─────────────────────────────────────┐
│ Application Processor │
│ (Business Logic) │
└─────────────────────────────────────┘
Architecture Principles
- Dependency Inversion: High-level modules (Consumer, Publisher) depend on abstractions (interfaces), not concrete implementations
- Separation of Concerns: Each component has a single responsibility:
MessageFetcher: Handles message retrievalMessageProcessor: Handles message processing logicErrorHandler: Handles error classification and recoveryDLQHandler: Handles dead letter queue routing
- Testability: All components can be mocked and tested independently
- Extensibility: New implementations can be added without modifying existing code
Logic Flow
Consumer Processing Flow
The consumer follows a well-defined processing loop with error handling at each stage:
┌─────────────────────────────────────────────────────────────┐
│ Consumer Start │
│ 1. Initialize components (Fetcher, Processor, DLQ) │
│ 2. Create/validate JetStream resources │
│ 3. Start advisory DLQ handler (if enabled) │
│ 4. Start metrics collection goroutine │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Main Loop │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Step 1: Check Context │ │
│ │ - If cancelled, exit gracefully │ │
│ └──────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌──────────────────▼───────────────────────────────────┐ │
│ │ Step 2: Fetch Batch │ │
│ │ - Fetch up to BatchSize messages │ │
│ │ - Wait up to BatchTimeout │ │
│ │ - Handle fetch errors with recovery │ │
│ └──────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ │ │
│ Success Error │
│ │ │ │
│ │ ┌────────▼────────┐ │
│ │ │ Handle Error │ │
│ │ │ - Classify type │ │
│ │ │ - Apply backoff │ │
│ │ │ - Recover if dev│ │
│ │ └────────┬────────┘ │
│ │ │ │
│ │ ┌────────▼────────┐ │
│ │ │ Continue? │ │
│ │ └────────┬────────┘ │
│ │ │ │
│ │ Yes │ No │
│ │ │ │ │
│ │ └───┬────┴───┐ │
│ │ │ │ │
│ │ Continue Exit │
│ │ │ │
│ └──────────────────┘ │
│ │ │
│ ┌──────────────────▼───────────────────────────────────┐ │
│ │ Step 3: Process Batch │ │
│ │ - For each message in batch: │ │
│ │ * Check context │ │
│ │ * Extract message ID │ │
│ │ * Create tracing span │ │
│ │ * Call processor.Handle() │ │
│ │ * Handle result (ACK/NAK/DLQ) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ │ │
│ Success Error │
│ │ │ │
│ │ ┌────────▼────────┐ │
│ │ │ Classify Error │ │
│ │ └────────┬────────┘ │
│ │ │ │
│ │ ┌─────────────┴─────────────┐ │
│ │ │ │ │
│ │ Permanent Transient │
│ │ │ │ │
│ │ ┌──────▼──────┐ ┌────────▼──────┐ │
│ │ │ Route to DLQ│ │ NAK with delay│ │
│ │ │ ACK message │ │ Apply backpres│ │
│ │ └──────┬──────┘ └────────┬──────┘ │
│ │ │ │ │
│ └─────────┴───────────────────────────┘ │
│ │ │
│ ┌──────────────────▼───────────────────────────────────┐ │
│ │ Step 4: Reset Error Streak (if successful) │ │
│ └──────────────────┬───────────────────────────────────┘ │
│ │ │
│ └─────────── Loop ─────────────────────┘
└─────────────────────────────────────────────────────────────┘
Message Processing Logic
Single Message Processing Flow
Message Received
│
▼
┌─────────────────────┐
│ Extract Message ID │
│ - Check header │
│ - Fallback to meta │
│ - Generate if none │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Create Trace Span │
│ - Add attributes │
│ - Propagate context │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Process Message │
│ - Call processor │
│ - Business logic │
└──────────┬──────────┘
│
┌──────┴──────┐
│ │
Success Error
│ │
│ ┌──────▼──────────┐
│ │ Classify Error │
│ └──────┬──────────┘
│ │
│ ┌──────┴──────┐
│ │ │
│ Permanent Transient
│ │ │
│ ┌───▼───┐ ┌────▼────┐
│ │ DLQ │ │ NAK │
│ │ ACK │ │ Backoff │
│ └───┬───┘ └────┬────┘
│ │ │
└──────┴─────────────┘
│
▼
End Processing
Error Handling Logic
Error Occurred
│
▼
┌─────────────────────┐
│ Is Permanent Error? │
│ - app.IsPermanent() │
└──────┬──────────────┘
│
┌───┴───┐
│ │
Yes No
│ │
│ ┌───▼──────────────────────┐
│ │ Increment Error Streak │
│ └───┬──────────────────────┘
│ │
│ ┌───▼──────────────────────┐
│ │ Streak >= Threshold? │
│ │ (default: 10 errors) │
│ └───┬──────────────────────┘
│ │
│ ┌───┴───┐
│ │ │
│ Yes No
│ │ │
│ │ ┌───▼──────────────┐
│ │ │ NAK with delay │
│ │ │ - Use backoff │
│ │ │ - Request retry │
│ │ └──────────────────┘
│ │
│ ▼
│ ┌──────────────────────┐
│ │ Apply Backpressure │
│ │ - Sleep: errors*100ms│
│ │ - Max: 5 seconds │
│ └───┬──────────────────┘
│ │
│ ▼
│ ┌──────────────────────┐
│ │ NAK with delay │
│ └──────────────────────┘
│
▼
┌──────────────────────┐
│ Route to DLQ │
│ - Enrich metadata │
│ - Publish to DLQ │
│ - ACK original msg │
└──────────────────────┘
Publisher Logic Flow
Publish Request
│
▼
┌─────────────────────┐
│ Validate Topic │
│ - Check config │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Serialize Message │
│ - JSON marshal │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Extract/Generate ID │
│ - From message.Uuid │
│ - Or generate UUID │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Set Header │
│ - Nats-Msg-Id │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Publish to NATS │
│ - js.PublishMsg() │
└──────────┬──────────┘
│
┌──────┴──────┐
│ │
Success Error
│ │
│ ┌──────▼──────────┐
│ │ Classify Error │
│ └──────┬──────────┘
│ │
│ ┌──────┴──────┐
│ │ │
│ Transient Permanent
│ │ │
│ ┌───▼───┐ ┌────▼────┐
│ │ Retry │ │ Fail │
│ │ Later │ │ Fast │
│ └───────┘ └─────────┘
│
▼
Success
Core Components
Consumer
The consumer handles message consumption with the following features:
Modes
- JetStream Mode: Persistent, durable message processing with acknowledgments
- Core Mode: Fire-and-forget message processing for simple use cases
Key Features
- Batch Processing: Configurable batch sizes and timeouts for efficient processing
- Backpressure: Automatic backpressure when processing errors accumulate
- Dead Letter Queue (DLQ): Automatic routing of failed messages to DLQ
- Advisory DLQ: Handles messages that exceed MaxDeliver limits
- Self-Healing: Automatic recreation of missing streams/consumers in dev environments
- Graceful Shutdown: Proper cleanup and draining of connections
Component Logic
MessageFetcher (defaultMessageFetcher)
- Fetches batches of messages using
sub.Fetch(batchSize, MaxWait(timeout)) - Handles fetch errors with exponential backoff
- Recovers subscriptions when connection issues occur
- Context-aware: respects cancellation signals
MessageProcessor (defaultBatchProcessor)
- Processes messages sequentially within a batch
- Extracts message IDs (header → metadata → generated)
- Creates OpenTelemetry spans for tracing
- Calls application processor for business logic
- Handles ACK/NAK based on processing results
ErrorHandler
- Classifies errors as transient or permanent using
app.IsPermanent() - Tracks consecutive error streaks
- Applies backpressure when streak exceeds threshold (default: 10)
- Calculates backoff delays for retries
DLQHandler (defaultDLQHandler)
- Routes permanent errors to DLQ with enriched metadata
- Validates DLQ stream exists at startup
- Publishes DLQ messages with error context
AdvisoryDLQHandler
- Subscribes to JetStream advisory events:
$JS.EVENT.ADVISORY.CONSUMER.MAX_DELIVERIES.* - Handles messages that exhaust MaxDeliver attempts
- Retrieves original message from stream using
GetMsg() - Routes to DLQ with advisory metadata
Configuration
[NATS]
Mode = "jetstream" # or "core"
Stream = "TELEGRAM"
Consumer = "telegram-consumer"
[NATS.ConsumerRules]
AckWait = "30s"
MaxDeliver = 3
MaxAckPending = 1000
DeliverPolicy = "all"
ReplayPolicy = "instant"
Backoff = ["1s", "2s", "5s", "10s"]
[DLQ]
Enabled = true
Subject = "caatsm.dlq"
[App]
BatchSize = 50
BatchTimeout = "2s"
MonitorInterval = "30s"
Publisher
The publisher handles message publishing with deduplication and observability.
Features
- Message Deduplication: Automatic UUID-based deduplication headers
- JetStream/Core Mode Support: Adapts based on available JetStream context
- Structured Logging: Comprehensive logging of publish operations
- Error Classification: Distinguishes between transient and permanent errors
Component Logic
Publishing Flow
- Validation: Checks that publisher topic is configured
- Serialization: Marshals message to JSON using
json.Marshal() - ID Extraction: Extracts UUID from message (if
ParsedTelegramtype) or generates new UUID - Header Attachment: Sets
Nats-Msg-Idheader for deduplication - Publishing: Calls
js.PublishMsg()to publish to JetStream - Error Handling: Classifies errors as transient (
ErrNoResponders) or permanent
Deduplication Strategy
- Uses
Nats-Msg-Idheader for JetStream deduplication - Extracts UUID from
ParsedTelegram.Uuidfield if available - Falls back to generating new UUID if not present
- JetStream uses this header to prevent duplicate message processing
Error Classification
- Transient:
nats.ErrNoResponders- JetStream temporarily unavailable, should retry - Permanent: Other errors - configuration issues, should fail fast
Error Handling
Error Types
- Transient Errors: Network issues, temporary unavailability (retried with backoff)
- Permanent Errors: Message format issues, business logic failures (routed to DLQ)
- Resource Errors: Missing streams/consumers (auto-recovered in dev, fail in prod)
Recovery Strategies
- Exponential Backoff: Configurable backoff for transient failures
- Circuit Breaker Pattern: Prevents cascade failures
- Resource Recreation: Automatic recreation of missing JetStream resources
- Graceful Degradation: Continues processing other messages when one fails
Dead Letter Queue (DLQ)
Features
- Rich Metadata: Includes original message, error details, delivery attempts
- Stream Validation: Validates DLQ stream exists at startup
- Advisory Processing: Handles MaxDeliver exhaustion automatically
- Operational Visibility: Comprehensive logging and metrics
DLQ Message Format
{
"transport_msg_id": "uuid",
"subject": "original.subject",
"stream": "TELEGRAM",
"consumer": "telegram-consumer",
"nats_sequence": 12345,
"deliveries": 3,
"error": "processing failed: invalid format",
"received_at": "2024-01-01T12:00:00Z",
"body": "original message data"
}
Observability
Metrics
- Consumer Metrics: ack_pending, redelivered, pending, delivered counts
- Processing Metrics: batch size, processing duration, error rates
- DLQ Metrics: messages routed to DLQ, publish failures
- Connection Metrics: connection health, reconnection events
Tracing
- End-to-End Tracing: Request correlation through trace IDs
- Span Attributes: Consumer name, stream name, batch size, error details
- Context Propagation: Trace context passed through processing pipeline
Logging
- Structured Logs: JSON format with correlation IDs
- Log Levels: Debug, Info, Warn, Error with appropriate detail levels
- Operational Context: Includes consumer, stream, and message metadata
Resilience Patterns
Backpressure
- Error Accumulation: Tracks consecutive processing errors
- Adaptive Delay: Increases delay based on error frequency
- Circuit Breaking: Stops processing when errors exceed threshold
Connection Management
- Auto-Reconnection: Built-in NATS reconnection logic
- Graceful Shutdown: Proper draining with timeouts
- Resource Cleanup: Ensures subscriptions and connections are closed
Self-Healing
- Development Mode: Auto-creates missing streams/consumers
- Production Mode: Fails fast on configuration issues
- Recovery Logic: Attempts to recreate resources on errors
Configuration
Environment Variables
CAATSM_NATS_URL=nats://localhost:4222
CAATSM_NATS_MODE=jetstream
CAATSM_DLQ_ENABLED=true
CAATSM_DLQ_SUBJECT=caatsm.dlq
Runtime Configuration
- Hot Reload: Configuration changes applied without restart
- Validation: Comprehensive validation at startup
- Defaults: Sensible defaults for all configuration options
Testing Strategy
Unit Tests
- Pure Functions: Configuration normalization, policy mapping
- Mock Dependencies: NATS connections, JetStream contexts
- Table-Driven Tests: Comprehensive coverage of edge cases
Integration Tests
- Real NATS: Testcontainers with actual NATS server
- End-to-End: Complete message processing pipelines
- Failure Scenarios: Network failures, resource unavailability
Test Categories
- Happy Path: Normal operation scenarios
- Error Recovery: Various failure and recovery scenarios
- Performance: Load testing and resource usage
- Configuration: Different configuration combinations
Simple Examples
Example 1: Complete Consumer Setup and Start
This example shows how to set up and start a consumer from scratch:
package main
import (
"context"
"time"
"caatsm/internal/infra/config"
"caatsm/internal/infra/nats"
"caatsm/internal/app"
"go.uber.org/zap"
)
func main() {
// 1. Load configuration
cfg := &config.Config{
NATS: config.NATSConfig{
URL: "nats://localhost:4222",
Mode: "jetstream",
Stream: "TELEGRAM",
Consumer: "telegram-consumer",
ConsumerRules: config.ConsumerRules{
AckWait: 30 * time.Second,
MaxDeliver: 3,
Backoff: []time.Duration{1*time.Second, 2*time.Second, 5*time.Second},
},
},
App: config.AppConfig{
BatchSize: 50,
BatchTimeout: 2 * time.Second,
},
DLQ: config.DLQConfig{
Enabled: true,
Subject: "caatsm.dlq",
},
}
// 2. Create NATS connection
nc, _ := nats.Connect(cfg.NATS.URL)
defer nc.Close()
// 3. Get JetStream context
js, _ := nc.JetStream()
// 4. Create message processor (your business logic)
processor := app.NewMessageProcessor(/* dependencies */)
// 5. Create logger
logger, _ := zap.NewProduction()
// 6. Create telemetry recorder
telemetry := /* your telemetry implementation */
// 7. Create consumer
consumer, err := natsinfra.ProvideConsumer(
nc,
js,
processor,
cfg,
telemetry,
logger,
)
if err != nil {
logger.Fatal("Failed to create consumer", zap.Error(err))
}
// 8. Start consumer with context
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Handle graceful shutdown
go func() {
// Wait for interrupt signal
<-ctx.Done()
shutdownCtx, _ := context.WithTimeout(context.Background(), 5*time.Second)
consumer.Shutdown(shutdownCtx)
}()
// 9. Start consuming (blocks until context cancelled)
if err := consumer.Start(ctx); err != nil {
logger.Error("Consumer stopped", zap.Error(err))
}
}
Example 2: Publishing a Message
Simple example of publishing a message:
package main
import (
"caatsm/internal/adapter/dto"
"caatsm/internal/infra/nats"
"github.com/google/uuid"
)
func publishMessage(publisher port.Publisher) error {
// Create message with UUID
message := &dto.ParsedTelegram{
Uuid: uuid.NewString(), // Used for deduplication
Data: []byte("telegram message data"),
// ... other fields
}
// Publish - automatically handles:
// - JSON serialization
// - UUID header attachment
// - Error classification
if err := publisher.Publish(message); err != nil {
return fmt.Errorf("failed to publish: %w", err)
}
return nil
}
Example 3: Message Processing Flow
Step-by-step what happens when a message is processed:
// Step 1: Consumer fetches batch of messages
msgs, err := subscription.Fetch(50, nats.MaxWait(2*time.Second))
// Result: Up to 50 messages, or timeout after 2 seconds
// Step 2: For each message in batch
for _, msg := range msgs {
// Step 2a: Extract message ID
msgID := msg.Header.Get("Nats-Msg-Id")
if msgID == "" {
// Fallback: use JetStream sequence
meta, _ := msg.Metadata()
msgID = fmt.Sprintf("js-%d", meta.Sequence.Stream)
}
// Step 2b: Create tracing span
ctx, span := tracer.Start(ctx, "process.message")
span.SetAttributes(
attribute.String("messaging.system", "nats"),
attribute.String("messaging.destination.name", msg.Subject),
)
// Step 2c: Process message (your business logic)
err := processor.Handle(ctx, msg.Data, msgID)
// Step 2d: Handle result
if err != nil {
if app.IsPermanent(err) {
// Permanent error: route to DLQ and ACK
dlqHandler.RouteToDLQ(ctx, msg, err)
msg.Ack()
} else {
// Transient error: NAK with backoff
msg.NakWithDelay(calculateBackoff(msg))
}
} else {
// Success: ACK message
msg.Ack()
}
span.End()
}
Example 4: Error Handling Scenarios
Different error scenarios and how they're handled:
// Scenario 1: Transient Error (Network Issue)
func processMessage(msg *nats.Msg) error {
// Simulate network error
if networkDown {
return app.NewTransientError("network unavailable")
}
// Result: Message is NAK'd, will be redelivered with backoff
}
// Scenario 2: Permanent Error (Invalid Format)
func processMessage(msg *nats.Msg) error {
var data MyStruct
if err := json.Unmarshal(msg.Data, &data); err != nil {
return app.NewPermanentError("invalid JSON format")
}
// Result: Message routed to DLQ, original message ACK'd
}
// Scenario 3: Backpressure Trigger
// When 10+ consecutive errors occur:
// - Processing pauses
// - Sleep duration = min(consecutiveErrors * 100ms, 5s)
// - Prevents overwhelming the system
// Scenario 4: MaxDeliver Exhausted
// When message fails MaxDeliver times (default: 3):
// - JetStream publishes advisory event
// - AdvisoryDLQHandler catches event
// - Retrieves original message
// - Routes to DLQ with metadata
Example 5: DLQ Message Structure
What a DLQ message looks like:
{
"transport_msg_id": "550e8400-e29b-41d4-a716-446655440000",
"subject": "telegram.orders.12345",
"stream": "TELEGRAM",
"consumer": "telegram-consumer",
"nats_sequence": 12345,
"deliveries": 3,
"error": "permanent error: invalid message format",
"received_at": "2024-01-15T10:30:00Z",
"body": "{\"order_id\":123,\"invalid\":\"data\"}",
"advisory_source": false
}
Example 6: Configuration Examples
Different configuration scenarios:
# Example 1: High Throughput Configuration
[NATS]
Mode = "jetstream"
Stream = "TELEGRAM"
Consumer = "telegram-consumer"
[NATS.ConsumerRules]
AckWait = "60s"
MaxDeliver = 5
MaxAckPending = 5000
Backoff = ["1s", "2s", "5s", "10s", "30s"]
[App]
BatchSize = 100 # Larger batches
BatchTimeout = "5s" # Longer timeout
# Example 2: Low Latency Configuration
[App]
BatchSize = 10 # Smaller batches
BatchTimeout = "500ms" # Shorter timeout
# Example 3: Development Mode (Self-Healing)
[NATS]
Mode = "jetstream"
# Missing streams/consumers auto-created
# Example 4: Production Mode (Fail Fast)
[NATS]
Mode = "jetstream"
# Missing streams/consumers cause startup failure
Example 7: Observability Integration
How to monitor the consumer:
// Metrics are automatically collected:
// - ack_pending: Messages waiting for ACK
// - redelivered: Messages being redelivered
// - pending: Messages in stream
// - delivered: Total messages delivered
// Tracing spans are created for:
// - Each message processing
// - DLQ routing
// - Error handling
// Logs include:
// - Message processing events
// - Error details with context
// - DLQ routing events
// - Connection status changes
Usage Examples
Basic Consumer Setup
consumer, err := natsinfra.ProvideConsumer(
natsConn,
jetStream,
messageProcessor,
config,
telemetryRecorder,
logger,
)
if err != nil {
return err
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
return consumer.Start(ctx)
Publishing Messages
publisher, err := natsinfra.ProvidePublisher(
jetStream,
natsConn,
config,
logger,
)
if err != nil {
return err
}
err = publisher.Publish(&dto.ParsedTelegram{
Uuid: uuid.NewString(),
Data: telegramData,
})
Custom Error Handling
type CustomProcessor struct {
// implementation
}
func (p *CustomProcessor) ProcessMessage(ctx context.Context, msg *nats.Msg) error {
// Business logic here
if shouldRetry := someCondition(); shouldRetry {
return app.NewTransientError("temporary failure")
}
if isInvalid := validateMessage(msg); isInvalid {
return app.NewPermanentError("invalid message format")
}
return nil
}
Performance Considerations
Optimization Strategies
- Batch Processing: Reduces per-message overhead
- Connection Pooling: Reuses connections efficiently
- Memory Management: Proper buffer sizing and cleanup
- Concurrent Processing: Parallel message processing within batches
Monitoring Points
- Throughput: Messages processed per second
- Latency: End-to-end processing time
- Resource Usage: Memory, CPU, and network utilization
- Error Rates: Percentage of failed messages
Operational Guide
Deployment
- Configuration: Set appropriate timeouts and limits
- Resource Provisioning: Ensure sufficient NATS cluster capacity
- Monitoring Setup: Configure alerts and dashboards
- DLQ Monitoring: Set up DLQ message processing
Troubleshooting
- High Latency: Check batch sizes and processing logic
- Message Loss: Verify consumer acks and DLQ configuration
- Connection Issues: Check NATS cluster health and network connectivity
- Resource Exhaustion: Monitor memory usage and connection counts
Maintenance
- Stream Cleanup: Periodically clean up old streams
- Consumer Recreation: Recreate consumers for configuration changes
- Performance Tuning: Adjust batch sizes based on load patterns
- Version Upgrades: Test compatibility with NATS server versions
Security Considerations
Authentication
- NATS Auth: Use NATS built-in authentication mechanisms
- TLS: Enable TLS for encrypted communication
- Token Auth: Use NATS tokens for service authentication
Authorization
- Subject Permissions: Restrict publish/subscribe permissions
- Stream Access: Control access to specific streams
- DLQ Security: Secure DLQ access to prevent data leakage
Data Protection
- Message Encryption: Encrypt sensitive message data
- Audit Logging: Log all message operations for compliance
- PII Handling: Avoid logging sensitive information
Future Enhancements
Planned Features
- Consumer Groups: Horizontal scaling with multiple consumers
- Message Filtering: Subject-based and header-based filtering
- Priority Queues: High-priority message processing
- Rate Limiting: Per-consumer and per-subject rate limits
- Message Transformation: In-flight message modification
- Multi-Region: Cross-region message replication
Extensibility Points
- Custom Serializers: Pluggable message serialization
- Middleware: Request/response middleware support
- Hooks: Pre/post processing hooks
- Metrics Backends: Support for additional metrics systems
- Storage Backends: Alternative storage for DLQ messages