# 08 — Redis Setup

Redis est utilisé dans Xpeditis pour :
1. **Cache des rate quotes** — clés `rate:{origin}:{destination}:{containerType}`, TTL 15 min
2. **Pub/sub WebSocket** — Socket.IO multi-pods nécessite Redis pour broadcaster les notifications

---

## Option A — Upstash (recommandé pour MVP)

### Pourquoi Upstash

- Redis serverless, pay-per-use ($0.2 per 100K commands)
- Free tier : 10 000 commandes/jour, 256 MB (suffisant pour 100 users)
- Compatible avec l'interface Redis standard (ioredis)
- Support TLS natif
- Régions EU disponibles (Frankfurt)
- **Pas de serveur à gérer**

### Setup Upstash

1. Créez un compte sur https://upstash.com
2. **Create Database**
   - Name: `xpeditis-prod`
   - Region: `EU-WEST-1 (Frankfurt)` ← le plus proche de Hetzner FSN1
   - Type: **Regional** (pas Global pour commencer)
   - Eviction: **Allkeys-LRU** (expire les clés les plus anciennes si mémoire pleine)
   - TLS: **Enabled**
3. Copiez les credentials affichés

### Variables d'environnement

```bash
# Upstash fournit une URL Redis complète
REDIS_HOST=your-redis.upstash.io
REDIS_PORT=6379
REDIS_PASSWORD=<upstash_token>
REDIS_DB=0

# OU avec URL (si le code le supporte)
REDIS_URL=redis://:password@your-redis.upstash.io:6379
```

### Vérification de la connexion

```bash
# Test avec redis-cli
redis-cli -h your-redis.upstash.io -p 6379 -a <password> --tls ping
# PONG

# Test de set/get
redis-cli -h your-redis.upstash.io -p 6379 -a <password> --tls \
  SET test:connection "xpeditis-ok" EX 60
redis-cli -h your-redis.upstash.io -p 6379 -a <password> --tls \
  GET test:connection
# "xpeditis-ok"
```

### Configuration dans l'app Xpeditis

Le code NestJS utilise `ioredis`. Vérifiez que TLS est activé dans la config cache :

Dans `apps/backend/src/infrastructure/cache/cache.module.ts`, assurez-vous que la config Redis accepte TLS :

```typescript
// La config doit inclure TLS pour Upstash
const redisOptions = {
  host: configService.get('REDIS_HOST'),
  port: configService.get<number>('REDIS_PORT', 6379),
  password: configService.get('REDIS_PASSWORD'),
  db: configService.get<number>('REDIS_DB', 0),
  // TLS requis pour Upstash
  tls: configService.get('NODE_ENV') === 'production' ? {} : undefined,
};
```

> Si la config actuelle ne supporte pas TLS, ajoutez la variable `REDIS_TLS=true` et adaptez le cache module en conséquence.

---

## Option B — Redis self-hosted dans k3s

### Quand choisir cette option

- 1 000+ users (le free tier Upstash devient limité)
- Besoin de Redis Cluster pour le WebSocket à grande échelle
- Contrôle total des données

### StatefulSet Redis dans Kubernetes

```bash
# Créer le namespace si pas encore fait
kubectl create namespace xpeditis-prod 2>/dev/null || true

# Créer le Secret Redis
cat > /tmp/redis-secret.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
  name: redis-secret
  namespace: xpeditis-prod
type: Opaque
stringData:
  REDIS_PASSWORD: "<MOT_DE_PASSE_FORT_REDIS>"
EOF
kubectl apply -f /tmp/redis-secret.yaml

# Créer la ConfigMap Redis
cat > /tmp/redis-config.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-config
  namespace: xpeditis-prod
data:
  redis.conf: |
    # Sécurité
    requirepass <MOT_DE_PASSE_FORT_REDIS>
    protected-mode yes

    # Persistance (AOF pour durabilité)
    appendonly yes
    appendfsync everysec
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb

    # Mémoire
    maxmemory 512mb
    maxmemory-policy allkeys-lru

    # Réseau
    bind 0.0.0.0
    tcp-backlog 511
    timeout 0
    tcp-keepalive 300

    # Logging
    loglevel notice

    # Performances
    lazyfree-lazy-eviction yes
    lazyfree-lazy-expire yes
EOF
kubectl apply -f /tmp/redis-config.yaml

# Créer le StatefulSet Redis
cat > /tmp/redis-statefulset.yaml << 'EOF'
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
  namespace: xpeditis-prod
spec:
  serviceName: redis-headless
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7-alpine
        ports:
        - containerPort: 6379
          name: redis
        command:
        - redis-server
        - /etc/redis/redis.conf
        env:
        - name: REDIS_PASSWORD
          valueFrom:
            secretKeyRef:
              name: redis-secret
              key: REDIS_PASSWORD
        resources:
          requests:
            cpu: 100m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 512Mi
        volumeMounts:
        - name: redis-config-vol
          mountPath: /etc/redis
        - name: redis-data
          mountPath: /data
        readinessProbe:
          exec:
            command:
            - redis-cli
            - -a
            - $(REDIS_PASSWORD)
            - ping
          initialDelaySeconds: 10
          periodSeconds: 5
        livenessProbe:
          exec:
            command:
            - redis-cli
            - -a
            - $(REDIS_PASSWORD)
            - ping
          initialDelaySeconds: 30
          periodSeconds: 30
      volumes:
      - name: redis-config-vol
        configMap:
          name: redis-config
  volumeClaimTemplates:
  - metadata:
      name: redis-data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: hcloud-volumes
      resources:
        requests:
          storage: 5Gi
---
apiVersion: v1
kind: Service
metadata:
  name: redis-headless
  namespace: xpeditis-prod
spec:
  clusterIP: None
  selector:
    app: redis
  ports:
  - port: 6379
    targetPort: 6379
---
apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: xpeditis-prod
spec:
  selector:
    app: redis
  ports:
  - port: 6379
    targetPort: 6379
  type: ClusterIP
EOF

kubectl apply -f /tmp/redis-statefulset.yaml

# Attendre que Redis soit prêt
kubectl rollout status statefulset/redis -n xpeditis-prod --timeout=120s

# Tester Redis
kubectl exec -it redis-0 -n xpeditis-prod -- redis-cli -a <password> ping
# PONG
```

### Variables d'environnement pour Redis self-hosted

```bash
REDIS_HOST=redis.xpeditis-prod.svc.cluster.local
REDIS_PORT=6379
REDIS_PASSWORD=<MOT_DE_PASSE_FORT_REDIS>
REDIS_DB=0
# Pas de TLS (réseau privé interne k3s)
```

---

## Vérification du cache Redis dans Xpeditis

Après déploiement de l'application, vérifiez que le cache fonctionne :

```bash
# Se connecter à Redis
kubectl exec -it redis-0 -n xpeditis-prod -- redis-cli -a <password>

# Après quelques rate searches depuis l'app :
KEYS rate:*
# 1) "rate:FRNCE:DEHAM:20ft"
# 2) "rate:FRNCE:NLRTM:20ft"
# ...

# Vérifier un TTL (doit être < 900 = 15 min)
TTL "rate:FRNCE:DEHAM:20ft"
# (integer) 647

# Stats globales
INFO stats
# keyspace_hits: 1234
# keyspace_misses: 156
# → Taux de hit = 1234/(1234+156) = 88% ✅
```

---

## Comparaison des options

| Critère | Upstash (Option A) | Self-hosted (Option B) |
|---|---|---|
| **Coût 100 users** | $0 (free tier) | ~€5/mois (stockage) |
| **Coût 1 000 users** | ~$5-10/mois | ~€5-10/mois |
| **Setup** | 5 minutes | 30 minutes |
| **HA** | Automatique | Non (StatefulSet 1 replica) |
| **TLS** | Forcé | Non (cluster interne) |
| **Ops** | Aucun | Monitoring mémoire |
| **Latence** | ~5-10ms (Frankfurt) | <1ms (cluster interne) |

**Recommandation :**
- MVP → **Upstash free tier** (zéro coût, zéro ops)
- 1 000+ users → **Self-hosted dans k3s** (latence minimale, contrôle complet)
