Skip to content

ADR 0053 — Project OCTOPUS Cell-Based Hyperscale Architecture

  • Status: Accepted
  • Date: 2026-05-14
  • Deciders: André Luiz Gallon (Architect/Operator)
  • Supersedes: —
  • Related: ADR 0043 (OOBI VLAN no-VXLAN), ADR 0048 (ZTP-Prem 12-layer), ADR 0054 (PQC-Everywhere), ADR 0055 (Admin Console), ADR 0056 (Auto-Provisioning)
  • Memory: project_octopus_hyperscale_admin_pqc_locked_2026_05_14.md

Context

Project OCTOPUS é o sub-projeto multi-site dentro de TLSStress.Art, materializado em pkg/octopus/ (PR #753). Os dois serviços cloud-side (CONNECT.Art rendezvous + STUN-coord signaling) precisam ser projetados desde o início para escalar de dezenas de clientes (MVP) a centenas de milhares sem refactor doloroso.

Sistemas SaaS que tentam escalar monoliticamente acabam pagando dívida arquitetural catastrófica:

  • Stripe levou ~2 anos migrando de monólito Ruby/Mongo para shards Razorpay-style
  • Slack reescreveu para Vitess depois de hot-spots irrecuperáveis no MySQL master único
  • Snowflake projetou cells desde o dia 1 e nunca precisou pause-the-world migration
  • Cloudflare opera cells regionais isoladas ("colos") com APIs cell-aware

Decisões iniciais — formato de IDs, sharding keys, namespaces de banco, separação control-plane vs data-plane — condicionam toda a evolução futura. Refactor depois é caro; design correto desde o início é barato.

Decisão

O OCTOPUS adota arquitetura cell-based desde o Wave-0, mas com implementação progressiva disparada por gates de adoção.

Conceitos canônicos

  • Cell: unidade de isolamento contendo até 10 000 clientes com seu próprio stack OCTOPUS completo (CONNECT.Art + STUN-coord + state store + observability). Crash de uma cell NÃO afeta outras cells. Cells são identificadas por cell_id (formato <region>-<n>, ex.: us-east-1-a, eu-west-1-b).
  • Region: agregado geográfico de 1+ cells servindo clientes daquela área. Cada region tem edge anycast próprio (Cloudflare Magic Transit / AWS Global Accelerator / Azure Front Door).
  • Control Plane: serviços globais que servem TODAS as cells (Admin Console, Provisioning Orchestrator, Token Marketplace, Vault PKI root, audit chain). Multi-region active-active.
  • Data Plane: tráfego de clientes através de CONNECT.Art / STUN-coord. Per-cell, cell-isolated.

Sharding key

deployment_id é a única sharding key. Consistent hashing distribui deployment_id across cells. Rebalance hot-spot-aware (cell saturada deflagra split).

Schemas / IDs cell-ready desde dia 1 (mesmo em single-cell)

  • Todo DeploymentID ULID encoded para sort-by-time + uniqueness sem coordenação
  • Todos os Postgres schemas têm coluna cell_id na chave primária (NULL aceito em Wave-1; obrigatório em Wave-3+)
  • APIs cloud emitem header X-Octopus-Cell em toda response — observability cell-aware desde o dia 1
  • Métricas Prometheus carregam label cell_id (cardinality já controlada)
  • Audit chain Merkle tem branch per-cell, root global

Gates de evolução por wave

Wave Trigger entry Arquitetura Estimativa
1 scaffold (#753 merge) Single region, single cell, DynamoDB regional, schemas cell-ready shipping now
2 500+ clientes ativos OU p99 > 100ms 1-region Multi region (3-5 cells single-per-region), DynamoDB Global Tables, GeoIP routing +3-6 meses
3 5 000+ clientes OU uma cell > 60% saturada Multi cell per region, NATS JetStream event bus, Cloudflare Workers + Anycast edge +3-6 meses
4 50 000+ clientes OU > 3 cells/region saturadas FoundationDB ou Spanner global, ClickHouse petabyte analytics, OpenTelemetry single fabric +6-9 meses

Cada wave é incremental sobre a anterior; nunca rewrite from scratch. Investidor cobra plano de scale antes de Series A — este ADR é a resposta.

Alternativas consideradas

Alternativa Por que rejeitada
Monolítico até dor Pago em refactor depois (2+ anos custo). Risco de blow-up sob tração. Não vendável para Series A.
Cells desde Wave-1 6× código + complexidade operacional ANTES de ter clientes. Premature operationalization.
Sharding por customer_id em vez de deployment_id customer_id é unidade de billing/admin; deployment_id é unidade de tráfego cloud. Tráfego é o que satura — shardar pelo eixo que satura.
Per-cliente isolation (1 cell por cliente Enterprise) Reservado para tier "Dedicated Cell" (premium SKU futuro). Default é shared-cell até 10k/cell.

Consequências

Positivas

  • Blast radius limitado a 1 cell (até 10k clientes afetados, nunca todos)
  • Capacity planning por cell: previsível, modelável
  • Tier "Dedicated Cell" virá vender-se sozinho para Enterprise (compliance, isolation guarantees)
  • Cells de carga distintas (free/pro/enterprise) podem ter SKUs separados de hardware
  • Multi-cloud failover: se AWS us-east-1 cai, traffic GeoIP-encaminhado para GCP us-east1 cell hot-standby

Negativas

  • Cross-cell features (ex.: customer com deployments em 2 regiões) precisam de coordinator no Control Plane (added complexity)
  • Operations team precisa de "cell awareness" — runbooks, on-call rotation per-cell
  • Audit chain Merkle root global tem latência de finalização (consenso multi-cell)

Neutras

  • cell_id aparece em todos os logs, métricas, schemas — overhead de tags desde o dia 1

Implementação Wave-1 (do scaffold atual ao gate de Wave-2)

PRs antecipados:

  • PR-OCTOPUS-3: cell-aware IDs/schemas em pkg/octopus/common/types/ — ULID, cell_id em todos DTOs (default default-1), header X-Octopus-Cell
  • PR-OCTOPUS-7: HPA + VPA + Karpenter manifests por cell
  • PR-OCTOPUS-8: Multi-region failover Route53 + Cloud DNS + Azure Traffic Manager
  • PR-OCTOPUS-10: SLO dashboards + error budgets cell-aware

Quando 500+ clientes ativos ou latência p99 > 100ms em uma região: triggar Wave-2.

Implementação Wave-1 — substrato de tenant do cell-0 (2026-06-13)

Esta seção detalha a mecânica concreta de isolamento por tenant do primeiro cell real (Hetzner, hel1-0), que o Provisioning Orchestrator (ADR 0056) materializa nas activities AllocateCell / ProvisionTenant / ReserveConnectArtSlot / ReserveStunCoordSlot (antes fail-closed). É a realização Wave-1 (single-cell) das decisões estratégicas acima — incremental, nunca rewrite.

Identidade do cell

  • cell_id = "hel1-0" (formato <region>-<n>, valida em common/types.CellID). AllocateCell(country) em Wave-1 retorna sempre o cell único (não há GeoIP routing até Wave-2); a assinatura por country é preservada para o roteamento futuro. O ADR mantém default-1 como fallback de dev.

Cell control DB (Postgres) — plano de controle do cell

Um Postgres no cell (database cell) hospeda o registro de controle, separado do Postgres do Temporal (concern distinto). Schema (migração 0001_cell_control.sql):

tenants(
  deployment_id   text primary key,      -- sharding key (ULID/dpl_*)
  cell_id         text not null,
  customer_email  text,
  status          text not null,         -- provisioning|provisioned|rollback_pending|released
  created_at      timestamptz default now(),
  updated_at      timestamptz default now()
)
slots(
  deployment_id   text not null,
  kind            text not null,         -- 'connect-art' | 'stun-coord'
  cell_id         text not null,
  reserved_at     timestamptz default now(),
  released_at     timestamptz,           -- NULL = ativo
  primary key (deployment_id, kind)
)
-- tabela de dados de exemplo com RLS por tenant (modelo p/ as tabelas de tráfego):
tenant_data(
  deployment_id text not null,
  ...,
  -- RLS: policy USING (deployment_id = current_setting('octopus.tenant', true))
)

Todas as tabelas carregam cell_id na chave (NULL aceito em Wave-1, obrigatório em Wave-3+) conforme a decisão "schemas cell-ready desde o dia 1".

Isolamento: Postgres RLS (shared-schema, não schema-per-tenant)

Wave-1 usa Row-Level Security em schema compartilhado (escala a 10k tenants/cell sem 10k schemas). As tabelas de dados de tráfego têm ENABLE ROW LEVEL SECURITY + policy USING (deployment_id = current_setting('octopus.tenant', true)). A conexão de runtime de cada deployment seta SET octopus.tenant = '<deployment_id>' (ou via um role por-tenant com SET ROLE), de modo que um tenant nunca lê linhas de outro. ProvisionTenant não cria schema — apenas registra o tenant (a policy é template, criada na migração) e prepara seu contexto RLS.

Redis namespace

Convenção de namespacing por prefixo de chave: octopus:{cell_id}:{deployment_id}:*. ProvisionTenant não cria nada no Redis (namespacing é lógico via prefixo); o runtime do tenant é obrigado ao prefixo pelo seu token/config. (Redis ACL por-tenant é Wave-2+.)

Contrato de admissão CONNECT.Art / STUN-coord (o que é um "slot")

CONNECT.Art (rendezvous, Pattern A, ADR 0050) e STUN-coord (signaling, Pattern B, ADR 0051) são data-plane. Um slot NÃO é um recurso de runtime escasso — é a entrada de admissão que autoriza um deployment_id a usar aquele serviço no cell. Reserve{ConnectArt,StunCoord}Slot faz UPSERT em slots; os serviços de data-plane consultam slots (ou um cache dele) para admitir/rejeitar um deployment_id apresentando seu cert (emitido via cert-manager, ADR 0053-PKI/H4). Assim a reserva é uma escrita de controle real e idempotente, e o enforcement de runtime é uma leitura do mesmo registro — sem acoplar o provisioner ao runtime.

Mapeamento activity → operação (idempotente, transacional)

Activity Operação no cell control DB
AllocateCell retorna hel1-0 (validado); sem escrita
ProvisionTenant INSERT tenants ... ON CONFLICT (deployment_id) DO UPDATE status='provisioned'
ReserveConnectArtSlot INSERT slots(deployment_id,'connect-art') ON CONFLICT DO UPDATE released_at=NULL
ReserveStunCoordSlot idem, kind='stun-coord'
ReleaseConnectArtSlot (comp.) UPDATE slots SET released_at=now() WHERE deployment_id,kind
ReleaseStunCoordSlot (comp.) idem
MarkTenantRollbackPending (comp.) UPDATE tenants SET status='rollback_pending'
ReleaseCellAllocation (comp.) UPDATE tenants SET status='released'

Toda activity é idempotente (UPSERT por chave determinística), satisfazendo o contrato de retry do Temporal (ADR 0056). O idempotencyKey derivado de (WorkflowID, ActivityName, Attempt) é gravado para auditoria.

Fronteiras (o que é Wave-1 vs adiado)

  • Wave-1 (este H5): cell control DB + RLS shared-schema + slots como admissão + activities reais idempotentes. O saga de onboarding completa fim-a-fim no cell.
  • Adiado (Wave-2+): registro de runtime ao vivo no CONNECT.Art/STUN-coord (hoje a admissão é por leitura do slots), Redis ACL por-tenant, GeoIP cell routing, multi-cell rebalance, schema-per-tenant para tier "Dedicated Cell".

Postura de custo

O cell control DB reaproveita a instância Postgres já presente no cell (database cell separado), evitando custo incremental. Tudo roda no único VPS Hetzner do cell-0 (~$12-15/mo, off-AWS) — ver project_f2_onboarding_wiring_2026_06_13.

Patent claim potencial

Family E — Cell-Based Hyperscale Federation with Per-Tenant Routing Locality: combinação de (1) sharding por deployment_id ULID, (2) GeoIP cell routing, (3) cross-cell coordinator com Merkle audit chain global, (4) cell-as-tier SKU. Provisional draft Q4 2026 se Pattern A + B + C provisional drafts já filed em Q3 2026.