Skip to content

0063. Redis se configura con una connection string (REDIS_URL), no con host y puerto

Estado

Aceptada

Contexto

Desde permissions-redis-cache, RedisService se construía con { host, port } leídos de REDIS_HOST y REDIS_PORT. Funcionaba perfecto en desarrollo, donde el Redis del docker-compose no lleva password ni TLS.

Al preparar el primer despliegue apareció el problema: ningún Redis gestionado acepta conexiones sin autenticación, y varios (Upstash entre ellos) exigen TLS. Ni la password ni el TLS son expresables en un modelo host/puerto — habría que agregar REDIS_PASSWORD, REDIS_TLS, y probablemente más adelante REDIS_USERNAME y REDIS_DB, replicando a mano lo que el estándar ya resuelve.

Además, la validación de entorno es fail-fast (ADR-0008): con las variables viejas requeridas, el contenedor no arrancaba en absoluto en producción.

Decisión

Una sola variable: REDIS_URL

Se reemplazan REDIS_HOST y REDIS_PORT por REDIS_URL, que ioredis parsea nativamente y que es el formato que todos los proveedores entregan:

redis://localhost:6379                          # dev, sin auth
redis://default:password@host:6379              # gestionado, sin TLS
rediss://default:password@host:6379             # gestionado, con TLS

Conserva un default (redis://localhost:6379) para no romper el .env ni el .env.test de nadie, ni la suite e2e.

REDIS_FAMILY opcional para redes IPv6-only

La red privada de Railway es IPv6-only, y ioredis resuelve DNS en IPv4 por defecto: redis.railway.internal simplemente no resuelve. Se agrega REDIS_FAMILY (opcional, valores 4 o 6) que se pasa a ioredis solo si está definida.

Se deja como variable de entorno y no hardcodeada, porque es una característica de la red donde se despliega, no del código. Upstash, por ejemplo, se accede por URL pública con TLS y no la necesita.

Es el mismo problema IPv4/IPv6 que ya había aparecido dos veces en este proyecto: en el CI del backend y en el bind de los tests e2e (ADR-0040). La tercera vez conviene dejarlo escrito.

La política de fallback no cambia

RedisService mantiene intactos commandTimeout, maxRetriesPerRequest, enableOfflineQueue: false y su retryStrategy. Redis sigue siendo una optimización que nunca tumba la aplicación.

Consecuencias

  • Migrar de proveedor de Redis es cambiar una variable. Es parte de por qué la migración a Upstash descrita en ADR-0062 no requiere tocar código.

  • REDIS_FAMILY mal configurada falla en silencio, y esto es lo más importante de este ADR. Si falta en Railway, ioredis no resuelve el host, RedisService degrada a PostgreSQL según su diseño y solo emite un warning con throttle. La aplicación responde con normalidad, pero:

    • cada request resuelve permisos con una query a la base en vez de leer del cache;
    • la idempotencia del outbox offline deja de funcionar (ADR-0055), porque se apoya en Redis.

    Por eso la verificación post-despliegue exige buscar Conectado a Redis en los logs de forma explícita: es un fallo que no se manifiesta como fallo.

  • Quien clone el repo tras este cambio debe actualizar su .env local. El default hace que no sea urgente en desarrollo.