TCP-based message processing system with persistent connections, authentication, job distribution, and cryptographic validation.
This project implements a client-server TCP system that handles:
- Client authentication
- Task distribution (jobs)
- Result submission and validation
- Per-session rate limiting
- Statistics persistence
- Asynchronous processing via message broker
The project uses Go Workspaces (go.work) to organize multiple related modules:
tcp-message-processor/
├── server/ # TCP Server
├── client/ # TCP Client
└── common/ # Shared code
Why Go Workspaces?
- Integrated Development - Changes to
commonmodule are reflected immediately inserverandclientwithout publishing versions - Shared Code -
common/pkgcontains utilities reused by both (logger, hash, tcp protocol, etc.) - Simplified Testing - Integration tests can import and use code from multiple modules
- Unified Versioning - Easier to maintain compatibility between components
- Developer Experience - IDEs and Go tools automatically recognize local dependencies
Decision: Use database/sql with PostgreSQL driver directly
While GORM is commonly used in conventional REST APIs and I have experience with it, this project doesn't benefit from an ORM due to its simple data model and write-only operations.
Why NOT use GORM here:
- Single Write Operation - Only INSERT statements, no complex queries or relationships
- No Domain Model - Statistics are fire-and-forget aggregations, not domain entities
- Asynchronous Only - Consumer writes to DB without business logic or validation
- No Migrations Needed - Single static table, schema defined in SQL file
- Unnecessary Abstraction - GORM features (associations, hooks, query builder) unused
When GORM makes sense:
- REST APIs with full CRUD operations
- Complex domain models with relationships (1:N, N:M)
- Dynamic query building based on filters
- Need for database abstraction (multi-DB support)
- Migration management and schema evolution
Asynchronous submission processing:
- Publisher sends submission events
- Consumer processes and persists to database
- Server doesn't block on database writes
- Messages survive restarts
Uses golang.org/x/time/rate (stdlib):
- Per-session limiter (1 req/second per client)
- Thread-safe with
sync.Map - In-memory storage
- Official Go library
Client includes automated integration tests using Testcontainers:
- Location:
client/test/integration/ratelimit_test.go - Coverage: Rate limiting validation (1 req/second)
- Infrastructure: PostgreSQL, RabbitMQ, TCP server in containers
- Execution:
cd client && make test-integration
- Go 1.25+
- Docker & Docker Compose
- Make (optional)
- golangci-lint (for linting)
# PostgreSQL + RabbitMQ
docker compose up -dcd server
make run-local# Separate terminal - Single client
cd client
make run-local
# Or multiple clients
make run-multi# Connect to PostgreSQL
docker exec -it tcp-message-processor-postgres-1 psql -U tcpuser -d tcpprocessor
# Query
SELECT * FROM submissions ORDER BY timestamp DESC LIMIT 10;Both modules support configuration via env.yaml file or environment variables (overrides file).
See module-specific documentation:
- RabbitMQ Management: http://localhost:15672 (guest/guest)
- PostgreSQL:
localhost:5432(tcpuser/tcppass/tcpprocessor)
- Observability: Metrics (Prometheus), distributed tracing (OpenTelemetry), dashboards (Grafana)
- Resilience: Circuit breaker, retry policies, health checks
- Scalability: Load balancer, multiple server instances, Redis for distributed rate limiting
- Security: TLS for TCP connections, real authentication (JWT, OAuth), action auditing