2025-11-14 08:42:26 +08:00
# go-caatsm
Civil Aviation Authority Telegram Message Processor
A high-performance, production-ready message processing system for aviation telegrams using Clean Architecture, NATS JetStream, and PostgreSQL.
## Architecture
This project follows Clean Architecture principles with clear separation of concerns:
```
/cmd/main/main.go # Application entry point
/internal
/app # Application layer (business logic orchestration)
processor.go # Message processor
/domain # Domain models (pure Go types, no external dependencies)
aviation.go # ParsedMessage and related types
/adapter # Adapter layer (interfaces and implementations)
/parser # Message parsing adapters
/mapper # Data mapping (domain ↔ infrastructure)
repository.go # Repository interface
publisher.go # Publisher interface
/infra # Infrastructure layer
/config # Configuration management (Koanf)
/nats # NATS JetStream client
/postgres # PostgreSQL repository (pgx)
/log # Logging (Zap)
/pkg/di # Dependency injection (Wire)
```
## Features
- **Clean Architecture**: Clear separation between domain, application, and infrastructure layers
- **NATS JetStream**: Reliable message streaming with automatic retries and dead-letter queues
2025-11-15 09:46:29 +08:00
- **TimescaleDB (PostgreSQL)**: High-performance persistence using pgx with batch operations and hypertables
2025-11-14 08:42:26 +08:00
- **Dependency Injection**: Google Wire for compile-time dependency injection
- **Configuration Management**: Koanf for flexible configuration loading (file + environment variables)
- **Structured Logging**: Zap logger with configurable levels and formats
- **Batch Processing**: Efficient batch message processing and database inserts
## Prerequisites
- Go 1.22+
- PostgreSQL 12+
- NATS Server with JetStream enabled
## Installation
1. Clone the repository:
```bash
git clone <repository-url>
cd go-caatsm
```
2. Install dependencies:
```bash
go mod download
```
3. Set up PostgreSQL database:
```bash
psql -U postgres -f internal/repository/telegrams.ddl
```
4. Configure the application:
- Copy `configs/config.dev.toml` and modify as needed
- Or set environment variables with `CAATSM_` prefix
## Configuration
Configuration is loaded from TOML files and environment variables. The configuration file should be located at `configs/config.{env}.toml` where `{env}` is determined by the `GO_ENV` environment variable (defaults to `dev` ).
2025-11-14 23:18:10 +08:00
- `nats.url` and `nats.stream` are required (the latter defaults to `TELEGRAM` when omitted).
- `subscription.topic` is optional; when not provided the application subscribes to `telegram.>` .
2025-11-14 08:42:26 +08:00
### Configuration Structure
```toml
[ nats ]
url = "nats://localhost:4222"
stream = "TELEGRAM"
consumer = "telegram-consumer"
2025-11-15 09:01:24 +08:00
client = "serial-client"
cluster = "tele-cluster"
2025-11-14 08:42:26 +08:00
2025-11-14 21:42:04 +08:00
[ nats . stream_limits ]
max_msgs = 100000
max_bytes = 67108864
max_age = "24h"
discard = "old"
storage = "file"
replicas = 1
2025-11-15 09:46:29 +08:00
[ nats . consumer_rules ]
2025-11-14 21:42:04 +08:00
max_deliver = 5
ack_wait = "30s"
max_ack_pending = 1024
2025-11-15 09:01:24 +08:00
deliver_policy = "all" # all,new,last,last_per_subject,sequence,time
replay_policy = "instant" # instant or original
backoff = [ "5s" , "30s" , "2m" ] # optional JetStream redelivery delays
start_sequence = 0
start_time = ""
2025-11-14 21:42:04 +08:00
2025-11-14 08:42:26 +08:00
[ subscription ]
2025-11-14 23:18:10 +08:00
# Optional. Defaults to "telegram.>" when omitted.
2025-11-14 21:42:04 +08:00
topic = "telegram.serial"
2025-11-15 09:01:24 +08:00
queue_group = "tele-queue"
2025-11-14 08:42:26 +08:00
[ publisher ]
2025-11-14 21:42:04 +08:00
topic = "telegram.json"
2025-11-14 08:42:26 +08:00
[ postgres ]
url = "postgres://user:password@localhost:5432/aviation?sslmode=disable"
max_conns = 10
min_conns = 2
[ app ]
batch_size = 50
batch_timeout = "2s"
2025-11-14 21:42:04 +08:00
monitor_interval = "30s"
2025-11-14 08:42:26 +08:00
[ log ]
level = "info"
format = "json"
2025-11-14 23:18:10 +08:00
2025-11-15 09:01:24 +08:00
[ telemetry ]
enabled = false
endpoint = "http://otel-collector:4318"
insecure = true
2025-11-14 23:18:10 +08:00
### Timeouts and Ack Wait
` [ timeouts ] ` is optional , but if you plan to tune JetStream redelivery you should set ` timeouts . ack_wait ` and / or ` [ nats . consumer ]. ack_wait ` . When neither is specified the application defaults both values to ` 30 s ` , ensuring predictable redelivery timing .
2025-11-14 08:42:26 +08:00
```
### Environment Variables
You can override any configuration value using environment variables with the `CAATSM_` prefix:
```bash
export CAATSM_NATS_URL = "nats://nats-server:4222"
export CAATSM_POSTGRES_URL = "postgres://user:pass@db:5432/aviation"
export CAATSM_LOG_LEVEL = "debug"
```
Environment variable names are converted from `CAATSM_NATS_URL` to `nats.url` in the configuration.
## Usage
### Build
2025-11-15 16:30:29 +08:00
Using Make (writes `bin/receiver` ):
2025-11-14 08:42:26 +08:00
```bash
2025-11-15 16:30:29 +08:00
make build
2025-11-14 08:42:26 +08:00
```
2025-11-15 16:30:29 +08:00
Using Task:
2025-11-14 08:42:26 +08:00
```bash
task build
```
2025-11-15 16:30:29 +08:00
Or directly with Go:
2025-11-14 08:42:26 +08:00
```bash
2025-11-15 16:30:29 +08:00
go build -o bin/receiver ./cmd/main
2025-11-14 08:42:26 +08:00
```
2025-11-15 16:30:29 +08:00
### Run
Use Make targets (binary mode):
```bash
make run-dev # GO_ENV=dev
make run-prod # GO_ENV=prod
make run-test # GO_ENV=test
make run-local # go run ./cmd/main listen (honors GO_ENV)
```
Task equivalents:
2025-11-14 08:42:26 +08:00
```bash
task run-dev
2025-11-15 16:30:29 +08:00
task run-prod
task run-test
task run-local # go run ./cmd/main listen
task dev-run # boots docker-compose dev stack + go run
2025-11-14 08:42:26 +08:00
```
### Command Line Options
```bash
./bin/receiver listen --help
Flags:
2025-11-15 09:01:24 +08:00
-n, --nats-url string NATS server address
-t, --subject string NATS subject to listen to
--stream string JetStream stream name
--consumer string JetStream durable consumer
--publisher-topic string Subject used by the publisher
--postgres-url string PostgreSQL connection URL
--log-level string Logger level ( debug| info| warn| error)
--replay-from string Deliver policy override ( all| new| last| seq:<n>| time:<RFC3339>)
--ack-wait duration Ack wait override ( e.g. 45s)
--telemetry-enabled Enable OpenTelemetry exporters
--telemetry-endpoint string
OTLP collector endpoint
--telemetry-insecure Send OTLP traffic without TLS
2025-11-14 08:42:26 +08:00
```
2025-11-15 09:01:24 +08:00
Critical overrides stay available through CLI flags; advanced tuning such as stream retention, consumer backoff, and copy counts are configured via the TOML file or `CAATSM_` environment variables.
| CLI flag | Config key | Purpose |
|---------------------|------------------------|----------------------------------------|
| `--nats-url` | `nats.url` | Point to a different NATS cluster |
| `--subject` | `subscription.topic` | Change the subscribed subject filter |
| `--stream` | `nats.stream` | Bind to another JetStream stream |
| `--consumer` | `nats.consumer` | Override the durable consumer name |
| `--publisher-topic` | `publisher.topic` | Publish parsed output to a new subject |
| `--postgres-url` | `postgres.url` | Redirect persistence to another DB |
| `--log-level` | `log.level` | Adjust runtime logging verbosity |
| `--telemetry-*` | `telemetry.*` | Toggle tracing/metrics exporters |
#### Replay & Backoff
- `--replay-from seq:12345` replays from a specific JetStream sequence, while `--replay-from time:2024-11-15T08:00:00Z` starts at a timestamp.
- Configure server-side retry delays with `[nats.consumer].backoff = ["5s", "30s", "2m"]` ; each duration becomes the delay before the next delivery attempt.
- Combine `backoff` with `--ack-wait` to increase acknowledgement windows (e.g., `--ack-wait 2m` ).
### Telemetry
- Enable tracing/metrics via `[telemetry] enabled = true` and set `endpoint` to your OTLP/HTTP collector (e.g., `http://otel-collector:4318` ).
- CLI overrides:
- `--telemetry-enabled` flips the feature on/off.
- `--telemetry-endpoint` and `--telemetry-insecure` adjust the OTLP HTTP endpoint and TLS behavior.
2025-11-15 15:27:53 +08:00
- When enabled the app emits OpenTelemetry traces (parser/repository/publisher spans) and metrics. Custom OTLP metrics include:
- `caatsm_messages_processed_total` (counter, broken down by `message_status` / `message_category` )
- `caatsm_publish_failures_total` (counter)
- `caatsm_parse_duration_ms` (histogram)
These flow through the collector → Prometheus → Grafana dashboards in the dev stack.
2025-11-15 09:01:24 +08:00
2025-11-15 16:30:29 +08:00
### Observability & Health
A lightweight monitoring server exposes both readiness information and Prometheus-friendly metrics:
- `GET /healthz` probes PostgreSQL (connection ping) and NATS (connection status). It returns HTTP 200 when both dependencies respond within `monitoring.health_timeout` , otherwise 503.
- `GET /metrics` streams `caatsm_processed_total` , `caatsm_failures_total` , and `caatsm_parse_latency_seconds` counters/histograms from the built-in Prometheus registry.
- Configure the server via the `[monitoring]` block (defaults shown):
```toml
[ monitoring ]
addr = ":2112"
enable_metrics = true
enable_health = true
read_timeout = "5s"
write_timeout = "5s"
health_timeout = "2s"
```
Set `monitoring.disabled = true` (or `addr = ""` ) if you need to turn the HTTP server off, e.g., during certain integration tests.
2025-11-14 08:42:26 +08:00
## Development
2025-11-15 12:08:26 +08:00
See `docs/dev-guide.md` for the full development workflow, including Docker Compose instructions, observability tooling, and troubleshooting tips.
2025-11-15 09:46:29 +08:00
2025-11-15 12:08:26 +08:00
Quick start:
2025-11-15 09:46:29 +08:00
```bash
2025-11-15 12:08:26 +08:00
# Start database + messaging
docker compose -f docker-compose.dev.yml up -d postgres nats nats-box
# Start observability stack (optional)
docker compose -f docker-compose.dev.yml up -d otel-collector jaeger prometheus grafana
2025-11-15 09:46:29 +08:00
```
2025-11-15 16:30:29 +08:00
Run the processor locally while the infra runs in Docker (default mode is JetStream; switch to core only if you explicitly set `CAATSM_NATS_MODE=core` ):
2025-11-15 09:46:29 +08:00
```bash
2025-11-15 12:08:26 +08:00
GO_ENV = dev \
CAATSM_NATS_MODE = core \
CAATSM_POSTGRES_URL = postgres://caatsm:caatsm@localhost:5432/aviation?sslmode= disable \
go run ./cmd/main listen
2025-11-15 09:46:29 +08:00
```
2025-11-15 12:08:26 +08:00
Tear everything down with `docker compose -f docker-compose.dev.yml down -v` .
2025-11-15 09:46:29 +08:00
2025-11-15 16:30:29 +08:00
## Deployment Examples
- `docs/deploy-systemd.md` shows a minimal systemd unit that wires configuration via environment files and restarts on failure.
- `docs/deploy-k8s.md` provides a reference Deployment + ConfigMap/Secret with liveness/readiness probes hitting `/healthz` and `/metrics` .
2025-11-14 08:42:26 +08:00
### Project Structure
- **Domain Layer** (`internal/domain` ): Pure business logic and domain models
- **Application Layer** (`internal/app` ): Orchestrates business flows
- **Adapter Layer** (`internal/adapter` ): Interfaces and adapters between layers
- **Infrastructure Layer** (`internal/infra` ): External concerns (NATS, PostgreSQL, config, logging)
### Adding New Features
1. **Domain Changes** : Add to `internal/domain` (no external dependencies)
2. **Business Logic** : Add to `internal/app`
3. **External Integrations** : Add to `internal/infra`
4. **Adapters** : Add to `internal/adapter` to bridge between layers
### Dependency Injection
Dependencies are managed using Google Wire. To add a new dependency:
1. Create a provider function in the appropriate package
2. Add it to `pkg/di/wire.go`
3. Run `wire ./pkg/di` to regenerate `wire_gen.go`
2025-11-15 09:01:24 +08:00
> If the `wire` binary is missing, install it with `go install github.com/google/wire/cmd/wire@v0.7.0` and ensure `$GOPATH/bin` is on your `PATH` (or run it directly via the absolute path).
2025-11-14 08:42:26 +08:00
### Testing
2025-11-15 16:30:29 +08:00
The project keeps tests close to the code that they exercise:
2025-11-14 08:42:26 +08:00
2025-11-16 10:18:40 +08:00
- **Domain/adapter/app unit tests** live under `internal/**` and cover parsing, validation, orchestration, and adapters. Run them all with `task test` (or `make test` ), which now uses the Ginkgo CLI to run unit test suites in verbose mode (`ginkgo -r -v ./cmd ./internal` ).
2025-11-15 16:30:29 +08:00
- **Integration tests** under `test/integration` spin up disposable TimescaleDB and NATS JetStream instances (via `testcontainers-go` ) and execute a full ingestion flow. Use `task test-int` after ensuring Docker is running.
- **Coverage goals** are tracked via `task coverage` , which produces both a coverage profile and an HTML report under `coverage/coverage.html` .
2025-11-15 09:01:24 +08:00
2025-11-16 09:58:49 +08:00
| Purpose | Make command | Task command |
|----------------------------|---------------------|---------------------|
| Run unit tests (Ginkgo) | `make test` | `task test` |
| Run integration tests | `make test-int` | `task test-int` |
| Run unit+integration tests | `make test-all` | `task test-all` |
| Generate coverage html | `make coverage` | `task coverage` |
| Lint (golangci-lint) | `make lint` | `task lint` |
2025-11-15 16:30:29 +08:00
2025-11-16 10:18:40 +08:00
> Integration tests need Docker available on the host. Ginkgo-based unit tests or lint targets require the respective binaries (`go install github.com/onsi/ginkgo/v2/ginkgo@latest`, [golangci-lint install guide](https://golangci-lint.run/)). Use `task install-test` to bootstrap Ginkgo tooling before running `task test`, `task test-all`, or their `make` equivalents. `make test` / `task test` run Ginkgo in verbose mode (`-v`) over `./cmd` and `./internal`, showing each spec for easier debugging.
2025-11-14 08:42:26 +08:00
## Message Flow
2025-11-15 16:30:29 +08:00
1. **NATS Consumer** receives raw telegram messages from NATS (JetStream durable pull by default; plain `nc.Subscribe` only when you opt into `nats.mode=core` )
2025-11-14 08:42:26 +08:00
2. **MessageProcessor** orchestrates the processing:
- Parses the message using the Parser adapter
- Stores the parsed message in PostgreSQL via Repository
- Publishes the parsed message to the output topic via Publisher
3. **ACK/NAK** is sent based on processing success/failure
4. **Retry Logic** handles transient failures automatically
2025-11-15 16:30:29 +08:00
### Error Handling & Retries
- **Parser failures** (invalid headers/body) are treated as permanent: the raw payload is stored in `aviation.telegrams_raw` , the message is ACKed, and no JetStream retries are attempted.
- **Repository failures** are transient: the consumer returns an error, the message is `NAK` ed, and JetStream redelivers it using `[nats.consumer_rules.backoff]` and `ack_wait` to space retries.
- **Publisher failures** are logged and persisted as raw records, but they are marked permanent to avoid hammering downstream topics; the deduplicated output can be replayed from the raw table later.
- Tune JetStream retry behavior via `[nats.consumer_rules.max_deliver]` , `[nats.consumer_rules.backoff]` , and CLI overrides like `--ack-wait` . The monitoring server plus Prometheus counters provide visibility into each failure bucket.
2025-11-15 09:01:24 +08:00
### Failure Buckets
Messages that cannot be parsed or fail to publish are written to `aviation.telegrams_raw` with a status:
| Status | Description |
|-----------------------|--------------------------------------------------|
| `parsed` | Successfully parsed and stored |
| `header_error` | Header invalid (missing start indicator, etc.) |
| `body_error` | Body pattern did not match any known format |
| `publish_error` | Downstream publisher returned an error |
Each entry stores the raw payload, received timestamp, and metadata to aid replay or manual inspection.
2025-11-14 08:42:26 +08:00
## Message Parsing
The system supports parsing of aviation telegram messages in the standard ICAO format. All messages follow a common header structure, followed by a message body that varies by message type.
### Message Format
All telegrams follow this general structure:
```
ZCZC <MessageID> <DateTime>
<PriorityIndicator> <PrimaryAddress>
<SecondaryAddresses>
<Originator>
<Body>
NNNN
```
**Header Fields:**
- `ZCZC` : Start indicator
- `MessageID` : Unique message identifier (e.g., "TMQ1324")
- `DateTime` : Message date and time (e.g., "150631")
- `PriorityIndicator` : Message priority (e.g., "FF", "DD")
- `PrimaryAddress` : Primary recipient address (ICAO code)
- `SecondaryAddresses` : Additional recipient addresses
- `Originator` : Message originator (optional)
### Supported Message Types
The parser supports the following message categories:
#### 1. ARR - Arrival Message
Arrival messages report aircraft arrival information.
**Format:**
```
(ARR-<FlightNumber>[/<SSR>]-<DepartureAirport>-<ArrivalAirport><ArrivalTime>)
```
**Parsed Fields:**
- `category` : "ARR"
- `aircraft_id` : Aircraft identification/flight number
- `ssr_mode_and_code` : SSR mode and code (optional)
- `departure_airport` : Departure airport ICAO code
- `departure_time` : Departure time
- `arrival_airport` : Arrival airport ICAO code
- `arrival_time` : Arrival time
- `estimated_elapsed_time` : Estimated flight duration (optional)
- `alternate_airport` : Alternate airport (optional)
- `other_info` : Additional information (optional)
**Example:**
```
ZCZC ARR1234 150631
FF ZBTJZPZX
150630 ZBACZQZX
(ARR-CCA1234-A1234-ZBTJ1500-ZGGG0135)
NNNN
```
#### 2. DEP - Departure Message
Departure messages report aircraft departure information.
**Format:**
```
(DEP-<FlightNumber>[/<SSR>]-<DepartureAirport><DepartureTime>-<Destination>)
```
**Parsed Fields:**
- `category` : "DEP"
- `aircraft_id` : Aircraft identification/flight number
- `ssr_mode_and_code` : SSR mode and code (optional)
- `departure_airport` : Departure airport ICAO code
- `departure_time` : Departure time
- `destination` : Destination airport ICAO code
- `estimated_elapsed_time` : Estimated flight duration
- `alternate_airport` : Alternate airport (optional)
- `other_info` : Additional information (optional)
**Example:**
```
ZCZC DEP5678 120915
DD KLAXZPZX
120914 KSFOZQZX
(DEP-ABC5678-A1234-ZBTJ1440-ZGGG)
NNNN
```
#### 3. CNL - Cancellation Message
Cancellation messages indicate flight cancellations.
**Format:**
```
(CNL-<FlightNumber>-<DepartureAirport>-<DestinationAirport>)
```
**Parsed Fields:**
- `category` : "CNL"
- `aircraft_id` : Aircraft identification/flight number
- `departure_airport` : Departure airport ICAO code
- `destination_airport` : Destination airport ICAO code
- `other_info` : Additional information (optional)
**Example:**
```
ZCZC CNL9012 150631
FF ZBTJZPZX
(CNL-CCA9012-ZBTJ-ZGGG)
NNNN
```
#### 4. DLA - Delay Message
Delay messages report flight delays with new departure times.
**Format:**
```
(DLA-<FlightNumber>[/<SSR>]-<DepartureAirport>[<NewDepartureTime>]-<ArrivalAirport>[<ArrivalTime>])
```
**Parsed Fields:**
- `category` : "DLA"
- `aircraft_id` : Aircraft identification/flight number
- `ssr_mode_and_code` : SSR mode and code (optional)
- `departure_airport` : Departure airport ICAO code
- `new_departure_time` : New departure time (optional)
- `arrival_airport` : Arrival airport ICAO code
- `arrival_time` : Estimated arrival time (optional)
- `other_info` : Additional information (optional)
**Example:**
```
ZCZC DLA3456 150631
FF ZBTJZPZX
(DLA-CCA3456-A1234-ZBTJ1600-ZGGG0200)
NNNN
```
#### 5. FPL - Flight Plan Message
Flight plan messages contain detailed flight planning information.
**Format:**
```
(FPL-<FlightNumber>-<Indicator>
-<AircraftID>/<SSR>
-<DepartureAirport><DepartureTime>
-<Speed><Level> <Route>
-<Destination><EstimatedTime> <AlternateAirport>
-<OtherInfo>)
```
**Parsed Fields:**
- `category` : "FPL"
- `flight_number` : Flight number
- `reference_data` : Reference data (optional)
- `aircraft_id` : Aircraft identification
- `ssr_mode_and_code` : SSR mode and code
- `flight_rules_and_type` : Flight rules and type
- `cruising_speed_and_level` : Cruising speed and flight level
- `departure_airport` : Departure airport ICAO code
- `departure_time` : Departure time
- `route` : Flight route
- `destination_and_total_time` : Destination and estimated total time
- `alternate_airport` : Alternate airport (optional)
- `estimated_arrival_time` : Estimated arrival time
- `pbn` : Performance-based navigation equipment
- `navigation_equipment` : Navigation equipment
- `estimated_elapsed_time` : Estimated elapsed time
- `selcal_code` : SELCAL code
- `register` : Aircraft registration (optional)
- `performance_category` : Performance category
- `reroute_information` : Reroute information (optional)
- `remarks` : Remarks (optional)
**Example:**
```
ZCZC FPL7890 150631
FF ZBTJZPZX
(FPL-JAE7433-IS
-B744/H-SXIRPZJWY/S
-ZBTJ1755
-K0926S0920 CG A326 VYK W80 HUR B339 GM A575 MANSA/K0919S0980
-EDDF0948 EDDK
-EET/ZMUB0100 UNKL0236
REG/B2422 SEL/JLAD
NAV/RNAV1 RNAV5 RNP4
RMK/AGCS EQUIPPED)
NNNN
```
### Parsing Process
1. **Header Parsing** : The parser extracts header information including message ID, date/time, priority, and addresses
2. **Category Detection** : The parser identifies the message category from the body content
3. **Body Parsing** : Based on the category, the parser applies the appropriate regex pattern to extract structured data
4. **Data Mapping** : Extracted data is mapped to domain model structures (ARR, DEP, CNL, DLA, or FPL)
5. **Storage** : The parsed message is stored in PostgreSQL with:
- Raw content in `content` field
- Parsed structured data in `body_data` field (JSONB)
- Metadata in dedicated columns
### Parsed Message Structure
All parsed messages are stored in the `ParsedMessage` domain model:
```go
type ParsedMessage struct {
Uuid string // Unique identifier
MessageID string // Telegram message ID
DateTime string // Message date/time
PriorityIndicator string // Priority level
PrimaryAddress string // Primary recipient
SecondaryAddresses string // Secondary recipients
Originator string // Message originator
OriginatorDateTime string // Originator date/time
Category string // Message category (ARR, DEP, CNL, DLA, FPL)
Content string // Raw message content
BodyData interface {} // Parsed body data (ARR, DEP, CNL, DLA, or FPL struct)
ReceivedAt time . Time // Reception timestamp
ParsedAt time . Time // Parsing timestamp
DispatchedAt time . Time // Dispatch timestamp
NeedDispatch bool // Dispatch flag
Parsed bool // Parsing success flag
Comments string // Parsing comments/errors
}
```
### Error Handling
- **Invalid Format**: Messages that don't match expected formats are stored with `Parsed = false` and error details in `Comments`
- **Partial Parsing**: Header parsing failures result in storing raw content only
- **Category Mismatch**: Unsupported categories are logged and stored with parsing errors
## Database Schema
The application uses the `aviation.telegrams` table. See `internal/repository/telegrams.ddl` for the schema definition.
Key fields:
- `uuid` : Primary key (UUID)
- `message_id` : Telegram message ID
- `content` : Raw message content
- `body_data` : Parsed body data (JSONB)
- `received_at` , `parsed_at` , `dispatched_at` : Timestamps
## Logging
Logging uses Zap with structured logging. Log levels and format can be configured:
- **Levels**: `debug` , `info` , `warn` , `error`
- **Formats**: `json` (production) or `console` (development)
Logs include contextual information:
- Message IDs
- Subject names
- Stream names
- Processing attempts
## Performance
- **Batch Processing**: Messages are processed in configurable batches (default: 50)
- **Database Inserts**: Uses PostgreSQL `COPY FROM` for efficient batch inserts
2025-11-14 23:18:10 +08:00
via `Repository.InsertBatch` . The default processor issues single inserts,
but you can switch to buffered batches in high-throughput deployments.
2025-11-14 08:42:26 +08:00
- **Connection Pooling**: Configurable PostgreSQL connection pool
- **JetStream**: Reliable message delivery with automatic retries
## Troubleshooting
### Connection Issues
- **NATS**: Check that NATS server is running and JetStream is enabled
- **PostgreSQL**: Verify database connection string and that the schema exists
### Message Processing Issues
- Check logs for parsing errors
- Verify message format matches expected telegram format
- Check database constraints and indexes
### Configuration Issues
- Ensure `GO_ENV` is set correctly
- Verify configuration file exists at `configs/config.{env}.toml`
- Check environment variable names use `CAATSM_` prefix
## Migration from Legacy System
This project was refactored from:
- **Watermill** → **nats.go JetStream**
- **Hasura GraphQL** → **PostgreSQL pgx**
- **Viper** → **Koanf**
- **Manual DI** → **Google Wire**
The legacy code has been removed. See the project history for migration details.
## License
2025-11-15 16:30:29 +08:00
This repository has not declared a public license yet.
2025-11-14 08:42:26 +08:00
## Contributing
2025-11-15 16:30:29 +08:00
Contribution guidelines are not published; please coordinate changes via pull requests or direct maintainers.
2025-11-14 08:42:26 +08:00