Escala e Alta Disponibilidade

O que escala e como

ComponenteEscalaObservações
appHorizontalSem estado. Requer Redis além de uma réplica
realtimeHorizontalRequer Redis além de uma réplica (adaptador Socket.IO)
postgresqlVertical + réplicas de leituraO gargalo inevitável
redisVertical / par em HAApenas coordenação; pequeno
cronjobsFixoUma chamada por tick, independente do número de réplicas

Pré-requisitos antes de passar de uma réplica

O Redis precisa estar acessível antes de você aumentar o replicaCount. Os dois modelos de deployment já o incluem por padrão, então isso já está atendido — a menos que você tenha definido redis.enabled: false (Helm) ou removido o serviço redis (Compose) sem fornecer REDIS_URL. Sem Redis, o pub/sub cai para um emissor local ao processo e o adaptador Socket.IO não tem transporte entre pods — o realtime registra uma linha na inicialização indicando modo de pod único e depois descarta os eventos entre pods silenciosamente. Veja Redis.

Você também precisa de armazenamento de objetos compartilhado — o armazenamento em disco local é por pod, então um arquivo enviado por uma réplica fica invisível para as outras. Veja Armazenamento de Objetos.

Escalando o app

app:
  replicaCount: 3
  resources:
    limits:
      memory: 8Gi
      cpu: 2000m
    requests:
      memory: 4Gi
      cpu: 1000m

A restrição é memória, não CPU. As execuções de workflow rodam dentro do processo do app em sandboxes isolated-vm, e o parsing de arquivos acontece em memória. A telemetria de produção mostra 4–8 GB constantes, com picos de até 12 GB sob carga alta de execução. Se você provisionar memória de menos, terá OOMKills que encerram execuções de workflow em andamento.

Um PodDisruptionBudget é criado automaticamente quando replicaCount > 1 (maxUnavailable: 25%). Aperte-o com podDisruptionBudget.minAvailable se precisar.

Autoscaling

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70
  targetMemoryUtilizationPercentage: 80

Requer o metrics-server. Quando ativado, o chart omite spec.replicas para que o HPA assuma o controle do número de réplicas.

A redução de escala encerra pods que podem estar executando workflows. Defina um minReplicas conservador e considere um bloco behavior com um stabilizationWindowSeconds longo na redução, para que execuções longas não sejam interrompidas repetidamente.

O realtime recebe o mesmo HPA, a menos que você o desative — e, de novo, só escale além de uma réplica com o Redis configurado:

autoscaling:
  realtime:
    enabled: false

Banco de dados

O Postgres é onde escalar deixa de ser uma questão de réplicas.

Conexões

Cada réplica do app abre um pool. O total de conexões cresce com o número de réplicas, e o Postgres tem um max_connections rígido. Um deployment que funciona com 2 réplicas pode esgotar as conexões com 6.

Faça a conta: réplicas × tamanho do pool + realtime + cronjobs + migrations + folga precisa ficar abaixo de max_connections.

Para qualquer coisa acima de algumas poucas réplicas, coloque o PgBouncer em modo de transaction pooling na frente do banco e aponte DATABASE_URL para ele. Essa é a mudança de maior impacto em um deployment grande — ela desacopla o número de réplicas do app do número de conexões com o banco.

Réplicas de leitura

Caminhos de leitura pesados — listagem de logs, logs de auditoria, agregações de dashboard — podem ser transferidos:

DATABASE_REPLICA_URL=postgresql://user:pass@replica-host:5432/studio

As leituras voltam para o primário quando a variável não está definida. Existem overrides por papel, caso componentes diferentes devam usar réplicas diferentes:

VariávelAplica-se a
DATABASE_REPLICA_URLPadrão para todos os papéis
DATABASE_REPLICA_URL_WEBO app web
DATABASE_REPLICA_URL_REALTIMEO serviço de realtime
DATABASE_REPLICA_URL_TRIGGERWorkers do Trigger.dev

Réplicas têm atraso. O Studio roteia para elas apenas leituras tolerantes a latência, mas se a sua réplica ficar muito atrasada, logs recém-gravados podem não aparecer por um instante. Monitore o atraso de replicação.

Dimensionamento

DeploymentInstânciaArmazenamento
Pequeno (1–5 pessoas)2 vCPU / 8 GB50 GB
Padrão (5–50 pessoas)4 vCPU / 16 GB100 GB+
Grande (50+ pessoas)8+ vCPU / 32 GB+250 GB+, com crescimento automático

Os embeddings da Knowledge Base são o principal motor de crescimento — o armazenamento vetorial escala com o volume de documentos, não com o número de pessoas. Ative o aumento automático de armazenamento.

Concorrência de execução

SCHEDULE_EXECUTION_CONCURRENCY_LIMIT (padrão 30) limita as execuções agendadas por instância do app. As outras três variáveis *_EXECUTION_CONCURRENCY_LIMIT valem apenas para o Trigger.dev e são inertes em uma auto-hospedagem padrão — veja Jobs em Background.

Quando as execuções ficam na fila mas a memória está tranquila, aumente o limite; quando a memória é o teto, adicione réplicas em vez disso.

Rate limits e cotas

Deployments auto-hospedados rodam sem limites de plano por padrão — sem rate limits, sem timeouts de execução, sem tetos de Tables e armazenamento. Cada um pode ser reativado individualmente; a lista de variáveis e os valores sugeridos estão em Variáveis de Ambiente.

Vale definir um timeout de execução mesmo em um deployment sem outros limites — é ele que impede um workflow descontrolado de segurar um sandbox indefinidamente.

Topologia de referência

Um deployment de produção atendendo cerca de 100 pessoas ativas:

app:
  replicaCount: 3
  resources:
    limits: { memory: 8Gi, cpu: 2000m }
    requests: { memory: 4Gi, cpu: 1000m }
  env:
    REDIS_URL: "rediss://:<password>@redis.internal:6380"

realtime:
  replicaCount: 2
  env:
    REDIS_URL: "rediss://:<password>@redis.internal:6380"

postgresql:
  enabled: false

externalDatabase:
  enabled: true
  host: "pgbouncer.internal"
  port: 6432
  database: studio
  sslMode: require

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 10

podDisruptionBudget:
  minAvailable: 2

Além disso: Postgres gerenciado com PITR, Redis gerenciado em um tier de HA, armazenamento de objetos com versionamento e imagens fixadas em uma tag explícita.

Common Questions

Escale horizontalmente para pessoas simultâneas e throughput de workflows. Escale verticalmente quando execuções individuais consomem muita memória — um único parsing de documento grande ou uma execução pesada precisa caber em um pod. A memória é quase sempre a restrição determinante.
Quando o número de réplicas vezes o tamanho do pool se aproximar do max_connections do Postgres — normalmente acima de algumas poucas réplicas. O transaction pooling desacopla as réplicas do app das conexões com o banco e é a mudança de maior impacto em um deployment grande.
A redução de escala pode encerrar um pod no meio de uma execução. Use um minReplicas conservador e uma janela de estabilização longa na redução, para que execuções demoradas não sejam interrompidas repetidamente.
Sim. Defina DATABASE_REPLICA_URL e as leituras tolerantes a latência — listagem de logs, logs de auditoria, agregações de dashboard — passam a ser roteadas para ela. Existem overrides por papel para os componentes web, realtime e trigger.