livrare lot 2

This commit is contained in:
EVOTECH IT SRL 2026-07-10 03:39:53 -07:00
commit 8ecc78e729
763 changed files with 164593 additions and 0 deletions

View file

@ -0,0 +1,291 @@
# 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):
```bash
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):
```bash
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**:
```bash
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):
```bash
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` repopuleze framework config
- Sesiuni async în zbor (`didi:queue:session:*`) — se pot pierde. Inventariere:
```bash
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)
```bash
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
```bash
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ă.
```bash
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
```bash
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
```bash
cd /home/admin365/didi_mono/backend/services/orchestration-layer/agent-v3
docker compose up -d
```
### 7. Smoke test
```bash
# 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
```bash
# 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**:
```bash
# 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**:
```bash
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:
```bash
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`
```bash
# 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.yml` — **nu ș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`**:
```bash
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`:
```yaml
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