Volver al blog Técnico

Multi-tenancy real en SaaS: ocho reglas que aprendimos construyendo Brain

Las decisiones de arquitectura que separan un SaaS que aguanta crecimiento de uno que se cae cuando el primer cliente grande lo estresa. Lecciones tras año y medio operando CentiNetOLT en producción con clientes simultáneos.

Equipo Brain 4 minutos de lectura
Multi-tenancy real en SaaS: ocho reglas que aprendimos construyendo Brain

Multi-tenancy es una de esas palabras que se usa con demasiada frecuencia y con muy poca precisión. Mucha gente llama “multi-tenant” a un SaaS que tiene una columna tenant_id en algunas tablas. Eso es solo el principio.

Multi-tenancy real significa que la operación de un cliente no afecta la operación de los demás — ni en datos, ni en performance, ni en disponibilidad. Y eso requiere decisiones a varios niveles: base de datos, capa de aplicación, capa de jobs, capa de red.

Estas son ocho reglas que adoptamos construyendo Brain. No son universales — son lo que aprendimos operando en producción con clientes simultáneos haciendo operaciones intensivas en hardware compartido.

Regla 1: Toda tabla operacional tiene tenant_id como columna obligatoria

Sin excepciones. Hasta las tablas auxiliares (logs, jobs, snapshots, cachés). Si una tabla no tiene tenant_id, no la podes filtrar correctamente, y eventualmente vas a tener un bug donde un cliente ve datos de otro.

La FK debe tener ON DELETE CASCADE con la tabla tenants para que al borrar un cliente desaparezcan todos sus datos sin queries manuales.

Regla 2: Toda query operacional filtra por tenant_id en el WHERE

No confíes en que el ORM lo va a hacer por vos. Validalo. Si tu framework lo hace mediante interceptors o middleware, escribí tests que prueban explícitamente que un query sin filtro de tenant falla o retorna vacío.

El día que falle ese filtro es un incidente de seguridad reportable.

Regla 3: Aislamiento de jobs por tenant con caps independientes

Si tenés una cola de trabajos (sync de OLT, generación de reportes, ingest de datos), no manejes una sola pool global. Crea pools por tenant con límites independientes.

Sin esto: un cliente con 50 OLTs satura tu pool, y los otros 20 clientes ven sus jobs colgados sin razón aparente. Con esto: cada cliente tiene su propio cap (digamos 2-3 jobs concurrentes), y un cliente grande no le come la prioridad a uno chico.

Regla 4: Throttling per-tenant en endpoints sensibles

Rate limiting global está bien para protección contra bots. Pero también necesitás rate limit por tenant, especialmente en endpoints que generan carga (sync manual, exports masivos, queries pesadas). Sin esto, un cliente con un script mal hecho puede degradar la performance para todos.

Regla 5: Pool de conexiones a la base con headroom

El default de muchos ORMs es 10 conexiones por instancia. Eso aguanta a 3-4 clientes haciendo trabajo simultáneo. Cuando llegas a 20 clientes activos, el pool se vuelve el cuello de botella invisible.

Sube el max del pool a algo que de margen real (30-50 según carga), y monitorea métricas de wait time del pool. Si los clientes esperan más de 100ms para obtener una conexión, el pool está chico.

Regla 6: Cifrado de credenciales sensibles en reposo, por tenant

Si almacenas credenciales de equipos del cliente (SSH a OLTs, API keys de terceros), cifrarlas con una llave por tenant. Una sola llave global significa que si alguien la compromete, accede a las credenciales de todos los clientes.

AES-256-GCM con key rotation periódica es el estándar razonable. La llave nunca va al frontend, nunca aparece en logs, nunca se reusa entre tenants.

Regla 7: Defense in depth para provisioning

Cuando un cliente provisiona algo en su tenant (crea una OLT, agrega un usuario, sube un archivo), validá el tenant_id en al menos dos capas: el guard de autorización en el controlador Y el WHERE de la query que persiste.

Parece redundante. Pero el día que cometas un bug en uno, el otro te salva del incidente.

Regla 8: Tests de aislamiento como parte del CI

Escribí tests que explícitamente verifican que el cliente A no puede leer, modificar ni borrar datos del cliente B. Corré esos tests en cada commit. No es opcional.

Sin estos tests, eventualmente alguien va a refactorizar un endpoint y va a romper el aislamiento sin darse cuenta — y vos te vas a enterar cuando un cliente reporte el problema.

Cómo se ven estas reglas en CentiNetOLT

La plataforma cumple las ocho reglas hoy en producción, gestionando clientes simultáneos en infraestructura compartida sin contención cruzada. Los detalles de implementación cambian con el tiempo (movimos jobs de in-process a una cola dedicada, ampliamos el pool dos veces conforme crecimos, agregamos throttling cuando un cliente grande nos forzó a hacerlo), pero las reglas se mantienen.

Si estás construyendo tu propio SaaS multi-tenant, podés usar estas reglas como checklist mínimo. Si estás evaluando un SaaS multi-tenant para usar como cliente, podés preguntarle al proveedor cuáles cumple. La respuesta te dice mucho del nivel de seriedad de la plataforma.

Para llevarte

  • Multi-tenant es una propiedad arquitectónica, no una columna en una tabla.
  • Aislamiento se valida con tests, no con buena voluntad.
  • Las reglas anteriores son mínimos, no aspiracionales.
  • Implementarlas tarde duele mucho más que implementarlas desde el principio.