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

11 KiB

Migrare Redis: didi-cache local → cluster HA 10.11.50.100

Autor: refactor 2026-04-22 Scop: mutarea tuturor clienților Redis (agent-v3 + didiFramework) de pe containerul local didi-cache pe clusterul HA managed (rag01/02/03 cu VIP HAProxy pe 10.11.50.100).


Stare actuală (post-refactor cod, pre-cutover)

  • Tot codul Redis e centralizat prin createRedisConnection() — 2 helpers paraleli:
    • agent-v3/src/shared/redis/connection.ts
    • didiFramework/src/config/redis.ts
  • Zero new Redis({...}) inline în code — grep confirmă.
  • Suport pentru ACL (username + password) adăugat: dacă REDIS_USERNAME e gol, se face legacy AUTH (doar password).
  • Auto-reconnect + retryStrategy + reconnectOnError pentru READONLY/MASTERDOWN.
  • docker-compose.yml (agent-v3, didiFramework) au env vars cu fallback la valorile vechi (didi-cache, redis123) — deci nimic nu s-a rupt, rulează identic cu înainte.

Target

Parametru Valoare
Host 10.11.50.100
Port 16379
Username didi
Password $CLUSTER_PASSWORD
DB 0
Sentinel VIP 10.11.50.100:16380 (master name ragmaster) — neutilizat deocamdată, folosim HAProxy VIP

Strategie failover: HAProxy VIP (simplu). ioredis reconectează automat la VIP după failover; TCP session se rupe 2-10s. Dacă se dovedește insuficient, putem trece la Sentinel client (cod pregătit să accepte overrides în helper).


Pre-cutover checklist

  1. Verificare conectivitate din host DIDI (10.11.10.12):

    docker exec didi-cache redis-cli -h 10.11.50.100 -p 16379 \
      --user didi --pass '$CLUSTER_PASSWORD' --no-auth-warning PING
    # Expected: PONG
    
  2. Verificare din container agent-v3 (important — rețeaua Docker bridge trebuie să rutează prin host la LAN):

    docker exec didi-agent-v3 sh -c 'apk add --no-cache redis 2>/dev/null; redis-cli -h 10.11.50.100 -p 16379 --user didi --pass "$CLUSTER_PASSWORD" --no-auth-warning PING'
    

    Dacă eșuează, host-ul nu rutează containerul la LAN. Fix: docker network inspect didi-network + verifică iptables DOCKER-USER.

  3. Verificare replicare cluster:

    docker exec didi-cache redis-cli -h 10.11.50.100 -p 16379 \
      --user didi --pass '$CLUSTER_PASSWORD' --no-auth-warning INFO replication
    # Expected: role:master, connected_slaves:2
    
  4. Backup Redis local (paranoia):

    docker exec didi-cache redis-cli -a redis123 --no-auth-warning --rdb /data/pre-migration-backup.rdb
    docker cp didi-cache:/data/pre-migration-backup.rdb ./didi-cache-backup-$(date +%F).rdb
    
  5. Fereastră de mentenanță — alege un moment cu trafic minim. Impact estimat:

    • 5-10s: restart services
    • <60s: primul /api/sync-redis să repopuleze framework config
    • Sesiuni async în zbor (didi:queue:session:*) — se pot pierde. Inventariere:
      docker exec didi-cache redis-cli -a redis123 --no-auth-warning --scan --pattern 'didi:queue:session:*' | wc -l
      
      Dacă > 0, așteaptă ca cozile RabbitMQ să se golească înainte de cutover.

Pași cutover

1. Oprire workeri + API (menține Redis local live pentru rollback)

cd /home/admin365/didi_mono/backend/services/orchestration-layer/agent-v3
docker compose stop agent-v3 worker-media-preprocess worker-techniques \
  worker-ai-tampered worker-claims worker-domain verdict-aggregator

cd /home/admin365/didi_mono/backend/services/orchestration-layer/didiFramework
docker compose stop didi-framework

2. Setează env vars noi

Editează /etc/didi.env (sau altă locație shared) sau adaugă la .env din root-ul fiecărui compose:

REDIS_HOST=10.11.50.100
REDIS_PORT=16379
REDIS_USERNAME=didi
REDIS_PASSWORD=$CLUSTER_PASSWORD
REDIS_DB=0

Recomandat: un singur .env la /home/admin365/didi_mono/backend/.env pe care îl referențiază ambele compose-uri prin env_file: (necesită mică modificare in compose).

Atenție: NU committa parola în git.

3. Rebuild containere cu noul cod

cd /home/admin365/didi_mono/backend/services/orchestration-layer/agent-v3
docker compose build agent-v3 worker-media-preprocess worker-techniques \
  worker-ai-tampered worker-claims worker-domain verdict-aggregator

cd /home/admin365/didi_mono/backend/services/orchestration-layer/didiFramework
docker compose build didi-framework

4. Pornește didiFramework primul + rulează sync-redis

Ordinea contează: agent-v3 citește config din Redis la fiecare request. Dacă nu există config, analizele eșuează.

cd /home/admin365/didi_mono/backend/services/orchestration-layer/didiFramework
docker compose up -d didi-framework

# Așteaptă să fie ready
sleep 10
docker compose logs didi-framework | tail -20

# Trigger sync-redis: PG → noul cluster Redis
curl -X POST http://localhost:3005/api/sync-redis
# Expected: 200 OK, { "success": true, "categories": ["techniques", "sources", ...] }

5. Validează că noul cluster are datele framework

docker exec didi-cache redis-cli -h 10.11.50.100 -p 16379 \
  --user didi --pass '$CLUSTER_PASSWORD' --no-auth-warning \
  --scan --pattern 'didi:framework:*'
# Expected: 8 keys (manifest, techniques, sources, claims, verdicts, weights, providers, dimensions_compact)

docker exec didi-cache redis-cli -h 10.11.50.100 -p 16379 \
  --user didi --pass '$CLUSTER_PASSWORD' --no-auth-warning \
  --scan --pattern 'didi:config:*' | wc -l
# Expected: ~30 keys

6. Pornește agent-v3 + workeri

cd /home/admin365/didi_mono/backend/services/orchestration-layer/agent-v3
docker compose up -d

7. Smoke test

# Health
curl http://localhost:24803/api/v3/health

# Test analiză text scurt (sync) — verifică tot fluxul Redis
curl -X POST http://localhost:24803/api/v3/pipeline/analyze \
  -H "Content-Type: application/json" \
  -d '{"text": "Test migrare redis cluster"}' | jq '.data.session_id, .data.status'
# Expected: session_id UUID, status: "completed"

# Confirmă că sesiunea e scrisă pe cluster
SID=<session_id_de_mai_sus>
docker exec didi-cache redis-cli -h 10.11.50.100 -p 16379 \
  --user didi --pass '$CLUSTER_PASSWORD' --no-auth-warning \
  --scan --pattern "didi:pipeline:${SID}:*"

8. Monitorizare 30 min

# Live logs — caută "Redis error" sau "ECONNREFUSED"
docker compose logs -f --tail 0 agent-v3 worker-techniques worker-claims verdict-aggregator | grep -iE "redis|error"

# Keys growth pe noul cluster
watch -n 5 'docker exec didi-cache redis-cli -h 10.11.50.100 -p 16379 --user didi --pass "$CLUSTER_PASSWORD" --no-auth-warning DBSIZE'

Rollback plan

Dacă ceva crapă în primele 30 min:

  1. Restore env vars:

    # Elimină REDIS_USERNAME și revino la defaults
    unset REDIS_HOST REDIS_PORT REDIS_USERNAME REDIS_PASSWORD REDIS_DB
    # sau sterge liniile din /etc/didi.env
    
  2. Restart containere:

    cd /home/admin365/didi_mono/backend/services/orchestration-layer/agent-v3
    docker compose up -d --force-recreate
    
    cd /home/admin365/didi_mono/backend/services/orchestration-layer/didiFramework
    docker compose up -d --force-recreate
    
  3. Sync-redis pe local să repopuleze:

    curl -X POST http://localhost:3005/api/sync-redis
    

Datele vechi sunt încă pe didi-cache (nu le-am șters). Maxim 30 min de analize noi se pierd.


Post-cutover — FALLBACK LOCAL PĂSTRAT

Containerul didi-cache e oprit dar NU șters — poate fi repornit oricând ca fallback pentru dev local sau incident recovery. Volumul didi-production-cache-data rămâne intact.

Script de switch rapid: services/orchestration-layer/scripts/redis-switch.sh

# Arată starea curentă
./redis-switch.sh status

# Switch la cluster (production)
./redis-switch.sh cluster

# Switch înapoi la local (rollback sau dev)
./redis-switch.sh local

Ce face:

  • Rescrie REDIS_* în ambele .env files (agent-v3 + didiFramework)
  • Pentru local: pornește containerul didi-cache dacă e oprit
  • Restart didi-framework + agent-v3 stack (API + 12 workeri)
  • Bootstrap auto-populează Redis-ul ales dacă e gol

Cum rămâne imaginea locală disponibilă:

  • didi-cache definit în production/docker-compose.ymlnu șterge secțiunea
  • Image: redis:7-alpine (standard, disponibil oricând)
  • Volum: didi-production-cache-data (păstrat cu datele vechi)
  • Parolă: redis123 (hardcodată în REDIS_HOSTS al Redis Commander pentru vizualizare)

Chiar și dacă clusterul cade complet, rollback-ul e 30 secunde: ./redis-switch.sh local.

Cleanup DEFINITIV (când cluster e stable 30+ zile și nu mai vrei fallback):

  1. Oprește și șterge containerul didi-cache:

    docker stop didi-cache
    docker rm didi-cache
    docker volume rm didi-production-cache-data  # păstrează ca backup încă 30 zile dacă ești paranoic
    
  2. Curăță production/docker-compose.yml — șterge secțiunea didi-cache.

  3. Redis Commander — update REDIS_HOSTS în data-layer/docker-compose.yml:

    REDIS_HOSTS: "production:10.11.50.100:16379:0:didi:$CLUSTER_PASSWORD"
    

    Sau deschide UI-ul nativ al cluster-ului dacă există.

  4. Update documentație:

    • agent-v3/INDEX.md: înlocuiește didi-cache:6379 cu 10.11.50.100:16379
    • didiCache/INDEX.md: marchează drept deprecated / legacy
    • project_network_architecture.md: actualizează diagrama

Riscuri reziduale

Risc Probabilitate Mitigare
Containerele nu pot rezolva 10.11.50.100 din docker-network Mică (testat din didi-cache — OK) Test forțat pre-cutover, fallback network_mode: host pe un worker dacă e nevoie
Failover cluster exact în timpul cutover-ului Foarte mică retryStrategy cu 5s max + reconnectOnError — ioredis recuperează
Cluster partajat cu alte proiecte și overwrite-uri accidentale Necunoscut Toate cheile DIDI încep cu didi:* sau agent:*. Recomand confirmare de la admin cluster că DB 0 e dedicat
Parola hardcodată în .env committed în git Umană .gitignore .env + folosește .env.example fără parole
Discrepanță maxmemory pe cluster (noeviction) → OOM la volum mare Mică (13MB actuali) Monitorizare INFO memory săptămânală

Verificare finală post-migrare

Checklist de confirmat înainte de cleanup:

  • DBSIZE pe cluster nou crește în timp (sesiuni acumulate)
  • didi:framework:* prezent (8 chei)
  • didi:config:* prezent (~30 chei)
  • Zero Redis error în logs în ultimele 24h
  • Test analiză text + media + URL — toate completează cu status completed
  • Admin dashboard /admin/framework load-ează normal (citește prin didiFramework → Redis)
  • Failover test: oprește temporar master-ul cluster (dacă e permis) → workers reconectează în < 10s