✨ Introduce Docker Compose development stack with TimescaleDB and NATS services. Update README with usage instructions for local development. Upgrade Go version to 1.25.0 and update dependencies. Refactor configuration for NATS consumer rules and enhance Telegram database schema with TimescaleDB support.
This commit is contained in:
@@ -32,7 +32,7 @@ This project follows Clean Architecture principles with clear separation of conc
|
|||||||
|
|
||||||
- **Clean Architecture**: Clear separation between domain, application, and infrastructure layers
|
- **Clean Architecture**: Clear separation between domain, application, and infrastructure layers
|
||||||
- **NATS JetStream**: Reliable message streaming with automatic retries and dead-letter queues
|
- **NATS JetStream**: Reliable message streaming with automatic retries and dead-letter queues
|
||||||
- **PostgreSQL**: High-performance data persistence using pgx with batch operations
|
- **TimescaleDB (PostgreSQL)**: High-performance persistence using pgx with batch operations and hypertables
|
||||||
- **Dependency Injection**: Google Wire for compile-time dependency injection
|
- **Dependency Injection**: Google Wire for compile-time dependency injection
|
||||||
- **Configuration Management**: Koanf for flexible configuration loading (file + environment variables)
|
- **Configuration Management**: Koanf for flexible configuration loading (file + environment variables)
|
||||||
- **Structured Logging**: Zap logger with configurable levels and formats
|
- **Structured Logging**: Zap logger with configurable levels and formats
|
||||||
@@ -91,7 +91,7 @@ discard = "old"
|
|||||||
storage = "file"
|
storage = "file"
|
||||||
replicas = 1
|
replicas = 1
|
||||||
|
|
||||||
[nats.consumer]
|
[nats.consumer_rules]
|
||||||
max_deliver = 5
|
max_deliver = 5
|
||||||
ack_wait = "30s"
|
ack_wait = "30s"
|
||||||
max_ack_pending = 1024
|
max_ack_pending = 1024
|
||||||
@@ -225,6 +225,28 @@ Critical overrides stay available through CLI flags; advanced tuning such as str
|
|||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
### Docker Compose Dev Stack
|
||||||
|
|
||||||
|
For a local stack running TimescaleDB + NATS (matching `config.dev.toml`), use `docker-compose.dev.yml`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.dev.yml up -d postgres nats
|
||||||
|
docker compose -f docker-compose.dev.yml up app
|
||||||
|
```
|
||||||
|
|
||||||
|
- `postgres` uses TimescaleDB, seeding the `aviation` schema via `internal/repository/telegrams.ddl` (extension + hypertable).
|
||||||
|
- `app` mounts the repo so code changes are picked up by `go run ./cmd/main listen`.
|
||||||
|
- `nats` exposes 4222 (client) and 8222 (monitoring); `nats-box` is available for JetStream inspection (`docker compose exec nats-box sh`).
|
||||||
|
|
||||||
|
Prefer to run the Go binary on your host for quicker iteration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.dev.yml up -d postgres nats
|
||||||
|
GO_ENV=dev CAATSM_POSTGRES_URL=postgres://caatsm:caatsm@localhost:5432/aviation?sslmode=disable go run ./cmd/main listen
|
||||||
|
```
|
||||||
|
|
||||||
|
Bring everything down with `docker compose -f docker-compose.dev.yml down -v` when finished.
|
||||||
|
|
||||||
### Project Structure
|
### Project Structure
|
||||||
|
|
||||||
- **Domain Layer** (`internal/domain`): Pure business logic and domain models
|
- **Domain Layer** (`internal/domain`): Pure business logic and domain models
|
||||||
|
|||||||
@@ -104,6 +104,31 @@ tasks:
|
|||||||
- echo "Linting code..."
|
- echo "Linting code..."
|
||||||
- golangci-lint run
|
- golangci-lint run
|
||||||
|
|
||||||
|
up:
|
||||||
|
desc: Start TimescaleDB + NATS dev stack
|
||||||
|
cmds:
|
||||||
|
- echo "Starting dev infrastructure..."
|
||||||
|
- podman compose -f docker-compose.dev.yml up -d
|
||||||
|
|
||||||
|
down:
|
||||||
|
desc: Stop dev compose stack and remove containers
|
||||||
|
cmds:
|
||||||
|
- echo "Stopping dev infrastructure..."
|
||||||
|
- podman compose -f docker-compose.dev.yml down
|
||||||
|
|
||||||
|
dev-run:
|
||||||
|
desc: Run receiver locally against dev stack
|
||||||
|
deps:
|
||||||
|
- up
|
||||||
|
cmds:
|
||||||
|
- >
|
||||||
|
CAATSM_NATS_URL=nats://localhost:4222
|
||||||
|
CAATSM_POSTGRES_URL=postgres://caatsm:caatsm@localhost:5432/aviation?sslmode=disable
|
||||||
|
CAATSM_TELEMETRY_ENABLED=true
|
||||||
|
CAATSM_TELEMETRY_ENDPOINT=localhost:4318
|
||||||
|
GO_ENV=dev
|
||||||
|
go run ./cmd/main listen
|
||||||
|
|
||||||
|
|
||||||
help:
|
help:
|
||||||
desc: Show this help message
|
desc: Show this help message
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ discard = "old"
|
|||||||
storage = "file"
|
storage = "file"
|
||||||
replicas = 1
|
replicas = 1
|
||||||
|
|
||||||
[nats.consumer]
|
[nats.consumer_rules]
|
||||||
max_deliver = 5
|
max_deliver = 5
|
||||||
ack_wait = "30s"
|
ack_wait = "30s"
|
||||||
max_ack_pending = 1024
|
max_ack_pending = 1024
|
||||||
@@ -52,5 +52,5 @@ format = "json"
|
|||||||
|
|
||||||
[telemetry]
|
[telemetry]
|
||||||
enabled = false
|
enabled = false
|
||||||
endpoint = "http://otel-collector:4318"
|
endpoint = "otel-collector:4318"
|
||||||
insecure = true
|
insecure = true
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
receivers:
|
||||||
|
otlp:
|
||||||
|
protocols:
|
||||||
|
http:
|
||||||
|
endpoint: 0.0.0.0:4318
|
||||||
|
grpc:
|
||||||
|
endpoint: 0.0.0.0:4317
|
||||||
|
|
||||||
|
exporters:
|
||||||
|
logging:
|
||||||
|
loglevel: info
|
||||||
|
|
||||||
|
service:
|
||||||
|
pipelines:
|
||||||
|
traces:
|
||||||
|
receivers: [otlp]
|
||||||
|
exporters: [logging]
|
||||||
|
metrics:
|
||||||
|
receivers: [otlp]
|
||||||
|
exporters: [logging]
|
||||||
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
services:
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
image: timescale/timescaledb:2.15.2-pg16
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: caatsm
|
||||||
|
POSTGRES_PASSWORD: caatsm
|
||||||
|
POSTGRES_DB: aviation
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
volumes:
|
||||||
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
- ./internal/repository/telegrams.ddl:/docker-entrypoint-initdb.d/01-telegrams.sql:ro
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
networks:
|
||||||
|
- devnet
|
||||||
|
|
||||||
|
nats:
|
||||||
|
image: nats:2.10-alpine
|
||||||
|
command: ["-js", "--http_port=8222", "-DV"]
|
||||||
|
ports:
|
||||||
|
- "4222:4222"
|
||||||
|
- "8222:8222"
|
||||||
|
networks:
|
||||||
|
- devnet
|
||||||
|
|
||||||
|
nats-box:
|
||||||
|
image: synadia/nats-box:latest
|
||||||
|
entrypoint: ["/bin/sh", "-c", "sleep infinity"]
|
||||||
|
depends_on:
|
||||||
|
- nats
|
||||||
|
networks:
|
||||||
|
- devnet
|
||||||
|
environment:
|
||||||
|
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4318
|
||||||
|
|
||||||
|
otel-collector:
|
||||||
|
image: otel/opentelemetry-collector-contrib:0.99.0
|
||||||
|
command:
|
||||||
|
- "--config=/etc/otelcol/config.yaml"
|
||||||
|
volumes:
|
||||||
|
- ./configs/otel-collector.dev.yaml:/etc/otelcol/config.yaml:ro
|
||||||
|
ports:
|
||||||
|
- "4317:4317"
|
||||||
|
- "4318:4318"
|
||||||
|
networks:
|
||||||
|
- devnet
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres-data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
devnet:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
module caatsm
|
module caatsm
|
||||||
|
|
||||||
go 1.24.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
@@ -35,7 +35,7 @@ require (
|
|||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
github.com/google/go-cmp v0.7.0 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20251007162407-5df77e3f7d1d // indirect
|
github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ type NATSConfig struct {
|
|||||||
Stream string `koanf:"stream"`
|
Stream string `koanf:"stream"`
|
||||||
Consumer string `koanf:"consumer"`
|
Consumer string `koanf:"consumer"`
|
||||||
StreamLimits StreamLimitsConfig `koanf:"stream_limits"`
|
StreamLimits StreamLimitsConfig `koanf:"stream_limits"`
|
||||||
ConsumerRules ConsumerRulesConfig `koanf:"consumer"`
|
ConsumerRules ConsumerRulesConfig `koanf:"consumer_rules"`
|
||||||
// Legacy fields
|
// Legacy fields
|
||||||
Client string `koanf:"client"`
|
Client string `koanf:"client"`
|
||||||
Cluster string `koanf:"cluster"`
|
Cluster string `koanf:"cluster"`
|
||||||
|
|||||||
@@ -1,37 +1,44 @@
|
|||||||
CREATE SCHEMA IF NOT EXISTS aviation;
|
CREATE SCHEMA IF NOT EXISTS aviation;
|
||||||
|
|
||||||
|
CREATE EXTENSION IF NOT EXISTS timescaledb;
|
||||||
|
|
||||||
CREATE TABLE aviation.telegrams (
|
CREATE TABLE aviation.telegrams (
|
||||||
uuid UUID PRIMARY KEY,
|
uuid UUID NOT NULL,
|
||||||
message_id VARCHAR(255) ,
|
message_id TEXT,
|
||||||
date_time VARCHAR(255) ,
|
date_time TEXT,
|
||||||
priority_indicator VARCHAR(255) ,
|
priority_indicator TEXT,
|
||||||
primary_address VARCHAR(255) ,
|
primary_address TEXT,
|
||||||
secondary_addresses TEXT,
|
secondary_addresses TEXT,
|
||||||
originator VARCHAR(255),
|
originator TEXT,
|
||||||
originator_date_time VARCHAR(255),
|
originator_date_time TEXT,
|
||||||
category VARCHAR(255),
|
category TEXT,
|
||||||
content TEXT,
|
content TEXT,
|
||||||
body_data JSONB,
|
body_data JSONB,
|
||||||
status VARCHAR(32) NOT NULL DEFAULT 'parsed',
|
status TEXT NOT NULL DEFAULT 'parsed',
|
||||||
error_reason TEXT,
|
error_reason TEXT,
|
||||||
received_at TIMESTAMP NOT NULL,
|
received_at TIMESTAMPTZ NOT NULL,
|
||||||
parsed_at TIMESTAMP,
|
parsed_at TIMESTAMPTZ,
|
||||||
dispatched_at TIMESTAMP,
|
dispatched_at TIMESTAMPTZ,
|
||||||
need_dispatch BOOLEAN
|
need_dispatch BOOLEAN,
|
||||||
|
PRIMARY KEY (uuid, received_at)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
SELECT create_hypertable('aviation.telegrams', 'received_at', if_not_exists => TRUE);
|
||||||
|
|
||||||
-- Indexes for better query performance
|
-- Indexes for better query performance
|
||||||
CREATE INDEX idx_telegrams_message_id ON aviation.telegrams (message_id);
|
CREATE INDEX idx_telegrams_message_id ON aviation.telegrams (message_id);
|
||||||
CREATE INDEX idx_telegrams_date_time ON aviation.telegrams (date_time);
|
CREATE INDEX idx_telegrams_date_time ON aviation.telegrams (date_time);
|
||||||
CREATE INDEX idx_telegrams_priority_indicator ON aviation.telegrams (priority_indicator);
|
CREATE INDEX idx_telegrams_priority_indicator ON aviation.telegrams (priority_indicator);
|
||||||
CREATE INDEX idx_telegrams_primary_address ON aviation.telegrams (primary_address);
|
CREATE INDEX idx_telegrams_primary_address ON aviation.telegrams (primary_address);
|
||||||
CREATE INDEX idx_telegrams_received_at ON aviation.telegrams (received_at);
|
CREATE INDEX idx_telegrams_received_at ON aviation.telegrams (received_at);
|
||||||
|
CREATE INDEX idx_telegrams_uuid ON aviation.telegrams (uuid);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS aviation.telegrams_raw (
|
CREATE TABLE IF NOT EXISTS aviation.telegrams_raw (
|
||||||
uuid UUID PRIMARY KEY,
|
uuid UUID NOT NULL,
|
||||||
status VARCHAR(32) NOT NULL,
|
status TEXT NOT NULL,
|
||||||
error_reason TEXT,
|
error_reason TEXT,
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
received_at TIMESTAMP NOT NULL,
|
received_at TIMESTAMPTZ NOT NULL,
|
||||||
metadata JSONB
|
metadata JSONB,
|
||||||
|
PRIMARY KEY (uuid, received_at)
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user