6. Design no Playground
Saber o que é um cache, uma fila ou um circuit breaker não é o mesmo que saber de quais um sistema precisa, onde e quantos. Neste exercício você recebe três designs que falham sob carga no System Design Playground — uma venda relâmpago, uma cascata causada por uma dependência lenta e um assistente de IA lento e caro demais — e precisa fazer cada um atingir suas metas dentro do orçamento, explicar por que o seu design funciona com as fórmulas do curso e mostrar que ele continua funcionando quando os dados são lançados de novo.
Objetivos de aprendizagem
Ao final deste exercício você será capaz de:
- Diagnosticar um design que falha a partir das métricas e do relatório de teste (qual meta falha, qual nó é o gargalo, por quê).
- Compor componentes da paleta e ajustar seus parâmetros para atingir metas de latência, erros, disponibilidade e custo.
- Justificar números de réplicas, timeouts e tamanhos de cache com contas de utilização, Lei de Little, taxa de acerto e custo.
- Avaliar robustez: distinguir um design que passa por sorte de um que passa sob falhas aleatórias.
- Comparar alternativas e defender um trade-off entre custo e confiabilidade.
Contexto
O playground simula um design sob um perfil de carga (o mesmo vocabulário do k6: smoke, load, stress, spike, soak, breakpoint, chaos). O que você precisa para este exercício (o manual completo está na página do playground, e as lições de cada cenário no guia de cenários):
- Cenário — um design inicial mais um enunciado, um perfil de carga e metas (por exemplo, p99 abaixo de um limite, taxa de erro, disponibilidade, atraso do consumidor), incluindo uma meta de custo (US$/mês, preços marcados como ilustrativos). O enunciado e as metas aparecem no inspetor quando nada está selecionado.
- Seed — todo número aleatório (chegadas, latências, falhas) vem de um único gerador com semente. Mesma seed + mesmo design + mesmo perfil ⇒ execução idêntica. A seed fica na barra de ferramentas; o botão 🎲 sorteia uma nova.
- Nível de caos —
off,low,medium,high: com que frequência acontecem eventos aleatórios (queda de réplicas, degradação de latência de uma dependência, picos de tráfego, chaves quentes, cache esvaziado, queda de zona, queda de terceiros, deploy ruim…). - Relatório — quando uma execução termina, a aba Report mostra o perfil, a seed, a duração, p50/p95/p99/p999, taxa de erro, disponibilidade, custo, o gargalo (o primeiro nó a saturar, com uma explicação), os eventos que aconteceram e passou/falhou para cada meta.
- Exportar / importar JSON — a barra de ferramentas salva o design atual como um arquivo JSON e o carrega de volta. Há também um link de compartilhamento (o design codificado na URL depois de
#sdp=).
Projete no playground completo (paleta + inspetor). As incorporações desta página são compactas: mostram o cenário e o executam, mas não permitem acrescentar componentes.
Tarefa
Parte A — Três desafios
Para cada cenário abaixo, parta do cenário como ele é carregado (não mude o perfil, a carga-alvo, a proporção de leituras nem as metas) e mude o design até que todas as metas passem com a seed padrão do cenário e o caos definido pelo cenário.
Desafio 1 — flash-sale
Uma venda relâmpago com estoque limitado (a abertura das 10:00 do BoxOffice em forma genérica): um pico de usuários atinge o catálogo e o inventário, e os pagamentos vão para um provedor externo.
Experimente: aperte ▶ e observe qual nó fica vermelho primeiro, e quando. Veja o relatório: qual meta falha, e o que diz a explicação do gargalo? Depois pergunte: quantas requisições por segundo o banco do inventário realmente aceita, e quem decide quantos usuários chegam até ele?
Pontos para pensar (não é uma receita): o que pode ser servido sem tocar no inventário (conteúdo estático, o catálogo, a disponibilidade de estoque que pode estar alguns segundos desatualizada); que taxa de admissão o inventário sustenta (utilização abaixo de ~0,7); o que acontece com os usuários ainda não admitidos (uma sala de espera é uma fila, e uma fila tem backlog); onde vai parar a latência do provedor de pagamento.
Desafio 2 — cascading-failure
Uma dependência de terceiros fica lenta; sem timeouts, as threads se acumulam e um serviço saudável cai.
Experimente: execute e observe a utilização dos servidores de aplicação e das suas threads. A CPU está baixa, mas as requisições falham — por quê? Aplique a Lei de Little: quanto vale L = λ × W quando W passa de 150 ms para 3 s?
Pontos para pensar: um valor de timeout derivado do p99 normal da dependência (não um chute); o que um circuit breaker traz e quanto custa; se um fallback (resposta degradada) é aceitável para essa chamada; se retentativas ajudam ou pioram (veja retry-storm).
Desafio 3 — ai-assistant
O assistente de suporte da BoxOffice: um orquestrador chama um LLM através de um gateway, recupera contexto de uma memória (RAG) e chama ferramentas. Ele é lento e caro.
Experimente: execute e abra o indicador de custo. Para onde vai o dinheiro? Quantas chamadas sequenciais ao LLM uma requisição de usuário faz, e o que isso causa na latência?
Pontos para pensar: o número de passos do agente (cada passo é uma chamada sequencial ao LLM); um cache semântico no gateway de LLM (que taxa de acerto é realista para perguntas de suporte?); rotear perguntas simples para um modelo mais barato; onde fica a safety mesh no caminho; tokens × preço = custo por requisição.
Parte B — Robustez com aleatoriedade
Um design que passa com uma seed pode passar por sorte. Execute o seu design final de cada desafio com 5 seeds e nível de caos medium, mantendo todo o resto como na Parte A:
- As seeds são publicadas pelo professor no Blackboard. Se não forem publicadas, use os cinco últimos dígitos da sua matrícula
Se as seedsS, S+1, S+2, S+3, S+4. - Um desafio é robusto quando todas as metas passam em pelo menos 4 das 5 seeds.
- Para cada seed que falhar, leia o log de eventos e explique o que aconteceu (qual evento aleatório, qual meta, por que o seu design não o absorveu).
A incorporação abaixo mostra o mesmo cenário com uma seed fixa e caos médio — as configurações que você vai usar no playground completo:
A física não muda
Você muda a arquitetura, não as leis da natureza. A menos que você justifique no texto (e a justificativa seja aceita), não edite:
- capacidade por réplica, tempo de serviço (
latencyMs) e variabilidade (sigma) de qualquer tipo de componente; - preços, e a latência, a cota (
rateLimit) ou o comportamento de queda dos componentes externos (payment,thirdparty,email) e do LLM; - as metas, o perfil, o RPS-alvo e a proporção de leituras do cenário.
Taxas de acerto de cache e CDN acima de 0,95, e de cache semântico acima de 0,4, precisam de justificativa baseada na carga (popularidade das chaves, repetição das perguntas).
O botão Show a possible solution
Cada cenário tem uma solução de referência. Você pode carregá-la e estudá-la. Uma entrega idêntica (ou quase idêntica) à solução de referência fica limitada a C. Para chegar a B ou A, o design precisa ser seu e ser mais barato que a referência com as mesmas metas ou mais robusto (passa em mais seeds em medium, ou passa em high), e você precisa explicar por quê.
Requisitos
- (obrigatório) Para cada um dos três desafios, o JSON exportado é importado no playground e, com a seed padrão, todas as metas passam (inclusive a de custo).
- (obrigatório) As regras da física acima são respeitadas (ou cada exceção é justificada).
- (obrigatório) Parte B: cada desafio passa em ≥ 4 de 5 seeds com caos
medium; uma tabela mostra os resultados seed × meta. - Para cada desafio, o relatório inicial (falhando) e o final (passando), ambos mostrando a seed.
- Uma justificativa por desafio com: diagnóstico, mudanças numeradas com o efeito medido (registro de iterações), as contas que justificam réplicas / timeouts / cache (utilização, Lei de Little, taxa de acerto, custo) e pelo menos uma alternativa testada ou descartada.
- Para cada seed que falhou na Parte B, uma explicação a partir do log de eventos.
- Uma comparação com a solução de referência (custo, robustez, estrutura).
- Seção Uso de IA (veja a política).
Entregáveis
No seu repositório (tag ex6):
playground/
├── flash-sale.json
├── cascading-failure.json
├── ai-assistant.json
└── reports/
├── flash-sale-initial.png
├── flash-sale-final-seed<seed>.png
├── flash-sale-medium-seed<seed>.png # one per seed of Part B
└── ... # same for the other two scenarios
Os relatórios podem ser capturas de tela (.png) ou o texto do relatório copiado para um arquivo .txt — o que importa é que a seed, o perfil, o nível de caos e os resultados das metas estejam visíveis. No site da sua documentação, uma página playground-design com uma seção por desafio e a tabela da Parte B. Links de compartilhamento (#sdp=…) ao lado de cada design são um extra bem-vindo.
Passo a passo
- Leia o enunciado e as metas. Anote-os na sua página: meta, limite e o orçamento.
- Execute o cenário como ele está e guarde o relatório inicial. Anote a primeira meta que falha e o gargalo apontado.
- Formule uma hipótese com números. Exemplo: "o BD do inventário recebe 4.000 escritas/s e sua capacidade é 2.500 escritas/s, então ρ = 1,6: não funciona com latência nenhuma." Ou: "W passou de 0,15 s para 3 s, então cada réplica precisa de 20× mais threads."
- Mude uma coisa de cada vez e execute de novo com a mesma seed. Mantenha um registro de iterações: mudança → efeito na meta que falhava → efeito no custo. Como a execução é determinística, uma diferença entre duas execuções é causada pela sua mudança.
- Pare quando todas as metas passarem e então tente cortar custo: menos réplicas, componentes menores, autoscaling em vez de capacidade fixa. Cada corte precisa manter todas as metas passando.
- Exporte o JSON, importe-o de volta em uma página nova e execute de novo para conferir que o relatório é o mesmo.
- Parte B: coloque o caos em
medium, execute as 5 seeds, preencha a tabela, leia o log de eventos de cada falha. Se menos de 4 passarem, melhore o design (redundância, autoscaling, timeouts, fallbacks) e refaça a Parte A — o design final precisa passar nas duas partes. - Compare com a solução de referência e escreva a justificativa.
Conta resolvida — dimensionando um nó
Suponha que o relatório mostre que o nó sql recebe 1.800 req/s e uma réplica atende 1.000 req/s. Com utilização-alvo de 0,7, réplicas = ⌈1.800 ÷ (1.000 × 0,7)⌉ = 3. Se um cache na frente dele tiver taxa de acerto h = 0,9, o banco recebe (1 − 0,9) × 1.800 = 180 req/s e uma réplica basta (ρ = 0,18) — se o cache compensa passa a ser uma questão de custo e da consistência que você pode aceitar.
Templates
## Challenge: <scenario id>
**Goals (from the brief):** p99 < ... ms · error rate < ...% · ... · cost < US$ .../month
### 1. Diagnosis (initial run, seed <seed>)
- Failing goals: ...
- Reported bottleneck: <node> — <explanation>
- My reading of the numbers: λ = ... req/s, μ = ... req/s, ρ = ...; L = λ × W = ...
### 2. Iteration log
| # | Change | Failing goal before → after | Cost before → after | Why it worked (or not) |
|-|-|-|-|-|
| 1 | | | | |
| 2 | | | | |
### 3. Final design
- Diagram: (screenshot or `export Mermaid`)
- Final report (seed <seed>): all goals pass — p99 ..., errors ..., availability ..., cost ...
- Sizing arithmetic: ...
### 4. Alternatives
| Alternative | Result | Why rejected |
|-|-|-|
### 5. Robustness (chaos = medium)
| Seed | Goal 1 | Goal 2 | Goal 3 | Cost | Pass? | Events that hurt |
|-|-|-|-|-|-|-|
| | | | | | | |
Passed in .../5 seeds. Explanation of each failure: ...
### 6. Compared with the reference solution
Cost: ... vs ... · Robustness: ... vs ... · Main structural difference: ...
## Desafio: <id do cenário>
**Metas (do enunciado):** p99 < ... ms · taxa de erro < ...% · ... · custo < US$ .../mês
### 1. Diagnóstico (execução inicial, seed <seed>)
- Metas que falham: ...
- Gargalo apontado: <nó> — <explicação>
- Minha leitura dos números: λ = ... req/s, μ = ... req/s, ρ = ...; L = λ × W = ...
### 2. Registro de iterações
| # | Mudança | Meta que falhava antes → depois | Custo antes → depois | Por que funcionou (ou não) |
|-|-|-|-|-|
| 1 | | | | |
| 2 | | | | |
### 3. Design final
- Diagrama: (captura de tela ou `export Mermaid`)
- Relatório final (seed <seed>): todas as metas passam — p99 ..., erros ..., disponibilidade ..., custo ...
- Contas de dimensionamento: ...
### 4. Alternativas
| Alternativa | Resultado | Por que foi descartada |
|-|-|-|
### 5. Robustez (chaos = medium)
| Seed | Meta 1 | Meta 2 | Meta 3 | Custo | Passou? | Eventos que atrapalharam |
|-|-|-|-|-|-|-|
| | | | | | | |
Passou em .../5 seeds. Explicação de cada falha: ...
### 6. Comparação com a solução de referência
Custo: ... vs ... · Robustez: ... vs ... · Principal diferença estrutural: ...
Rubrica
Níveis conforme definidos em Como os exercícios são avaliados. O professor reimporta seus arquivos JSON e os executa de novo com as seeds que você informar.
| Critério | I | D | C | B | A |
|---|---|---|---|---|---|
| Metas com a seed padrão (obrigatório) | Não entregue, ou o JSON não importa | Uma meta falha em pelo menos um desafio | Todas as metas passam nos três desafios | — | — |
| Regras da física (obrigatório) | Metas atingidas editando a física | Edições sem justificativa | Respeitadas | — | Exceção justificada e bem argumentada |
| Robustez (Parte B) (obrigatório) | Não feita | < 4/5 seeds em algum desafio | ≥ 4/5 seeds em todos os desafios | Cada falha explicada pelo log de eventos | 5/5 em tudo, ou passa em ≥ 4/5 com high |
| Eficiência de custo | — | Acima do orçamento | Dentro do orçamento | Claramente abaixo do orçamento, cortes justificados | Mais barato que a solução de referência com as mesmas metas |
| Justificativa com números | — | Só narrativa | Diagnóstico + registro de iterações | Contas de utilização / Lei de Little / taxa de acerto / custo justificam o dimensionamento | Prevê um resultado antes de executar e confere a previsão |
| Originalidade e alternativas | Copiado | Idêntico à referência (limite C) | Uma alternativa discutida | Design próprio, alternativas comparadas | Uma percepção que a solução de referência não tem |
FAQ
Posso mudar o perfil de carga, o RPS-alvo ou as metas?
Não. Eles definem o problema. Você muda apenas o design: componentes, conexões e seus parâmetros (dentro das regras da física).
Meu design passa com a seed 42 mas falha com a 43. O simulador está com defeito?
Provavelmente não. Seeds diferentes significam chegadas, latências e eventos aleatórios diferentes. Veja o log de eventos da execução que falhou: uma réplica caiu e só sobrou uma, uma chave quente, uma dependência lenta sem timeout… É exatamente disso que trata a Parte B.
Se eu executar a mesma seed duas vezes, recebo o mesmo relatório?
Sim — mesma seed, mesmo design, mesmo perfil e mesmo nível de caos dão uma execução idêntica. É isso que dá sentido ao seu registro de iterações e permite ao professor reproduzir seus resultados. Se aparecer uma diferença, confira se o design é mesmo o mesmo (importe o JSON de novo).
Posso simplesmente subir a taxa de acerto do cache para 0,99?
Não sem um motivo. A taxa de acerto depende da carga (o quão concentrada é a popularidade das chaves, com que frequência os dados mudam). Acima de 0,95 para caches e CDNs, ou acima de 0,4 para um cache semântico, você precisa justificar — caso contrário é editar a física.
Onde fica salvo o meu design? Fechei a aba.
O playground salva automaticamente o design atual de cada cenário no seu navegador, mas o armazenamento do navegador pode ser apagado. Exporte o JSON com frequência e faça commit dos arquivos.
Acho que o simulador tem um bug.
Envie ao professor o JSON, a seed, o perfil e o nível de caos, e descreva o que você esperava e o que viu. Se for um bug, o exercício será ajustado para todos.
Posso usar IA para encontrar um design?
Você pode discutir ideias com uma IA e deve declarar o uso. Mas a verificação oral vai pedir que você mude um parâmetro e preveja o efeito antes de executar — só o seu próprio entendimento de utilização, Lei de Little e custos resolve isso.