2025-11-19 13:07:09 +08:00
# NATS Integration
2025-11-18 11:47:35 +08:00
## Overview
2025-11-19 13:07:09 +08:00
The NATS integration provides a streamlined, production-ready message processing system focused on essential functionality. It supports JetStream persistent messaging with basic error handling, TLS security, and observability.
2025-11-18 11:47:35 +08:00
2025-11-18 14:15:58 +08:00
### Key Concepts
2025-11-19 13:07:09 +08:00
1. **Consumer** : Pulls messages from NATS JetStream in batches and processes them
2. **Publisher** : Publishes messages to NATS with deduplication
3. **Batch Processing** : Fetches multiple messages for efficiency
4. **Error Classification** : Distinguishes transient vs permanent errors
5. **Dead Letter Queue (DLQ)** : Routes permanent errors to DLQ
6. **TLS Support** : Secure connections with client certificates
7. **Basic Monitoring** : Essential metrics and logging
2025-11-18 14:15:58 +08:00
### 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
```
2025-11-18 11:47:35 +08:00
## Architecture
2025-11-19 13:07:09 +08:00
### Architecture
2025-11-18 11:47:35 +08:00
2025-11-19 13:07:09 +08:00
The NATS integration follows simplified Clean Architecture with focused components:
2025-11-18 14:15:58 +08:00
2025-11-18 11:47:35 +08:00
```
┌─────────────────────────────────────┐
│ Application Layer │
2025-11-19 13:07:09 +08:00
│ (Business logic & processing) │
2025-11-18 11:47:35 +08:00
├─────────────────────────────────────┤
│ Infrastructure Layer │
2025-11-19 13:07:09 +08:00
│ (NATS implementation) │
2025-11-18 11:47:35 +08:00
│ │
│ ┌─────────────────────────────┐ │
│ │ Consumer │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ MessageFetcher │ │ │
│ │ │ MessageProcessor │ │ │
│ │ │ DLQHandler │ │ │
│ │ └─────────────────────┘ │ │
│ └─────────────────────────────┘ │
│ │
│ ┌─────────────────────────────┐ │
│ │ Publisher │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
```
2025-11-19 13:07:09 +08:00
### Component Interaction
2025-11-18 14:15:58 +08:00
```
┌──────────────┐
│ Publisher │
│ │
│ 1. Serialize │
2025-11-19 13:07:09 +08:00
│ 2. Publish │
2025-11-18 14:15:58 +08:00
└──────┬───────┘
│
│ Publish to Subject
▼
┌─────────────────────────────────────┐
│ NATS JetStream │
│ │
│ ┌──────────────┐ │
│ │ Stream │ │
│ │ (TELEGRAM) │ │
│ └──────┬───────┘ │
│ │ │
│ ┌──────▼───────┐ │
│ │ Consumer │ │
│ │ (Pull Sub) │ │
│ └──────┬───────┘ │
└─────────┼───────────────────────────┘
│
│ Fetch Batch
▼
┌─────────────────────────────────────┐
│ Consumer │
│ │
│ ┌──────────────────────────────┐ │
│ │ MessageFetcher │ │
│ │ - FetchBatch() │ │
│ │ - HandleFetchError() │ │
│ └──────────┬───────────────────┘ │
│ │ │
│ ┌──────────▼───────────────────┐ │
│ │ MessageProcessor │ │
│ │ - ProcessBatch() │ │
2025-11-19 13:07:09 +08:00
│ │ - ProcessMessage() │ │
2025-11-18 14:15:58 +08:00
│ └──────────┬───────────────────┘ │
│ │ │
│ ┌──────────▼───────────────────┐ │
│ │ DLQHandler │ │
│ │ - RouteToDLQ() │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────┘
│
│ ACK/NAK
▼
┌─────────────────────────────────────┐
│ Application Processor │
│ (Business Logic) │
└─────────────────────────────────────┘
```
### Architecture Principles
1. **Dependency Inversion** : High-level modules (Consumer, Publisher) depend on abstractions (interfaces), not concrete implementations
2. **Separation of Concerns** : Each component has a single responsibility:
2025-11-19 13:07:09 +08:00
- `MessageFetcher` : Handles message retrieval
- `MessageProcessor` : Handles message processing logic
- `DLQHandler` : Handles dead letter queue routing
2025-11-18 14:15:58 +08:00
3. **Testability** : All components can be mocked and tested independently
4. **Extensibility** : New implementations can be added without modifying existing code
## Logic Flow
### Consumer Processing Flow
2025-11-19 13:07:09 +08:00
The consumer follows a simplified processing loop:
2025-11-18 14:15:58 +08:00
2025-11-19 13:07:09 +08:00
```
┌─────────────────────────────────────────────────────────────┐
│ Consumer Start │
│ 1. Initialize components (Fetcher, Processor, DLQ) │
│ 2. Start main processing loop │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 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 backoff │ │
│ └──────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ │ │
│ Success Error │
│ │ │ │
│ │ ┌────────▼────────┐ │
│ │ │ Apply Backoff │ │
│ │ │ Continue Loop │ │
│ │ └─────────────────┘ │
│ │ │
│ └──────────────────┬──────────────────────────────┘
│ │
│ ┌─────────────────────────▼─────────────────────────────┐ │
│ │ Step 3: Process Batch │ │
│ │ - For each message in batch: │ │
│ │ * Extract message ID │ │
│ │ * Call processor.Handle() │ │
│ │ * Handle result (ACK/NAK/DLQ) │ │
│ └──────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ │ │
│ Success Error │
│ │ │ │
│ │ ┌────────▼────────┐ │
│ │ │ Classify Error │ │
│ │ └────────┬────────┘ │
│ │ │ │
│ │ ┌─────────────┴─────────────┐ │
│ │ │ │ │
│ │ Permanent Transient │
│ │ │ │ │
│ │ ┌──────▼──────┐ ┌────────▼──────┐ │
│ │ │ Route to DLQ│ │ NAK with delay│ │
│ │ │ ACK message │ └────────────────┘ │
│ │ └──────┬──────┘ │
│ │ │ │
│ └─────────┴───────────────────────────────────────┘
│ │
│ └─────────── Loop ─────────────────────┘
└─────────────────────────────────────────────────────────────┘
2025-11-18 14:15:58 +08:00
```
┌─────────────────────────────────────────────────────────────┐
│ 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
```
2025-11-18 11:47:35 +08:00
## Core Components
### Consumer
The consumer handles message consumption with the following features:
2025-11-21 09:59:47 +08:00
The consumer uses JetStream mode exclusively for persistent, durable message processing with acknowledgments.
2025-11-18 11:47:35 +08:00
#### Key Features
2025-11-19 13:07:09 +08:00
- **Batch Processing**: Configurable batch sizes for efficient processing
- **Error Handling**: Distinguishes transient vs permanent errors
- **Dead Letter Queue (DLQ)**: Routes permanent errors to DLQ
- **TLS Support**: Secure connections with client certificates
- **Basic Monitoring**: Essential metrics collection
2025-11-18 11:47:35 +08:00
2025-11-18 14:15:58 +08:00
#### Component Logic
**MessageFetcher (`defaultMessageFetcher`)**
- Fetches batches of messages using `sub.Fetch(batchSize, MaxWait(timeout))`
2025-11-19 13:07:09 +08:00
- Handles fetch errors with simple exponential backoff
2025-11-18 14:15:58 +08:00
- Context-aware: respects cancellation signals
**MessageProcessor (`defaultBatchProcessor`)**
- Processes messages sequentially within a batch
- Extracts message IDs (header → metadata → generated)
- Calls application processor for business logic
- Handles ACK/NAK based on processing results
**DLQHandler (`defaultDLQHandler`)**
2025-11-19 13:07:09 +08:00
- Routes permanent errors to DLQ with basic metadata
2025-11-18 14:15:58 +08:00
- Validates DLQ stream exists at startup
- Publishes DLQ messages with error context
2025-11-18 11:47:35 +08:00
#### Configuration
```toml
[NATS]
2025-11-19 13:07:09 +08:00
URL = "nats://localhost:4222"
2025-11-18 11:47:35 +08:00
Stream = "TELEGRAM"
Consumer = "telegram-consumer"
2025-11-19 13:07:09 +08:00
[NATS.Auth]
Token = "your-token" # Optional token authentication
TLSEnabled = true # Enable TLS
TLSCertFile = "/path/to/client.crt" # Client certificate
TLSKeyFile = "/path/to/client.key" # Client private key
TLSCAFile = "/path/to/ca.crt" # CA certificate
2025-11-18 11:47:35 +08:00
[NATS.ConsumerRules]
AckWait = "30s"
MaxDeliver = 3
MaxAckPending = 1000
[DLQ]
Enabled = true
Subject = "caatsm.dlq"
[App]
BatchSize = 50
BatchTimeout = "2s"
```
### Publisher
The publisher handles message publishing with deduplication and observability.
#### Features
- **Message Deduplication**: Automatic UUID-based deduplication headers
2025-11-21 09:59:47 +08:00
- **JetStream Publishing**: Uses JetStream for reliable message delivery
2025-11-18 11:47:35 +08:00
- **Structured Logging**: Comprehensive logging of publish operations
- **Error Classification**: Distinguishes between transient and permanent errors
2025-11-18 14:15:58 +08:00
#### Component Logic
**Publishing Flow**
1. **Validation** : Checks that publisher topic is configured
2. **Serialization** : Marshals message to JSON using `json.Marshal()`
3. **ID Extraction** : Extracts UUID from message (if `ParsedTelegram` type) or generates new UUID
4. **Header Attachment** : Sets `Nats-Msg-Id` header for deduplication
5. **Publishing** : Calls `js.PublishMsg()` to publish to JetStream
6. **Error Handling** : Classifies errors as transient (`ErrNoResponders` ) or permanent
**Deduplication Strategy**
- Uses `Nats-Msg-Id` header for JetStream deduplication
- Extracts UUID from `ParsedTelegram.Uuid` field 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
2025-11-18 11:47:35 +08:00
### Error Handling
#### Error Types
- **Transient Errors**: Network issues, temporary unavailability (retried with backoff)
- **Permanent Errors**: Message format issues, business logic failures (routed to DLQ)
#### Recovery Strategies
2025-11-19 13:07:09 +08:00
- **Simple Backoff**: Exponential backoff for transient failures
2025-11-18 11:47:35 +08:00
- **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
```json
{
"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
2025-11-19 13:07:09 +08:00
2025-11-18 11:47:35 +08:00
## Configuration
### Environment Variables
```bash
CAATSM_NATS_URL = nats://localhost:4222
2025-11-19 13:07:09 +08:00
CAATSM_NATS_TOKEN = your-token # Optional
2025-11-18 11:47:35 +08:00
CAATSM_DLQ_ENABLED = true
CAATSM_DLQ_SUBJECT = caatsm.dlq
```
2025-11-19 13:07:09 +08:00
### TLS Configuration
For production deployments with TLS:
```toml
[ NATS . Auth ]
TLSEnabled = true
TLSCertFile = "/etc/ssl/certs/client.crt"
TLSKeyFile = "/etc/ssl/private/client.key"
TLSCAFile = "/etc/ssl/certs/ca.crt"
```
2025-11-18 11:47:35 +08:00
### Runtime Configuration
2025-11-19 13:07:09 +08:00
- **Validation**: Basic validation at startup
- **Defaults**: Sensible defaults for essential options
2025-11-18 11:47:35 +08:00
## 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
2025-11-19 13:07:09 +08:00
- **Error Handling**: Basic error scenarios
- **Configuration**: Configuration validation
2025-11-18 11:47:35 +08:00
2025-11-18 14:15:58 +08:00
## Simple Examples
### Example 1: Complete Consumer Setup and Start
This example shows how to set up and start a consumer from scratch:
```go
package main
import (
"context"
"time"
2025-11-19 13:07:09 +08:00
2025-11-18 14:15:58 +08:00
"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 {
2025-11-19 13:07:09 +08:00
URL : "nats://localhost:4222" ,
2025-11-18 14:15:58 +08:00
Stream : "TELEGRAM" ,
Consumer : "telegram-consumer" ,
2025-11-19 13:07:09 +08:00
Auth : config . NATSAuthConfig {
Token : "your-token" , // Optional
},
2025-11-18 14:15:58 +08:00
ConsumerRules : config . ConsumerRules {
2025-11-19 13:07:09 +08:00
AckWait : 30 * time . Second ,
MaxDeliver : 3 ,
MaxAckPending : 1000 ,
2025-11-18 14:15:58 +08:00
},
},
App : config . AppConfig {
BatchSize : 50 ,
BatchTimeout : 2 * time . Second ,
},
DLQ : config . DLQConfig {
Enabled : true ,
Subject : "caatsm.dlq" ,
},
}
2025-11-19 13:07:09 +08:00
2025-11-18 14:15:58 +08:00
// 2. Create NATS connection
2025-11-19 13:07:09 +08:00
nc , err := nats . ProvideNATSConn ( cfg , zap . NewNop ())
if err != nil {
panic ( err )
}
2025-11-18 14:15:58 +08:00
defer nc . Close ()
2025-11-19 13:07:09 +08:00
2025-11-18 14:15:58 +08:00
// 3. Get JetStream context
2025-11-19 14:53:58 +08:00
js , err := nats . ProvideJetStream ( nc , cfg , zap . NewNop ())
2025-11-19 13:07:09 +08:00
if err != nil {
panic ( err )
}
2025-11-18 14:15:58 +08:00
// 4. Create message processor (your business logic)
processor := app . NewMessageProcessor ( /* dependencies */ )
2025-11-19 13:07:09 +08:00
2025-11-18 14:15:58 +08:00
// 5. Create logger
2025-11-19 13:07:09 +08:00
logger := zap . NewNop ()
2025-11-18 14:15:58 +08:00
// 6. Create telemetry recorder
telemetry := /* your telemetry implementation */
2025-11-19 13:07:09 +08:00
2025-11-18 14:15:58 +08:00
// 7. Create consumer
2025-11-19 13:07:09 +08:00
consumer , err := nats . ProvideConsumer (
2025-11-18 14:15:58 +08:00
nc ,
js ,
processor ,
cfg ,
telemetry ,
logger ,
)
if err != nil {
2025-11-19 13:07:09 +08:00
panic ( err )
2025-11-18 14:15:58 +08:00
}
2025-11-19 13:07:09 +08:00
2025-11-18 14:15:58 +08:00
// 8. Start consumer with context
ctx , cancel := context . WithCancel ( context . Background ())
defer cancel ()
2025-11-19 13:07:09 +08:00
2025-11-18 14:15:58 +08:00
// 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:
```go
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:
```go
// 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:
```go
// 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):
2025-11-19 13:07:09 +08:00
// - Message is not automatically handled
// - Consider monitoring JetStream consumer info for failed deliveries
2025-11-18 14:15:58 +08:00
```
### Example 5: DLQ Message Structure
What a DLQ message looks like:
```json
{
"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
2025-11-19 13:07:09 +08:00
Basic configuration with TLS:
2025-11-18 14:15:58 +08:00
```toml
[ NATS ]
2025-11-19 13:07:09 +08:00
URL = "nats://secure.nats.server:4222"
2025-11-18 14:15:58 +08:00
Stream = "TELEGRAM"
Consumer = "telegram-consumer"
2025-11-19 13:07:09 +08:00
[ NATS . Auth ]
TLSEnabled = true
TLSCertFile = "/etc/ssl/certs/client.crt"
TLSKeyFile = "/etc/ssl/private/client.key"
TLSCAFile = "/etc/ssl/certs/ca.crt"
2025-11-18 14:15:58 +08:00
[ NATS . ConsumerRules ]
2025-11-19 13:07:09 +08:00
AckWait = "30s"
MaxDeliver = 3
MaxAckPending = 1000
[ DLQ ]
Enabled = true
Subject = "caatsm.dlq"
2025-11-18 14:15:58 +08:00
[ App ]
2025-11-19 13:07:09 +08:00
BatchSize = 50
BatchTimeout = "2s"
```
2025-11-18 14:15:58 +08:00
2025-11-19 13:07:09 +08:00
Development configuration:
2025-11-18 14:15:58 +08:00
2025-11-19 13:07:09 +08:00
```toml
2025-11-18 14:15:58 +08:00
[ NATS ]
2025-11-19 13:07:09 +08:00
URL = "nats://localhost:4222"
Stream = "TELEGRAM"
Consumer = "telegram-consumer"
2025-11-18 14:15:58 +08:00
2025-11-19 13:07:09 +08:00
[ DLQ ]
Enabled = true
Subject = "caatsm.dlq"
2025-11-18 14:15:58 +08:00
```
### Example 7: Observability Integration
How to monitor the consumer:
```go
// 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
```
2025-11-18 11:47:35 +08:00
## Usage Examples
### Basic Consumer Setup
```go
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
```go
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
```go
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
1. **Configuration** : Set appropriate timeouts and limits
2. **Resource Provisioning** : Ensure sufficient NATS cluster capacity
3. **Monitoring Setup** : Configure alerts and dashboards
4. **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
2025-11-19 13:07:09 +08:00
- **Token Auth**: Use NATS tokens for simple authentication
- **TLS**: Enable TLS with client certificates for secure communication
2025-11-18 11:47:35 +08:00
2025-11-19 13:07:09 +08:00
### TLS Configuration
```toml
[ NATS . Auth ]
TLSEnabled = true
TLSCertFile = "/path/to/client.crt"
TLSKeyFile = "/path/to/client.key"
TLSCAFile = "/path/to/ca.crt"
```
2025-11-18 11:47:35 +08:00
### Data Protection
2025-11-19 13:07:09 +08:00
- **TLS Encryption**: All communication is encrypted
- **Basic Logging**: Avoid logging sensitive message content
2025-11-18 11:47:35 +08:00
## Future Enhancements
2025-11-19 13:07:09 +08:00
### Future Enhancements
- **Additional Auth Methods**: Support for more authentication mechanisms if needed
- **Advanced Monitoring**: Enhanced metrics and tracing if required
- **Performance Tuning**: Batch size and timeout optimizations