- T1: SIGTERM/SIGINT → client.Disconnect(250) → os.Exit(0) - T2: 11 tests with miniredis mock (handleMessage, getEnv, parseInt, randomString) - T3: golang.org/x/net/context → stdlib context, math/rand → math/rand/v2 - T4: .golangci.yml + .github/workflows/ci.yml - T5: Redis SETEX error log includes key and memberID - Dockerfile: add git for go mod tidy
118 lines
5.2 KiB
Markdown
118 lines
5.2 KiB
Markdown
# Repository Guidelines
|
|
|
|
## Project Overview
|
|
|
|
MQTT presence service written in Go. Subscribes to an MQTT topic (`presence`), parses incoming messages (`memberID;...`), and stores presence data in Redis with a TTL. Purpose: real-time member presence tracking for the Backone platform (port of a Django/Python original).
|
|
|
|
## Architecture & Data Flow
|
|
|
|
```
|
|
MQTT Broker ──publish──▶ MQTT Client (paho.mqtt)
|
|
│
|
|
▼
|
|
handleMessage()
|
|
- parse memberID from payload (split on ";")
|
|
- truncate memberID to 50 chars
|
|
- build JSON {mqtt, ts}
|
|
- SETEX to Redis key "presence:<memberID>"
|
|
│
|
|
▼
|
|
Redis
|
|
```
|
|
|
|
Single goroutine architecture. `main()` connects Redis, connects MQTT, subscribes on connect, then blocks forever (`select {}`). All message handling is synchronous in the MQTT callback.
|
|
|
|
## Key Directories
|
|
|
|
```
|
|
backone-manage-go/
|
|
├── mqtt_presence_redis_go/ # ALL source code lives here
|
|
│ ├── mqtt_presence_redis_go.go # Single-file application (package main)
|
|
│ ├── go.mod / go.sum
|
|
│ └── Dockerfile
|
|
├── .gitignore
|
|
└── README.md
|
|
```
|
|
|
|
No subpackages, no internal/, no cmd/. The entire application is one `.go` file.
|
|
|
|
## Development Commands
|
|
|
|
```bash
|
|
# Build
|
|
cd mqtt_presence_redis_go && go build -o mqtt_presence_redis_go mqtt_presence_redis_go.go
|
|
|
|
# Run locally (requires MQTT broker + Redis running)
|
|
cd mqtt_presence_redis_go && go run mqtt_presence_redis_go.go
|
|
|
|
# Docker build
|
|
cd mqtt_presence_redis_go && docker build -t mqtt-presence-redis-go .
|
|
|
|
# No Makefile, no CI pipeline, no lint config
|
|
```
|
|
|
|
## Code Conventions & Common Patterns
|
|
|
|
### Configuration
|
|
- **All config via environment variables** — no config files, no viper/yaml.
|
|
- Pattern: `getEnv("ENV_KEY", "default")` helper at package level.
|
|
- Env vars: `MQTT_HOST`, `MQTT_PORT`, `MQTT_USER`, `MQTT_PASS`, `MQTT_TOPIC_PRESENCE`, `MQTT_REDIS_HOST`, `MQTT_REDIS_PORT`, `MQTT_REDIS_DB`, `MQTT_REDIS_PREFIX`, `MQTT_REDIS_SETEX`, `MQTT_REDIS_PASSWORD`.
|
|
|
|
### Naming
|
|
- Package-level vars: `camelCase` (e.g. `mqttHost`, `redisPrefix`).
|
|
- Functions: `camelCase` (e.g. `handleMessage`, `getEnv`, `parseInt`, `randomString`).
|
|
- Constants: no `const` block used; inline defaults.
|
|
- File naming: `<package_name>.go` matches module directory.
|
|
|
|
### Error Handling
|
|
- `log.Fatalf` for startup failures (Redis ping, MQTT connect).
|
|
- `log.Printf` + `return` for runtime errors (message handling).
|
|
- No custom error types; no error wrapping.
|
|
|
|
### Dependencies
|
|
- `github.com/eclipse/paho.mqtt.golang` — MQTT client
|
|
- `github.com/go-redis/redis/v8` — Redis client
|
|
- `golang.org/x/net/context` — context (stdlib `context` preferred in Go 1.7+)
|
|
|
|
### Patterns
|
|
- No interfaces, no DI, no dependency injection.
|
|
- No middleware, no routing — single topic subscription.
|
|
- No graceful shutdown handling (`select {}` blocks forever).
|
|
- `math/rand` for client ID (not `crypto/rand`) — acceptable for non-security use.
|
|
- JSON marshaling via `map[string]interface{}` — no typed struct for presence data.
|
|
|
|
## Important Files
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `mqtt_presence_redis_go/mqtt_presence_redis_go.go` | Entire application |
|
|
| `mqtt_presence_redis_go/go.mod` | Module: `git.proit.id/dsutanto/backone-manage-go/mqtt_presence_redis_go`, Go 1.24.3 |
|
|
| `mqtt_presence_redis_go/Dockerfile` | Multi-stage build: `golang:1.24-alpine` → `alpine:3.20` |
|
|
|
|
## Runtime/Tooling Preferences
|
|
|
|
- **Go 1.24.3** (specified in go.mod and Dockerfile)
|
|
- **No package manager** beyond `go mod`
|
|
- **No linter configured** (no `.golangci.yml`)
|
|
- **No formatter config** (standard `gofmt` assumed)
|
|
- **No CI/CD** — deployment via Docker only
|
|
- **Redis 8 client** (`go-redis/v8`) — requires Redis 6+ for SETEX
|
|
- **MQTT 3.1.1** via paho client
|
|
|
|
## Testing & QA
|
|
|
|
- **No tests exist.** No `*_test.go` files anywhere.
|
|
- **No test framework configured.**
|
|
- No coverage tooling, no test scripts.
|
|
- If adding tests: use standard `testing` package. Table-driven tests for `handleMessage` message parsing logic. Mock Redis with `miniredis` or `go-redis/mock`. Mock MQTT with a test broker or interface-based mock.
|
|
|
|
## Key Observations for AI Assistants
|
|
|
|
1. **Single-file architecture** — all changes go in `mqtt_presence_redis_go.go`. No cross-file imports to track.
|
|
2. **No tests** — any refactor carries risk. Consider adding tests for `handleMessage` before significant changes.
|
|
3. **No graceful shutdown** — `select {}` means no signal handling, no MQTT disconnect on SIGTERM. Docker sends SIGTERM then SIGKILL.
|
|
4. **`golang.org/x/net/context`** is used instead of stdlib `context` — this is a legacy import; stdlib `context` is standard since Go 1.7.
|
|
5. **Payload parsing** is fragile: splits on `;`, takes first element, truncates to 50 chars. No validation of payload format.
|
|
6. **Redis key pattern**: `presence:<memberID>` with configurable prefix and TTL (default 86400s / 24h).
|
|
7. **The comment about Django** indicates this is a port — check Python original for behavioral reference if behavior questions arise.
|