Referência: SaaS Multi-Tenant
Referência composta para um SaaS B2B em PostgreSQL 18.4 (AWS RDS), ~8k tenants, esquema compartilhado com tenant_id, 1.2B linhas em events. Stack: PgBouncer 1.x com pooling transacional, Patroni 3.x em cópia de DR auto-gerenciada, pgvector 0.8+ não utilizado neste perfil.
Visão Geral da Arquitetura
┌─────────────┐
Pods do App (120) ──►│ PgBouncer │──► Primário (OLTP)
│ pool 200 │ │
└─────────────┘ ├── réplica assíncrona (API de leitura)
└── réplica atrasada (BI, 15 min)| Camada | Escolha | Racional |
|---|---|---|
| Tenancy | Esquema compartilhado + tenant_id uuid | Simplicidade operacional para 10k tenants |
| Isolamento | Políticas RLS por tabela | Defesa em profundidade contra bugs do app |
| Pooling | Modo transacional do PgBouncer | 120 pods → 200 conexões de servidor |
| Leituras | Réplica para API de relatórios | Primário protegido de dashboards |
| Migrações | Flyway, trem semanal de baixo risco | Janela mensal de alto risco |
Esquema e 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);
-- App define por requisição
SET app.tenant_id = '550e8400-e29b-41d4-a716-446655440000';-- Indexa todo caminho crítico com escopo de tenant
CREATE INDEX CONCURRENTLY projects_tenant_created_idx
ON projects (tenant_id, created_at DESC);Política de Conexão e Pool
# Trecho do pgbouncer.ini
pool_mode = transaction
default_pool_size = 50
max_client_conn = 2000
server_idle_timeout = 60SET app.tenant_idé executado no início da transação via middleware.- Declarações preparadas desabilitadas em modo transacional (documentado no README do app).
max_connectionsno RDS: 500; nunca exponha o primário sem pooler.
Cadência de Replicação e Migração
-- Exemplo de migração semanal (baixo risco)
CREATE INDEX CONCURRENTLY IF NOT EXISTS events_tenant_time_idx
ON events (tenant_id, occurred_at DESC);- DDL de alto risco: primeiro domingo do mês, janela de 03:00 UTC.
- Alerta de lag da réplica: lag de 256 MB na réplica OLTP; 2 GB na réplica BI.
- Exportação de tenant: dump lógico filtrado por
tenant_idpara saídas de clientes corporativos.
Lições Aprendidas
- O desempenho do RLS requer índices que comecem com
tenant_id. - Tenants "barulhentos" são isolados por limites de taxa na camada do app; o maior tenant está sob observação.
- O conselho aprova tabelas entre tenants (cobrança, administração) separadamente dos dados do tenant.
Relacionados
- Tenancy com Row-Level Security - Padrões RLS
- Tenant ID de Esquema Compartilhado - modelagem
- Matemática de Conexão - dimensionamento de pool
- Básico de Gerenciamento de Mudanças - níveis de migração
Versões da Stack: Esta página foi escrita para PostgreSQL 18.4 (estável 18, manutenção 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, e PostGIS 3.5+.