# 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.