168 lines
5.6 KiB
Markdown
168 lines
5.6 KiB
Markdown
# Migrare RabbitMQ: `staging-dataLayer-rabbitmq` local → cluster HA `10.11.50.100`
|
||
|
||
**Autor**: refactor 2026-04-22 (după Redis migration)
|
||
**Scop**: mutarea cozilor async (cele 31 de cozi analysis.*) de pe containerul RabbitMQ local pe clusterul HA managed cu vhost dedicat `/didi`.
|
||
|
||
---
|
||
|
||
## Stare actuală (post-cutover)
|
||
|
||
- Codul RabbitMQ era **deja centralizat** în 3 fișiere:
|
||
- `src/queue/connection.ts` — manager singleton conexiune + channels
|
||
- `src/shared/queue/constants.ts` — `getRabbitMQConfig()` + `getRabbitMQUrl()`
|
||
- `src/scripts/init-priority-queues.ts` — script standalone
|
||
- Zero refactor pe cod aplicație. Un singur punct de schimbat.
|
||
- URL-encoding corect pentru vhost `/didi` adăugat în `getRabbitMQUrl()` (folosește `encodeURIComponent` pentru user/pass/vhost).
|
||
- `CONNECTION_RETRY_DELAY` tuned de la 5s la 2s pentru failover HA mai rapid.
|
||
- Logging îmbunătățit: arată host+vhost+user la conectare/eroare/close.
|
||
|
||
---
|
||
|
||
## Target
|
||
|
||
| Parametru | Valoare |
|
||
|---|---|
|
||
| AMQP | `10.11.50.100:16672` |
|
||
| Management UI | `http://10.11.50.100:16673` |
|
||
| Username | `didi` |
|
||
| Password | `$CLUSTER_PASSWORD` |
|
||
| Vhost dedicat | `/didi` (izolat de alte proiecte) |
|
||
| URI encoded | `amqp://didi:...@10.11.50.100:16672/%2Fdidi` |
|
||
| Cluster HA | 3 noduri rag01/02/03 + HAProxy VIP |
|
||
|
||
---
|
||
|
||
## Diferență critică față de Redis
|
||
|
||
**Nu a fost nevoie de migrare de date**. Cozile RabbitMQ sunt efemere — topologia (exchange + 31 queues) se creează automat de workeri la `assertExchange()` / `assertQueue()` pe prima conectare. Mesajele în zbor la cutover se pierd (erau 0 la momentul migrării).
|
||
|
||
---
|
||
|
||
## Pași executați la cutover
|
||
|
||
### 1. Refactor cod (minim)
|
||
|
||
- `src/queue/connection.ts`:
|
||
- Retry: 5s → 2s
|
||
- Logging: `[RabbitMQ] Connecting to HOST:PORT/VHOST (user: USER)...`
|
||
- Error messages: includ host+vhost pentru debug
|
||
- Add `blocked/unblocked` handlers (broker flow control)
|
||
- `src/shared/queue/constants.ts`:
|
||
- `getRabbitMQUrl()` folosește `encodeURIComponent()` pentru user/pass/vhost
|
||
- Fix critical: vhost `/didi` acum devine `%2Fdidi` în URL (fără asta, amqplib parsa ca vhost `didi` fără slash)
|
||
|
||
### 2. docker-compose (agent-v3)
|
||
|
||
`x-worker-env` schimbate din hardcoded la fallbacks:
|
||
```yaml
|
||
RABBITMQ_HOST: ${RABBITMQ_HOST:-staging-dataLayer-rabbitmq}
|
||
RABBITMQ_PORT: ${RABBITMQ_PORT:-5672}
|
||
RABBITMQ_USER: ${RABBITMQ_USER:-admin}
|
||
RABBITMQ_PASS: ${RABBITMQ_PASS:-rabbitmq123}
|
||
RABBITMQ_VHOST: ${RABBITMQ_VHOST:-/}
|
||
```
|
||
|
||
### 3. `.env` agent-v3
|
||
|
||
Adăugat bloc:
|
||
```
|
||
RABBITMQ_HOST=10.11.50.100
|
||
RABBITMQ_PORT=16672
|
||
RABBITMQ_USER=didi
|
||
RABBITMQ_PASS=$CLUSTER_PASSWORD
|
||
RABBITMQ_VHOST=/didi
|
||
```
|
||
|
||
### 4. Rebuild + restart
|
||
|
||
- `docker compose build` (toate 8 imagini, cache fast)
|
||
- `docker compose up -d --force-recreate` pe agent-v3 stack
|
||
- Workerii s-au conectat în < 1s și au creat automat toate 31 cozile
|
||
|
||
### 5. Verificare
|
||
|
||
- 31/31 cozi prezente pe `/didi` cu consumerii corespunzători (claims×3, rest×2)
|
||
- 14 conexiuni active de la workeri prin HAProxy VIP
|
||
- Smoke test: analiză async plan 4 = pro, status `completed` în 3.9s, risk MOSTLY_RELIABLE (22)
|
||
|
||
---
|
||
|
||
## Script de verificare: `scripts/verify-rabbitmq-cluster.ts`
|
||
|
||
Mirror al `verify-redis-cluster.ts`. Rulează cu:
|
||
|
||
```bash
|
||
RABBITMQ_HOST=10.11.50.100 RABBITMQ_PORT=16672 \
|
||
RABBITMQ_USER=didi RABBITMQ_PASS='...' RABBITMQ_VHOST=/didi \
|
||
node_modules/.bin/ts-node --transpile-only scripts/verify-rabbitmq-cluster.ts
|
||
```
|
||
|
||
Verifică:
|
||
1. Conectivitate + auth pe vhost
|
||
2. Channel creation
|
||
3. Assert exchange `analysis` (topic, durable)
|
||
4. Write privilege (creează + șterge coadă temporară)
|
||
5. Topologie expected (31 cozi) — WARN dacă workerii nu au rulat încă
|
||
|
||
Exit codes: `0` = OK sau WARN (topology lipsă pre-cutover), `1` = FATAL.
|
||
|
||
---
|
||
|
||
## Fallback LOCAL păstrat
|
||
|
||
Containerul `staging-dataLayer-rabbitmq` e **oprit** dar **nu șters** — rollback rapid prin script.
|
||
|
||
### Switch rapid: `services/orchestration-layer/scripts/redis-switch.sh`
|
||
|
||
Scriptul acoperă ACUM ambele servicii (Redis + RabbitMQ):
|
||
|
||
```bash
|
||
# Arată status curent
|
||
./redis-switch.sh status
|
||
|
||
# Switch ambele (Redis + Rabbit) la cluster
|
||
./redis-switch.sh cluster
|
||
|
||
# Switch ambele la local
|
||
./redis-switch.sh local
|
||
|
||
# Doar unul:
|
||
./redis-switch.sh cluster redis # Redis → cluster, Rabbit neschimbat
|
||
./redis-switch.sh local rabbit # Rabbit → local, Redis neschimbat
|
||
```
|
||
|
||
Script-ul:
|
||
- Pornește containerul local dacă e oprit (`docker start` sau `docker compose up -d`)
|
||
- Rescrie blocul corespunzător în `.env` (fără să atingă celelalte)
|
||
- Restart `didi-framework` + `agent-v3` stack
|
||
|
||
### Rollback în 30s
|
||
|
||
```bash
|
||
./redis-switch.sh local rabbit # doar RabbitMQ înapoi la local
|
||
```
|
||
|
||
---
|
||
|
||
## Cleanup DEFINITIV (după 30+ zile stabil)
|
||
|
||
1. Stop + remove container local:
|
||
```bash
|
||
docker stop staging-dataLayer-rabbitmq # deja oprit
|
||
docker rm staging-dataLayer-rabbitmq
|
||
docker volume rm didi-staging-rabbitmq-data
|
||
```
|
||
|
||
2. Șterge secțiunea din `data-layer/docker-compose.yml`
|
||
|
||
3. Update `didiQueue/INDEX.md` marcat ca deprecated
|
||
|
||
4. Admin dashboard nginx — șterge `/health-check/rabbitmq` (pointa la container local)
|
||
|
||
---
|
||
|
||
## Riscuri cunoscute / TODO
|
||
|
||
1. **amqplib nu are Sentinel/discovery nativ** — la failover cluster, ioredis-style "învață" singur noul master. amqplib doar se deconectează și se reconectează la VIP (care rutează la noul master). Downtime client: 5-15s acceptabil.
|
||
2. **init-priority-queues.ts** — neinvestigat în detaliu dacă mai e rulat. Cozile se creează oricum prin workeri.
|
||
3. **Monitoring**: management UI-ul e pe `10.11.50.100:16673` — accesibil doar din LAN. Admin dashboard nu-l embedă direct; user poate deschide browser separat.
|