didi-lot2-backend/backend/services/orchestration-layer/agent-v3/MIGRATION_RABBITMQ.md
2026-07-10 03:39:53 -07:00

5.6 KiB
Raw Blame History

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.tsgetRabbitMQConfig() + 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:

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:

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):

# 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

./redis-switch.sh local rabbit    # doar RabbitMQ înapoi la local

Cleanup DEFINITIV (după 30+ zile stabil)

  1. Stop + remove container local:

    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.