Referencia: SaaS Multi-inquilino
Referencia compuesta para un SaaS B2B en PostgreSQL 18.4 (AWS RDS), ~8k inquilinos, esquema compartido con tenant_id, 1.2B filas en events. Pila: PgBouncer 1.x pooling de transacciones, Patroni 3.x en copia de DR autogestionada, pgvector 0.8+ no utilizado en este perfil.
Resumen de la Arquitectura
┌─────────────┐
Pods de la app (120) ──►│ PgBouncer │──► Primaria (OLTP)
│ pool 200 │ │
└─────────────┘ ├── réplica asíncrona (API de lectura)
└── réplica retardada (BI, 15 min)| Capa | Elección | Justificación |
|---|---|---|
| Inquilinos | Esquema compartido + tenant_id uuid | Simplicidad operativa hasta 10k inquilinos |
| Aislamiento | Políticas RLS por tabla | Defensa en profundidad contra errores de la app |
| Pooling | Modo transacción de PgBouncer | 120 pods → 200 conexiones de servidor |
| Lecturas | Réplica para API de informes | Primaria protegida de los dashboards |
| Migraciones | Flyway, tren semanal de bajo riesgo | Ventana mensual de alto riesgo |
Esquema y RLS
CREATE TABLE projects (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id uuid NOT NULL,
name text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
CREATE POLICY projects_tenant_isolation ON projects
USING (tenant_id = current_setting('app.tenant_id', true)::uuid);
-- La app establece por solicitud
SET app.tenant_id = '550e8400-e29b-41d4-a716-446655440000';-- Indexar cada ruta de acceso activa con ámbito de inquilino
CREATE INDEX CONCURRENTLY projects_tenant_created_idx
ON projects (tenant_id, created_at DESC);Política de Conexión y Pool
# Extracto de pgbouncer.ini
pool_mode = transaction
default_pool_size = 50
max_client_conn = 2000
server_idle_timeout = 60SET app.tenant_idse ejecuta al inicio de la transacción a través de middleware.- Declaraciones preparadas deshabilitadas en modo transacción (documentado en el README de la app).
max_connectionsen RDS: 500; nunca exponer la primaria sin un pooler.
Cadencia de Replicación y Migración
-- Ejemplo de migración semanal (bajo riesgo)
CREATE INDEX CONCURRENTLY IF NOT EXISTS events_tenant_time_idx
ON events (tenant_id, occurred_at DESC);- DDL de alto riesgo: primer domingo del mes, ventana de las 03:00 UTC.
- Alerta de latencia de réplica: latencia de 256 MB en la réplica OLTP; 2 GB en la réplica de BI.
- Exportación de inquilinos: volcado lógico filtrado por
tenant_idpara salidas empresariales.
Lecciones
- El rendimiento de RLS necesita índices que comiencen con
tenant_id. - Los inquilinos ruidosos están aislados por límites de tasa en la capa de la aplicación; el inquilino más grande en observación.
- El consejo aprueba tablas entre inquilinos (facturación, administración) por separado de los datos del inquilino.
Relacionado
- Inquilinos con Row-Level Security - Patrones RLS
- ID de Inquilino de Esquema Compartido - modelado
- Matemáticas de Conexión - dimensionamiento de pools
- Básicos de Gestión de Cambios - niveles de migración
Versiones de la pila: Esta página fue escrita para PostgreSQL 18.4 (estable 18, mantenimiento 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, y PostGIS 3.5+.