<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Matheus Fidelis - Engineering Blog</title>
    <description>Technical Blog</description>
    <link>https://fidelissauro.dev/</link>
    <atom:link href="https://fidelissauro.dev/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 15 Apr 2026 10:32:39 +0000</pubDate>
    <lastBuildDate>Wed, 15 Apr 2026 10:32:39 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
      <item>
        <title>System Design - Observabilidade e Monitoramento</title>
        <description>&lt;p&gt;Após elaborarmos diversos tópicos como &lt;a href=&quot;/performance-capacidade-escalabilidade/&quot;&gt;Performance, Capacidade e Escalabilidade&lt;/a&gt;, &lt;a href=&quot;/single-point-of-failure&quot;&gt;Métricas de Continuidade de Negócio&lt;/a&gt; e diversos outros assuntos relacionados, este capítulo tem o objetivo de fazer um “recap” de alguns conceitos de forma simplificada, trazendo o ponto de vista final de &lt;strong&gt;Observabilidade, Monitoramento e Confiabilidade&lt;/strong&gt;. Nele, vamos conceituar algumas pontas soltas referentes a &lt;strong&gt;Logs, Métricas, Traces, Alerting e APM&lt;/strong&gt;, e principalmente como utilizar a simplicidade de diversos frameworks de mercado, como &lt;strong&gt;USE, RED e os Four Golden Signals&lt;/strong&gt;, alinhados ao negócio para encontrar métricas comuns, de fácil entendimento e, principalmente, alinhadas entre times técnicos e de negócio.&lt;/p&gt;

&lt;p&gt;Aqui, vamos elucidar principalmente como os temas de &lt;strong&gt;Observabilidade, Monitoramento e Confiabilidade se correlacionam e se complementam entre si&lt;/strong&gt;. Vamos entender cada um dos pilares da observabilidade e qual tipo de entendimento estratégico precisamos ter sobre cada um deles.&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot; id=&quot;MathJax-script&quot; async=&quot;&quot; src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js&quot;&gt;
&lt;/script&gt;

&lt;script type=&quot;text/x-mathjax-config&quot;&gt;


    MathJax.Hub.Config({
        jax: [&quot;input/TeX&quot;,&quot;output/HTML-CSS&quot;],
        displayAlign: &quot;left&quot;
    });
    
    
&lt;/script&gt;

&lt;h1 id=&quot;definindo-confiabilidade&quot;&gt;Definindo Confiabilidade&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Confiabilidade&lt;/strong&gt; é a propriedade de um sistema &lt;strong&gt;entregar comportamento correto ao longo do tempo&lt;/strong&gt;, sob condições esperadas e sob uma fração representativa de condições adversas. Confiabilidade vai muito além de uma aplicação “ficar de pé” e não é sinônimo direto de “alta disponibilidade”. Um sistema pode estar tecnicamente disponível e, ainda assim, ser pouco confiável se responde com dados errados, se degrada de forma caótica, se apresenta latência imprevisível ou se não consegue manter invariantes essenciais do domínio quando pressionado.&lt;/p&gt;

&lt;p&gt;Confiabilidade, portanto, agrega os conceitos de continuidade de serviço, integridade e previsibilidade em termos operacionais.&lt;/p&gt;

&lt;p&gt;A utilidade dessa definição é que ela coloca confiabilidade no lugar certo: como uma &lt;strong&gt;restrição arquitetural&lt;/strong&gt; e um &lt;strong&gt;contrato operacional&lt;/strong&gt;, e não como um “atributo desejável”. A partir daqui, todos os termos que serão abordados neste capítulo, como &lt;strong&gt;SLIs/SLOs, error budget, Four Golden Signals, RED e USE&lt;/strong&gt;, e demais estratégias abordadas em outros capítulos, como estratégias de redundância, padrões de resiliência e práticas de incident response, passam a ser consequência de um objetivo: reduzir a probabilidade e o impacto de comportamentos incorretos, reduzir o tempo para detectar e recuperar e limitar o blast radius quando algo inevitavelmente falhar.&lt;/p&gt;

&lt;p&gt;A confiabilidade, então, é um conjunto de práticas e disciplinas da engenharia e arquitetura de software que busca atingir níveis cada vez maiores e auditáveis de continuidade operacional.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;observabilidade&quot;&gt;Observabilidade&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Observabilidade&lt;/strong&gt; é a capacidade de inferir o estado interno de um sistema a partir de suas saídas externas. O termo tem origem na Teoria de Controle, da década de 1960, introduzida academicamente por Rudolf E. Kalman, por meio de publicações sobre a teoria de sistemas lineares.&lt;/p&gt;

&lt;p&gt;A Teoria do Controle é um ramo da engenharia e da matemática que estuda como modelar, analisar e regular o comportamento de sistemas dinâmicos para projetar sistemas complexos que se comportem de maneira estável ao longo do tempo, mesmo diante de perturbações externas ocasionais. Na teoria, um sistema é dito observável caso exista a capacidade de inferir o estado interno de um sistema apenas por suas saídas externas.&lt;/p&gt;

&lt;p&gt;A observabilidade em sistemas de software depende de saídas, registros e métricas de desempenho para cumprir esse papel. Trata-se da capacidade de compreender o estado interno de um sistema complexo a partir dos eventos e sinais externos que ele emite.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/observability.png&quot; alt=&quot;Observability&quot;&gt;&lt;/p&gt;

&lt;p&gt;Esses eventos e sinais podem ser traduzidos, inicialmente, nos três pilares da observabilidade: &lt;strong&gt;logs, traces e métricas&lt;/strong&gt;. O objetivo é entender comportamento, padrões e construir estruturas que sejam “interrogáveis” por meio de padrões e dimensões conhecidas e não conhecidas. Podemos presumir que: &lt;strong&gt;uma vez que conseguimos correlacionar logs, traces e métricas para elaborar questionamentos complexos sobre o sistema, temos observabilidade.&lt;/strong&gt; E, ainda mais, &lt;strong&gt;se podemos utilizar logs, traces e métricas para conduzir análises exploratórias, temos observabilidade&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Por mais que seja altamente dependente, a observabilidade é uma propriedade estrutural de um sistema, e não um conjunto de ferramentas. É altamente possível que empresas, produtos e estruturas inteiras disponham de ferramentas altamente complexas e caras e, mesmo assim, não possuam observabilidade em essência.&lt;/p&gt;

&lt;p&gt;À medida que nosso ferramental é utilizado para interpretar comportamentos, e podemos utilizá-lo, técnica e culturalmente, para entender comportamentos e padrões ocultos de forma histórica, temos observabilidade.&lt;/p&gt;

&lt;h2 id=&quot;monitoramento-e-observabilidade&quot;&gt;Monitoramento e Observabilidade&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;O monitoramento e a observabilidade&lt;/strong&gt; são conceitos que caminham juntos, de forma tão tênue que normalmente são confundidos e referenciados como se fossem a mesma coisa. Entender a diferença entre os dois pode ser de grande ajuda para elevar o nível de confiabilidade nos sistemas. Os dois conceitos não são excludentes, mas complementares em essência.&lt;/p&gt;

&lt;p&gt;Monitoramento é a capacidade de coleta e análise de métricas pré-definidas, de contextos já vividos, para verificar o estado de um sistema a partir de dimensões já conhecidas. O monitoramento nos dá a capacidade de monitorar, verificar e alertar quando algo conhecido dá errado, como, por exemplo, aumento de erros em APIs específicas, saturação de recursos, locks em um banco de dados, aumentos no tempo de resposta, etc.&lt;/p&gt;

&lt;p&gt;O monitoramento se desenvolve normalmente por meio de medidas quantitativas, como, por exemplo, porcentagem de uso de CPU, latência de rede, quantidade de dados de entrada e saída de rede, taxas de erro, espaço em disco, etc., e, com base nisso, permite configurar thresholds para disparar alertas quando algo sai de um padrão estabelecido.&lt;/p&gt;

&lt;p&gt;Observabilidade, por outro lado, é a capacidade de investigar fenômenos desconhecidos por meio da exploração de dados contextuais mais amplos e entender o “porquê” de algo inesperado ter acontecido. Deixamos de observar um estado &lt;strong&gt;determinístico&lt;/strong&gt;, como “minha API está lenta”, e expandimos isso para &lt;strong&gt;“por que essa API está lenta agora?”&lt;/strong&gt;, analisando todos os sinais de forma correlacionada para compreender o comportamento.&lt;/p&gt;

&lt;p&gt;Resumindo, o monitoramento está diretamente ligado a identificar e alertar sobre problemas conhecidos, enquanto a observabilidade está ligada ao comportamento. Uma vez que sua observabilidade possibilita encontrar padrões e investigar problemas não óbvios, essas novas dimensões descobertas podem ser utilizadas como insumos para gerar monitoramento. &lt;strong&gt;Observabilidade está correlacionada a comportamento e exploração; monitoramento é acompanhamento.&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;monitoramento-como-detecção-de-sintomas&quot;&gt;Monitoramento como Detecção de Sintomas&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Monitoramento&lt;/strong&gt; é, em essência, a disciplina de detectar sintomas conhecidos de degradação, falha ou risco operacional. Antes de qualquer coisa, só monitoramos o que é claro e conhecido. Ter clareza sobre esse conceito é muito importante para a sua diferenciação. Ele parte do princípio de que já sabemos, com algum grau de clareza, &lt;strong&gt;quais sinais merecem ser acompanhados e quais desvios desses sinais representam uma ameaça à saúde do sistema&lt;/strong&gt;. O monitoramento trabalha com métricas, eventos e thresholds previamente definidos, observando comportamentos esperados e disparando alertas quando algo sai da normalidade conhecida.&lt;/p&gt;

&lt;p&gt;Quando configuramos alertas para dimensões conhecidas, como aumento de taxa de erro, aumento de latência, saturação de CPU e memória, filas ou tópicos acumulando mensagens e eventos, locks em banco de dados ou falhas em health checks, estamos modelando sintomas de que algo pode estar errado. &lt;strong&gt;Não estamos necessariamente explicando a causa do problema, mas detectando que algo não está de acordo.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O monitoramento pode ter sinais padronizados dentro de uma organização, mas é de extrema importância entender que ele é evolutivo e amadurece junto ao sistema e ao time de engenharia. Sintomas conhecidos dependem de conhecimento prévio. Monitoramos aquilo que já aprendemos a medir, aquilo que já sabemos que pode falhar ou aquilo que já identificamos historicamente como importante, e revisitamos esses critérios sempre que algo estrutural muda.&lt;/p&gt;

&lt;h3 id=&quot;observabilidade-como-comportamento&quot;&gt;Observabilidade como Comportamento&lt;/h3&gt;

&lt;p&gt;Como vimos, o monitoramento está orientado a sintomas conhecidos. A observabilidade, por sua vez, está orientada a comportamento. Ela é a capacidade de explorar os sinais emitidos por um sistema de forma correlacionada para compreender como ele está se comportando internamente, mesmo quando o problema ainda não foi previamente modelado como uma condição de alerta. Enquanto o monitoramento pergunta &lt;strong&gt;“algo conhecido saiu do normal?”&lt;/strong&gt;, a observabilidade permite perguntar &lt;strong&gt;“o que está acontecendo dentro do sistema para que esse comportamento esteja acontecendo agora?”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Em sistemas distribuídos, &lt;strong&gt;uma degradação pode nascer em um ponto e se manifestar em outro&lt;/strong&gt;. Uma causa pequena pode produzir um efeito grande, dependendo da carga, da topologia e das dependências envolvidas. A observabilidade nos ajuda a correlacionar logs, métricas e traces emitidos por vários serviços envolvidos em uma transação para encontrar a origem de um comportamento com desvio.&lt;/p&gt;

&lt;p&gt;Por isso, &lt;strong&gt;dizer que observabilidade está ligada a comportamento é dizer que ela se interessa menos por valores isolados e mais pela forma como o sistema reage ao longo do tempo&lt;/strong&gt;, sob diferentes condições.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;três-pilares-da-observabilidade&quot;&gt;Três Pilares da Observabilidade&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;A observabilidade é a correlação direta de três pilares principais&lt;/strong&gt;, sendo eles &lt;strong&gt;Métricas, Logs e Traces&lt;/strong&gt;. Todos possuem valor individual e seu contexto &lt;strong&gt;sistêmico&lt;/strong&gt;; porém, quando somados e correlacionados, podemos &lt;strong&gt;expandir&lt;/strong&gt; esses sinais isolados para uma visão de comportamento mais ampla de ambientes complexos. &lt;strong&gt;Nesta seção, temos o objetivo de abordar cada um dos três pilares e explorar seus agregados, como Alerting e APM.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;métricas&quot;&gt;Métricas&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Métricas&lt;/strong&gt; são aspectos quantitativos e estatísticos do software que têm o objetivo de medir comportamentos, desempenho e demais estados de um sistema ao longo do tempo. Métricas, por si só, possuem características temporais e fornecem uma visão de tendências ao longo de períodos do dia. Métricas podem operar tanto no nível técnico quanto no de negócio. Existem métricas técnicas, como tempo de resposta, quantidade de sucessos, quantidade de erros, contadores de status codes, métricas de estado de circuit breakers (abertos e fechados), acionamentos de fallbacks, etc. As métricas de negócio operam em um nível mais característico e &lt;strong&gt;específico&lt;/strong&gt; da aplicação e podem ser variações como quantidade de vendas, quantidade de pagamentos aceitos, pagamentos recusados, quantidade de transações autorizadas ou negadas, quantidade de recusas por falta de saldo e demais validações.&lt;/p&gt;

&lt;h3 id=&quot;contadores&quot;&gt;Contadores&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/counters.png&quot; alt=&quot;Counters&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contadores&lt;/strong&gt; são valores que apenas aumentam (ou &lt;strong&gt;resetam&lt;/strong&gt; para zero, como na reinicialização de um serviço). &lt;strong&gt;É útil contar o número de eventos&lt;/strong&gt;, como requisições totais, erros, itens processados com sucesso, itens processados com erro, circuitos abertos, etc. Durante a coleta, esses valores podem ser agregados em séries temporais para entender comportamentos de utilização e picos.&lt;/p&gt;

&lt;h3 id=&quot;gauges&quot;&gt;Gauges&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/gauges.png&quot; alt=&quot;Gauges&quot;&gt;&lt;/p&gt;

&lt;p&gt;Um gauge representa um valor numérico que pode aumentar ou diminuir. &lt;strong&gt;É ideal para medir valores pontuais&lt;/strong&gt;, como uso de CPU, memória em uso, número de conexões ativas, tempos de resposta, etc. Ao contrário dos contadores, os gauges representam registros de valores absolutos que podem variar ao longo do tempo.&lt;/p&gt;

&lt;h3 id=&quot;histogramas&quot;&gt;Histogramas&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/histogram.png&quot; alt=&quot;Gauges&quot;&gt;&lt;/p&gt;

&lt;p&gt;Os histogramas agregam observações (como durações de requisições ou tamanhos de resposta) e as agrupam em baldes (buckets) configuráveis. &lt;strong&gt;Eles permitem calcular quantis e percentis&lt;/strong&gt; (ex.: “99% de todas as requisições foram concluídas em menos de 300 ms”), etc. Eles nos ajudam a explorar agregações de métricas de forma mais complexa e aprofundada, analisando a dispersão dos dados por meio de várias dimensões, como média, mediana, percentis e desvio padrão.&lt;/p&gt;

&lt;h2 id=&quot;traces&quot;&gt;Traces&lt;/h2&gt;

&lt;p&gt;Em ambientes distribuídos de microserviços, uma &lt;strong&gt;única&lt;/strong&gt; transação pode passar por dezenas de serviços diferentes para ser considerada concluída. &lt;strong&gt;Traces&lt;/strong&gt; têm o objetivo de capturar amostras de solicitações, detalhando-as de ponta a ponta, catalogando todas as entradas e saídas de uma transação por meio de &lt;strong&gt;múltiplos&lt;/strong&gt; componentes de um sistema distribuído.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/trace.png&quot; alt=&quot;trace&quot;&gt;&lt;/p&gt;

&lt;p&gt;Eles mostram o &lt;strong&gt;caminho&lt;/strong&gt; de ponta a ponta da transação, incluindo tempos de &lt;strong&gt;processamento&lt;/strong&gt;, latência, erros de chamada entre serviços, etc. Diferentemente dos logs, que são isolados, os traces conectam eventos em uma narrativa coesa, revelando como diferentes partes do sistema interagem.&lt;/p&gt;

&lt;p&gt;Traces são utilizados para entender erros e desvios de tempos de resposta de uma transação e &lt;strong&gt;facilitam o entendimento&lt;/strong&gt; do “porquê” de um problema em contextos complexos. &lt;strong&gt;Em um trace de ponta a ponta, podemos compreender o tempo de execução em nível de funções, métodos, queries de bancos de dados e clientes HTTP&lt;/strong&gt; de todas as aplicações que interagem durante o funcionamento de uma transação.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;logs&quot;&gt;Logs&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Logs&lt;/strong&gt; são registros textuais de eventos que ocorrem em um sistema. São a saída do runtime que representa algo que ocorreu. Um log é um registro imutável, vindo da aplicação que o emitiu, de um evento discreto que ocorreu em um ponto específico no tempo dentro de uma aplicação ou sistema e normalmente vem acompanhado de metadados e um timestamp para comparação e ordenação histórica, podendo ser correlacionado em uma linha do tempo, isoladamente ou com outras aplicações, quando estruturado.&lt;/p&gt;

&lt;p&gt;Eles capturam informações detalhadas sobre ações, erros e estados em momentos específicos, como mensagens de erro, dados de transação, informações de payloads ou dados de usuários. Em essência, logs funcionam como um diário detalhado do sistema, permitindo que exista uma &lt;strong&gt;investigação funcional de problemas&lt;/strong&gt; e, diferentemente dos traces, possuem uma característica de troubleshooting funcional, onde nem todo “problema” do software é necessariamente um “erro” ou um “desvio”. Eles nos ajudam a responder perguntas como &lt;strong&gt;“O que aconteceu com a transação xxx?”&lt;/strong&gt;, &lt;strong&gt;“O que um usuário específico fez?”&lt;/strong&gt;, &lt;strong&gt;“Qual foi o erro exato que causou a falha desta requisição?”&lt;/strong&gt;, &lt;strong&gt;“Quais foram os parâmetros de uma função quando ela foi chamada e qual foi seu retorno?”&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;níveis-de-severidade&quot;&gt;Níveis de Severidade&lt;/h3&gt;

&lt;p&gt;Quando tratamos os logs como um “diário detalhado” do sistema, a classificação de severidade é o componente semântico que traduz um fluxo textual qualquer em um componente de telemetria “interrogável”. Os níveis de severidade classificam os registros imutáveis de log em criticidade e contexto, que dizem “o que esse evento significa” e “o que alguém deve fazer a respeito”. Os níveis mais comuns (&lt;strong&gt;TRACE, DEBUG, INFO, WARN, ERROR e FATAL/CRITICAL&lt;/strong&gt;) existem para representar intenções diferentes, não apenas a gravidade do ocorrido. Nesta seção, iremos abordar critérios claros de classificação para cada um deles.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Level&lt;/th&gt;
      &lt;th&gt;Intenção&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;TRACE&lt;/td&gt;
      &lt;td&gt;Rastrear passos internos muito finos para investigação pontual&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;DEBUG&lt;/td&gt;
      &lt;td&gt;Explicar decisões internas e facilitar troubleshooting&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;INFO&lt;/td&gt;
      &lt;td&gt;Registrar fatos relevantes do fluxo e do domínio&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;WARN&lt;/td&gt;
      &lt;td&gt;Registrar um desvio recuperável&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ERROR&lt;/td&gt;
      &lt;td&gt;Falha de operação&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;FATAL / CRITICAL&lt;/td&gt;
      &lt;td&gt;Falha terminal em nível de runtime&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h4 id=&quot;nível-trace&quot;&gt;Nível TRACE&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;TRACE&lt;/strong&gt; é o nível de microscopia. Ele existe para quando você precisa observar o caminho exato que o código percorreu, com granularidade alta e verbosa, tipicamente em investigações pontuais, como ordem de decisões internas, &lt;strong&gt;branches&lt;/strong&gt; condicionais, parâmetros intermediários, transformações de payload, detalhes de serialização/deserialização e qualquer nuance que ajude a reproduzir um comportamento que não aparece em logs mais altos, podendo chegar até à verbosidade do protocolo de uma comunicação.&lt;/p&gt;

&lt;h4 id=&quot;nível-debug&quot;&gt;Nível DEBUG&lt;/h4&gt;

&lt;p&gt;O &lt;strong&gt;DEBUG&lt;/strong&gt; trabalha em nível de diagnóstico. Ele fica abaixo do “contar a história” e acima do “registrar absolutamente tudo”. A intenção do DEBUG é explicar o porquê de uma decisão do sistema, dando visibilidade a variáveis e estados relevantes para troubleshooting, como escolhas de fallback, impressão de parâmetros que levaram uma regra de negócio a seguir por um caminho, identificação de dependências chamadas e seus tempos, composição de requests para serviços downstream, resultados de validações e checkpoints do fluxo que ajudam a localizar o ponto exato de divergência. Geralmente, é utilizado durante períodos de crise para tratar condicionais muito específicas que levam a desvios não tão &lt;strong&gt;óbvios&lt;/strong&gt;, sendo muito útil para sistemas que possuem &lt;strong&gt;múltiplos&lt;/strong&gt; fluxos e uma visão “não tão” &lt;strong&gt;determinística&lt;/strong&gt; em nível de conhecimento do time técnico e de &lt;strong&gt;múltiplas&lt;/strong&gt; condicionais internas.&lt;/p&gt;

&lt;h4 id=&quot;nível-info&quot;&gt;Nível INFO&lt;/h4&gt;

&lt;p&gt;O &lt;strong&gt;INFO&lt;/strong&gt; trabalha em um aspecto narrativo da transação. Ele registra eventos relevantes do ponto de vista do sistema e do domínio, de modo que, quando você costura o fluxo por um correlation ID, você consegue ler uma história. O objetivo do INFO é rastrear uma transação de forma consistente e cronológica, como quando uma requisição entra, quem é a entidade principal envolvida, se uma operação foi aceita ou recusada, se um estado mudou, quando um job iniciou e finalizou, quando e como um evento de domínio foi publicado e como e quando uma transação foi concluída, com todas as informações relevantes para tratar um &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;correlationId&lt;/code&gt; em nível de um agregado forte.&lt;/p&gt;

&lt;h4 id=&quot;nível-warn&quot;&gt;Nível WARN&lt;/h4&gt;

&lt;p&gt;O &lt;strong&gt;WARN&lt;/strong&gt; é o nível do desvio com continuidade. É quando algo saiu do ideal, mas o sistema ainda conseguiu seguir adiante, como uma dependência que respondeu de forma mais lenta e um retry foi necessário, um fallback foi acionado, um circuito abriu por proteção, um timeout quase estourou, uma fila começou a crescer, uma validação marginal foi aceita por regra de tolerância, um cache miss inesperado elevou a latência, uma operação precisou degradar para manter a disponibilidade. &lt;strong&gt;Um bom WARN é acionável, deve carregar contexto para permitir triagem&lt;/strong&gt; e pode ser utilizado para confiabilidade, porque ele frequentemente aparece antes do incidente.&lt;/p&gt;

&lt;h4 id=&quot;nível-error&quot;&gt;Nível ERROR&lt;/h4&gt;

&lt;p&gt;O &lt;strong&gt;ERROR&lt;/strong&gt; é falha de operação. Aqui, a execução não atingiu o resultado esperado do ponto de vista daquela transação. A requisição falhou e retornou erro, um critério de domínio foi violado e o comando foi rejeitado, uma dependência falhou sem compensação possível, uma transação abortou, uma transação no banco de dados não foi concluída, uma conexão não pôde ser fechada, uma mensagem não pôde ser processada e foi para DLQ, um dado essencial estava ausente ou um estado ficou inconsistente a ponto de impedir continuidade.&lt;/p&gt;

&lt;p&gt;Um ERROR precisa ser pensado como um “log de triagem” e deve dizer o que falhou, por que falhou, junto com sua possível causa, onde falhou e em qual componente, e como correlacionar com o resto do fluxo, respeitando &lt;strong&gt;correlation IDs&lt;/strong&gt;.&lt;/p&gt;

&lt;h4 id=&quot;nível-fatal&quot;&gt;Nível FATAL&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;FATAL&lt;/strong&gt; (ou CRITICAL, dependendo do ecossistema) é falha terminal, aquela que compromete a continuidade do processo ou do serviço. É quando o runtime não consegue seguir, o processo cai, o serviço não inicia, uma configuração essencial é inválida, um recurso crítico não está acessível na inicialização ou uma condição irrecuperável foi atingida e a única resposta segura é encerrar. Como, por exemplo, um NullPointer crítico, uma dependência crítica que não pode ser acessada, falta de variáveis e parametrizações necessárias para iniciar a aplicação, etc. Logs FATAL são geralmente associados a falhas de runtime e impedem a aplicação de funcionar.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;correlação-de-logs&quot;&gt;Correlação de Logs&lt;/h3&gt;

&lt;p&gt;A principal função dos logs está no seu nível de detalhe útil. Uma métrica pode mostrar a quantidade de erros dentro de um período específico; porém, um log tem o objetivo de mostrar os outputs da aplicação que indicam quais erros, exceções e em que cenários esses erros aconteceram. Em ambientes cada vez mais distribuídos, com &lt;strong&gt;múltiplos&lt;/strong&gt; serviços dentro de uma mesma transação, podemos estabelecer padrões de campos que se repetem em todos os serviços pelos quais uma determinada transação passa, para que seja possível correlacionar os logs de diversas aplicações e gerar uma “história” de uma transação.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/log-correlation-search-min.png&quot; alt=&quot;Log Correlation Search&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/log-correlation.png&quot; alt=&quot;Log Correlation Result&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Os logs, para terem valor, precisam contar uma história&lt;/strong&gt;. Conceitualmente, &lt;strong&gt;trabalhamos uma transação como um agregado, e as linhas de log como itens decorrentes desse agregado&lt;/strong&gt;. Quando bem estruturado, esse padrão nos permite, por meio de identificadores únicos como &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trace_id&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;correlation_id&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;order_id&lt;/code&gt;, correlacionar os logs de diversas fontes para explicar como uma determinada transação ocorreu, como o extrato de uma história. &lt;strong&gt;Talvez esse seja o cenário em que os logs vão, de fato, gerar todo o seu potencial e justificar seus altos custos de ingestão, armazenamento e retenção.&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;estruturação-e-indexação-de-logs&quot;&gt;Estruturação e Indexação de Logs&lt;/h3&gt;

&lt;p&gt;O maior desafio da ingestão de logs está no custo. Aplicações podem gerar gigabytes ou terabytes de logs por dia, tornando o armazenamento e a análise tarefas muito complexas. Esses logs podem conter uma variedade imensa de informações e valores únicos e despadronizados, como IDs de usuário, IDs de requisição, mensagens de erro detalhadas, stack traces extensos, que são, sim, dados úteis; porém, quando trabalhamos com indexação utilizando os valores desses campos, podemos enfrentar problemas relacionados a performance e custo.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/log-json.png&quot; alt=&quot;Logs&quot;&gt;&lt;/p&gt;

&lt;p&gt;Logs de texto puro são difíceis de analisar em escala. Logs estruturados e padronizados, por exemplo, em JSON, permitem que ferramentas de agregação de logs realizem a indexação por campos específicos mais buscados, além de filtros e agregações, de forma menos &lt;strong&gt;custosa&lt;/strong&gt; computacionalmente e financeiramente. Podemos indexar a partir de &lt;strong&gt;correlation IDs&lt;/strong&gt;, IDs de conta, nível de criticidade e afins. Ter um formato e campos padronizados pode ser, sim, um desafio em ambientes maiores; porém, torna o pilar de logs significativamente mais eficiente em termos de custo de armazenamento, escalabilidade e busca.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;agregados-dos-pilares&quot;&gt;Agregados dos Pilares&lt;/h2&gt;

&lt;p&gt;Além dos três pilares, temos dois outros termos que nos ajudam a agregar confiabilidade &lt;strong&gt;aos&lt;/strong&gt; sistemas: &lt;strong&gt;alerting&lt;/strong&gt; e &lt;strong&gt;APM&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;alerting&quot;&gt;Alerting&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Alerting&lt;/strong&gt;, ou alertas, são os mecanismos que transformam os números e dimensões já conhecidos de degradação de um sistema em sinais para intervenção humana. É a disciplina responsável por observar sinais emitidos por logs, métricas, traces ou agregados desses pilares e decidir quando uma condição saiu do campo da simples medição e foi para um campo em que será necessária uma intervenção. É uma forma de saber, de maneira automatizada, que um determinado comportamento do sistema atingiu um nível de risco, desvio ou impacto e que ele merece reação humana ou automatizada.&lt;/p&gt;

&lt;p&gt;Do ponto de vista de confiabilidade, o maior valor do alerting está em acelerar feedback loops sobre o comportamento do sistema. Quanto mais cedo um comportamento degradado é percebido pelo time de engenharia responsável, menor tende a ser o tempo de detecção, menor a chance de amplificação do dano para o cliente e maior a possibilidade de contenção do impacto antes de um impacto sistêmico maior para todo o ambiente. &lt;strong&gt;Alertas bem definidos reduzem MTTD (Mean Time To Detect), ajudam a proteger o error budget, orientam war rooms e criam um senso de prioridade operacional.&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;apm&quot;&gt;APM&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;APM&lt;/strong&gt;, ou Application Performance Monitoring, é a camada de observabilidade voltada a compreender como uma aplicação se comporta durante a execução de trabalho útil, visando entender a experiência do cliente. Enquanto métricas de infraestrutura ajudam a enxergar o estado de recursos alocados, métricas de aplicação ajudam a entender o estado operacional de negócio e os traces ajudam a reconstruir o caminho de uma transação de ponta a ponta, o APM organiza esses sinais ao redor da experiência da própria aplicação, destacando operações, endpoints, dependências, tempos de resposta, throughput, taxas de erro e fragmentos relevantes de execução, para que seja possível observar a saúde do software com mais proximidade do uso real.&lt;/p&gt;

&lt;p&gt;O APM tenta responder perguntas como &lt;strong&gt;“quais operações estão mais lentas agora?”&lt;/strong&gt;, &lt;strong&gt;“quais delas regrediram após uma mudança?”&lt;/strong&gt;, &lt;strong&gt;“quais endpoints concentram mais erro?”&lt;/strong&gt;, &lt;strong&gt;“quais dependências estão consumindo mais tempo da transação?”&lt;/strong&gt;, &lt;strong&gt;“onde estão os gargalos de latência?”&lt;/strong&gt;, &lt;strong&gt;“qual parte do fluxo ficou mais cara sob carga?”&lt;/strong&gt; e &lt;strong&gt;“quais jornadas estão sofrendo de forma mais perceptível?”&lt;/strong&gt;. Ele busca agregar todos os sinais de observabilidade sob um guarda-chuva de experiência de uso.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;service-levels&quot;&gt;Service Levels&lt;/h1&gt;

&lt;p&gt;Os &lt;strong&gt;Service Levels&lt;/strong&gt; são o principal framework de mercado para engenharia de confiabilidade. Tendo sua origem na engenharia da Google, eles nos dão direcionamentos simples de como &lt;strong&gt;transformar&lt;/strong&gt; métricas técnicas em “estrelas-guia” de produto, que são capazes de ser interpretadas por diversos níveis de uma empresa. Na prática, tornam-se a interface comum de linguagem entre engenharia e negócio, onde encontramos um acordo claro sobre &lt;strong&gt;qual é a experiência mínima aceitável&lt;/strong&gt;, &lt;strong&gt;quais são as tolerâncias operacionais&lt;/strong&gt; e &lt;strong&gt;qual é o custo de sustentar esse patamar desejado&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Um sistema pode ter dashboards &lt;strong&gt;extremamente detalhados&lt;/strong&gt; e, ainda assim, operar no escuro se não houver um referencial explícito de “normalidade” e “aceitável” para a jornada do usuário, e é exatamente esse vácuo que SLA, SLO, SLI e Error Budget preenchem em sistemas maduros.&lt;/p&gt;

&lt;h2 id=&quot;sli---service-level-indicator&quot;&gt;SLI - Service Level Indicator&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;SLI&lt;/strong&gt;, ou Service Level Indicator, é o indicador mensurável que materializa o SLA e o SLO. O SLI é o dado que será observado, como, por exemplo, Availability/Uptime, Latency, Throughput, Error Rate, Saturation, Recovery Time, etc. Ele indica qual será a métrica observada em ambos os casos. A escolha e a maturidade de um SLI devem ser criteriosas e evolutivas, acompanhando os times de engenharia e os próprios &lt;strong&gt;SLOs&lt;/strong&gt; e &lt;strong&gt;SLAs&lt;/strong&gt;. Podem tanto ser métricas técnicas, como as citadas, quanto métricas de negócio ou específicas de um produto, como, por exemplo, acurácia de um modelo, taxas de aprovação de transações, redução de fraudes, etc.&lt;/p&gt;

&lt;h2 id=&quot;sla---service-level-agreement&quot;&gt;SLA - Service Level Agreement&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;SLA&lt;/strong&gt;, ou Service Level Agreement, é o indicador mais importante em nível de cliente. O SLA é um &lt;strong&gt;compromisso contratual&lt;/strong&gt; de nível de serviço, normalmente formalizado com clientes, áreas internas ou parceiros. Esse compromisso atua na esfera contratual de um provedor de &lt;strong&gt;serviço&lt;/strong&gt;, seja ele interno ou externo.&lt;/p&gt;

&lt;p&gt;Quando contratamos algum serviço, seja ele IaaS, SaaS ou PaaS, ele está inerente a um contrato de disponibilidade. Quando esse contrato é quebrado, podem existir &lt;strong&gt;consequências jurídicas&lt;/strong&gt; para o prestador do serviço. Por isso, SLA não é o lugar para “fidelidade técnica”, e sim para &lt;strong&gt;accountability&lt;/strong&gt;. Ele tende a ser mais estável, mais conservador e menos granular, porque precisa ser mensurável, auditável e defensável. Ele está além de uma métrica do time técnico, que deve trabalhar com margens menores que o SLA como objetivo operacional.&lt;/p&gt;

&lt;p&gt;Um SLA está inerente a tudo que permeia a operação do cliente final. Pode ser considerado a partir de disponibilidade, tempo de resposta, tempo de recuperação de desastre, etc. Os &lt;strong&gt;SLAs&lt;/strong&gt; podem ser definidos como “ter 99.99% de uptime, 99.9% de disponibilidade nas requisições, responder uma transação de cartão de crédito em menos de 600 ms, ter um data loss de no máximo 2 h em RPO, ter um tempo de &lt;strong&gt;recuperação&lt;/strong&gt; de falhas de até 1 h”, etc.&lt;/p&gt;

&lt;p&gt;Quando estabelecemos &lt;strong&gt;SLAs&lt;/strong&gt; de disponibilidade, a definição dessa métrica nunca deve ser 100%, pois qualquer variação ou desvio pode comprometer o contrato. Ao invés disso, adicionamos “9’s”, como, por exemplo, 99%, 99.9%, 99.99%, etc., mas nunca 100%.&lt;/p&gt;

&lt;p&gt;Os &lt;strong&gt;SLAs&lt;/strong&gt; precisam ser &lt;strong&gt;declarados&lt;/strong&gt; e conhecidos por todas as camadas do produto — times técnicos, negócio, marketing e suporte — e devem ter um escopo claro, como disponibilidade mensal, disponibilidade anual, disponibilidade diária, tempos de resposta, etc. O SLA, inclusive, pode ser granular em nível de serviço ou feature do sistema, medido de forma isolada em uma jornada, endpoint, etc.&lt;/p&gt;

&lt;h2 id=&quot;slo---service-level-objective&quot;&gt;SLO - Service Level Objective&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;SLO&lt;/strong&gt;, Service Level Objective, é o seu “contrato interno” de confiabilidade. O SLO é, de fato, uma métrica inerente ao time técnico: o critério que o time de engenharia utiliza para operar, decidir e assumir riscos.&lt;/p&gt;

&lt;p&gt;Um SLO pode ser “responder em menos de 600 ms em p99 e 500 ms em p95”, “garantir replicação de dados em três fatores”, “ter uma média diária de error rate abaixo de 1%” ou herdar diretamente os critérios do SLA.&lt;/p&gt;

&lt;p&gt;Caso os &lt;strong&gt;SLIs&lt;/strong&gt; dos &lt;strong&gt;SLOs&lt;/strong&gt; sejam os mesmos do SLA, eles devem ser mais restritivos que o SLA, pois também funcionam como uma blindagem técnica do contrato. Por exemplo, se o SLA estabelecido por contrato é de uma disponibilidade mensal de 99.9%, com um tempo de resposta de p99 de 800 ms, o SLO precisa ser mais restritivo, considerando, por exemplo, 99.95% de disponibilidade e um p99 de 500 ms. A longo prazo, o objetivo de um SLO é tornar-se o SLA do produto, enquanto o time técnico continua elevando o nível de exigência para atingir excelência operacional.&lt;/p&gt;

&lt;h2 id=&quot;error-budget&quot;&gt;Error Budget&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;Error Budget&lt;/strong&gt; é o orçamento de erros associado a um contrato. Se o SLO define “quanto erro é aceitável”, o Error Budget define “quanto erro você ainda pode consumir antes de entrar em risco”. Se nosso SLO é 99.95% de disponibilidade e nossos &lt;strong&gt;SLIs&lt;/strong&gt; apontam para 99.98% de disponibilidade, isso significa que temos &lt;strong&gt;0.03%&lt;/strong&gt; de margem para erros dentro do sistema.&lt;/p&gt;

&lt;p&gt;O objetivo do Error Budget, além de mostrar o quanto de margem ainda temos para errar dentro das metas técnicas, é funcionar como um indicador de feedback loop dentro das releases de software. Quando o budget está saudável e &lt;strong&gt;possui&lt;/strong&gt; margens consideráveis, você pode acelerar mudanças e deploys em produção. Ao contrário disso, quando o budget está sendo consumido e está muito &lt;strong&gt;próximo&lt;/strong&gt; de atingir o limite, você desacelera, prioriza correções, reduz blast radius e aumenta o rigor de release e revisões. Se o budget estourou, você &lt;strong&gt;congela releases não essenciais&lt;/strong&gt;, direciona capacidade para estabilidade e conduz war rooms de observabilidade e acompanhamento.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;frameworks-de-mercado&quot;&gt;Frameworks de Mercado&lt;/h1&gt;

&lt;p&gt;Antes de entrar nos frameworks de mercado, vale estabelecer o porquê de eles existirem e qual problema real resolvem. Pois, a princípio, perante vários cases de uso complexos, com múltiplos níveis de observabilidade e operação, em um primeiro momento, eles podem parecer bem simplistas. Mas não são. O objetivo dos frameworks de mercado é fornecer “estrelas-guia” simplificadas para os times de engenharia e produto. Dentro de produtos de tecnologia, a maior parte das discussões operacionais degrada por dois caminhos previsíveis: ou o time se afoga em centenas de métricas desconexas, sem conseguir distinguir sintoma de causa, ou se apega a uma ou duas métricas “fáceis”, como CPU média, 5xx e latência média, e toma decisões erradas com muita confiança.&lt;/p&gt;

&lt;p&gt;O objetivo de frameworks como Four Golden Signals, RED e USE é sugerir métricas simples e &lt;strong&gt;facilmente compreensíveis&lt;/strong&gt;, que vão atuar como &lt;strong&gt;bússolas&lt;/strong&gt; de navegação do sistema em produção. &lt;strong&gt;Usá-los, de forma alguma, descaracteriza a necessidade de observar dimensões mais detalhadas; apenas simplifica o entendimento da saúde do serviço por meio de métricas direcionadas.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;use-method&quot;&gt;USE Method&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/use-resources.png&quot; alt=&quot;USED&quot;&gt;&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;USE Method&lt;/strong&gt; surge no contexto de engenharia de performance de sistemas, popularizado e formalizado por Brendan Gregg no artigo “Thinking Methodically about Performance”, como uma estratégia de checagem sistemática e padronizada da “saúde” de recursos físicos alocados.&lt;/p&gt;

&lt;p&gt;O objetivo do &lt;strong&gt;Método USE&lt;/strong&gt; é dar visibilidade a cada recurso, como CPU, memória, disco, rede e outros, como filas e pools, e observá-los em três dimensões principais: Utilization, Saturation e Errors. Esses recursos podem ser tanto recursos alocados para aplicações dentro de containers ou servidores quanto recursos de suas dependências, como databases, caches, filas, etc.&lt;/p&gt;

&lt;p&gt;Ele descreve o USE como um método para iniciar uma investigação de queda de performance de forma simples e objetiva, monitorando sinais vitais e identificando gargalos sistêmicos com rapidez.&lt;/p&gt;

&lt;h3 id=&quot;utilization-utilização&quot;&gt;Utilization (Utilização)&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Utilization&lt;/strong&gt; é quanto do recurso está sendo consumido em um intervalo de tempo. Em CPU, pode ser o percentual de tempo em execução ou o percentual utilizado em relação ao alocado. Em memória, pode ser o uso em relação ao provisionado. Em disco, pode ser busy time / throughput em relação ao limite de IOPs. Em rede, pode ser a bandwidth consumida em relação ao total permitido. Em pools, pode ser conexões em uso em relação ao limite do banco, etc. &lt;strong&gt;Utilization é um indicador de carga e tendência.&lt;/strong&gt; Ele é útil para capacity planning, detecção de regressões e para “contextualizar” a saturação.&lt;/p&gt;

&lt;h3 id=&quot;saturation-saturação&quot;&gt;Saturation (Saturação)&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Saturação&lt;/strong&gt; é o estado de superalocação de um recurso computacional, como CPU, memória, thread pools e connection pools. Geralmente, é monitorada pela ocupação ou pressão sobre o recurso, e não apenas pela utilização percentual. Por exemplo, quantos processos estão aguardando CPU (run queue) ou quantas requisições estão enfileiradas aguardando processamento. Quando esse recurso começa a degradar por operar próximo de sua capacidade total, entendemos que ele está saturado.&lt;/p&gt;

&lt;p&gt;Em CPU, a saturação pode aparecer como run queue (processos esperando CPU), throttling de cgroup e o comumente utilizado load average. Em memória, pode aparecer como swapping, garbage collector em excesso, falhas de alocação, etc. Em disco, aparece como filas de I/O, iowait, latência de I/O, backlog de flush. Em rede, aparece como drops, retransmissões, filas no kernel, etc.&lt;/p&gt;

&lt;h3 id=&quot;errors-erros&quot;&gt;Errors (Erros)&lt;/h3&gt;

&lt;p&gt;Os &lt;strong&gt;Errors&lt;/strong&gt; no USE são falhas diretamente associadas ao recurso monitorado. Em CPU, falhas por starvation ou erros de scheduling. Em memória, OOMKills, allocation failures, crashes por falta de heap, evictions. Em disco, I/O errors, timeouts, corrupções, falhas de mount. Em rede, connection resets, TLS handshakes falhando por exaustão de recursos, packet loss, DNS failures. Em pools, “too many connections”, “thread pool exhausted”, “queue full”, “rate limit exceeded”. &lt;strong&gt;O valor desses erros é o sinal necessário para indicar que o recurso provisionado e alocado não está sendo suficiente para suportar a quantidade de trabalho.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;red-method&quot;&gt;RED Method&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/red-metrics.png&quot; alt=&quot;RED&quot;&gt;&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;RED Method&lt;/strong&gt; nasce de uma necessidade equivalente à do USE, porém voltada para serviços e aplicações. Como vimos, o USE foi concebido para recursos e infraestrutura, enquanto microserviços demandam uma visão de métricas direcionadas à experiência e ao comportamento do serviço em produção, por meio de três dimensões básicas. O termo é associado a Tom Wilkie e à Grafana, por meio de diversos artigos e apresentações técnicas direcionadas à instrumentação e ao monitoramento de serviços, principalmente em sistemas distribuídos.&lt;/p&gt;

&lt;p&gt;O RED busca simplificar os sinais vitais mais importantes para qualquer &lt;strong&gt;aplicação web&lt;/strong&gt;, sendo eles Rate, Errors e Duration. Em caso de dúvidas sobre o que monitorar, a base será essa.&lt;/p&gt;

&lt;h3 id=&quot;rate-request-rate--throughput&quot;&gt;Rate (Request Rate / Throughput)&lt;/h3&gt;

&lt;p&gt;O &lt;strong&gt;Rate&lt;/strong&gt; representa a pressão de demanda sobre o serviço e a sua capacidade efetiva de processar essa demanda. Ele evidencia quantas transações estão chegando ao sistema em um determinado intervalo de tempo, como “transações por segundo”, “requests por minuto”, etc. Ele representa o quanto o sistema está sendo requisitado pelos clientes.&lt;/p&gt;

&lt;p&gt;Essa métrica pode ser medida em um contexto global do sistema, mas, além disso, deve ser medida de forma granular também em nível de endpoint e funcionalidade, como requisições por segundo por rota, por operação (GET/POST), por tenant, por região, por versão (canary vs stable), etc.&lt;/p&gt;

&lt;p&gt;O request rate é a primeira métrica a ser monitorada, pois o aumento do rate de uma aplicação ou funcionalidade pode acarretar saturação e aumento proporcional de erros e filas internas, caso o sistema &lt;strong&gt;não opere&lt;/strong&gt; com escalabilidade horizontal de forma responsiva. Ele também nos ajuda a identificar picos e tendências de uso do sistema, que podem gerar insights valiosos para capacity planning e aplicação de estratégias de autoscaling.&lt;/p&gt;

&lt;h3 id=&quot;errors-error-rate&quot;&gt;Errors (Error Rate)&lt;/h3&gt;

&lt;p&gt;Os &lt;strong&gt;Errors&lt;/strong&gt; estão diretamente &lt;strong&gt;ligados&lt;/strong&gt; ao request rate, pois &lt;strong&gt;têm&lt;/strong&gt; o objetivo de demonstrar a porcentagem das requisições que estão chegando ao sistema e falhando. A métrica tem o critério de medir falhas observáveis do ponto de vista do consumidor, mas só é útil se “erro” for definido de forma semântica.&lt;/p&gt;

&lt;p&gt;É comum que essa &lt;strong&gt;semântica&lt;/strong&gt; considere apenas erros “HTTP 5xx”. Isso é insuficiente por dois motivos: primeiro, porque 4xx pode representar degradação com base em desvios, como autenticação falhando por clock skew, validações quebradas por mudança de contrato, “429” por rate limit excessivo, etc. Em confiabilidade, erro é tudo aquilo que viola a expectativa de sucesso do consumidor: falha de autorização indevida, timeout, resposta inválida, inconsistência, idempotência quebrada, duplicidade e até sucesso tardio, quando o usuário já desistiu.&lt;/p&gt;

&lt;p&gt;Resumindo, todos os códigos de erro, sejam eles 4xx &lt;strong&gt;ou&lt;/strong&gt; 5xx, devem ser monitorados e considerados; porém, nem todos precisam ser tratados como &lt;strong&gt;SLOs&lt;/strong&gt;, apenas observados em nível de serviço.&lt;/p&gt;

&lt;h3 id=&quot;duration-request-duration--latency&quot;&gt;Duration (Request Duration / Latency)&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Duration&lt;/strong&gt; mede o tempo para completar uma operação do ponto de vista do consumidor. É o critério mais fácil de medir e também o mais fácil de medir de forma incorreta. O primeiro ponto de atenção é o uso exclusivo da média de latência em sistemas complexos. A média pode ser distorcida quando há problemas de tempos de resposta em uma distribuição de cauda longa, e as caudas são onde a experiência degrada, os timeouts disparam e os retries começam a amplificar a carga. Duration precisa ser analisada em percentis (p50/p95/p99) e, idealmente, com histogramas para observar a forma da distribuição. Quando &lt;strong&gt;aplicada&lt;/strong&gt; também em nível granular por métodos ou endpoints, podemos entender quais funcionalidades estão apresentando desvios de tempo de resposta, acelerando o &lt;strong&gt;troubleshooting&lt;/strong&gt; e o tempo de recuperação.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;four-golden-signals&quot;&gt;Four Golden Signals&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/four-golden-signals.png&quot; alt=&quot;Four Golden Signals&quot;&gt;&lt;/p&gt;

&lt;p&gt;Os &lt;strong&gt;Four Golden Signals&lt;/strong&gt; são uma forma direcionada e &lt;strong&gt;simplificada&lt;/strong&gt; de descrever a saúde operacional de um sistema user-facing, evitando o caos de métricas infinitas. O conceito foi popularizado pela literatura do Google sobre Site Reliability Engineering e tem o objetivo de realizar uma recomendação &lt;strong&gt;explícita&lt;/strong&gt; de quatro métricas principais, os Sinais de Ouro. Esses quatro sinais têm o objetivo, inclusive, de orientar a definição de &lt;strong&gt;SLOs&lt;/strong&gt; de forma simples.&lt;/p&gt;

&lt;p&gt;O objetivo é padronizar métricas em escopos pequenos, médios e grandes, evitando o fenômeno de “monitoramento por acúmulo”, em que times passam a colecionar uma quantidade muito grande de métricas e dashboards, mas, sem um modelo mental coeso, acabam incapazes de responder rapidamente a perguntas simples como &lt;strong&gt;“o sistema está saudável do ponto de vista do usuário?”&lt;/strong&gt;, &lt;strong&gt;“quais sistemas estão degradados agora?”&lt;/strong&gt;, etc.&lt;/p&gt;

&lt;p&gt;Os quatro sinais são &lt;strong&gt;Latency, Traffic, Errors e Saturation&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;latency&quot;&gt;Latency&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Latência&lt;/strong&gt; nos Four Golden Signals corresponde ao tempo que um sistema, transação ou funcionalidade leva para responder a uma requisição. No modelo, isso inclui tanto &lt;strong&gt;respostas&lt;/strong&gt; bem-sucedidas quanto respostas com erro, porque, para a experiência do usuário, “rápido e errado” ainda é um comportamento observável relevante, assim como “lento e correto”.&lt;/p&gt;

&lt;p&gt;A latência, assim como em outros frameworks de mercado, não deve considerar apenas a média e &lt;strong&gt;precisa&lt;/strong&gt; levar em consideração a leitura de &lt;strong&gt;percentis&lt;/strong&gt;, que podem dar visibilidade a comportamentos ocultos de outliers, como p99, p95, p90, p50, etc.&lt;/p&gt;

&lt;h3 id=&quot;traffic&quot;&gt;Traffic&lt;/h3&gt;

&lt;p&gt;O &lt;strong&gt;Traffic&lt;/strong&gt;, tráfego ou throughput, busca dar visibilidade à quantidade de solicitações que o sistema está recebendo dentro de um intervalo de tempo e pode ser ilustrado como requisições por segundo, transações por segundo, queries por segundo, bytes, mensagens, etc., representando o “quanto de trabalho” está chegando ao sistema.&lt;/p&gt;

&lt;p&gt;Aqui, o objetivo também é dar visibilidade a comportamentos causais, como quando o tráfego sobe e é necessário separar crescimento legítimo de uso do sistema de demandas de amplificação por mecanismos internos, como retries, fanout, reprocessamento, loops, cache miss em massa ou abuso indevido do serviço.&lt;/p&gt;

&lt;h3 id=&quot;errors&quot;&gt;Errors&lt;/h3&gt;

&lt;p&gt;Os &lt;strong&gt;Errors&lt;/strong&gt; são a taxa de falhas percebidas em relação ao Traffic. Na definição do livro do Google, erros podem aparecer como códigos de erro internos, como 5xx, mas também como falhas explícitas de protocolo e falhas semânticas de resultado, dependendo do que faz sentido para o sistema. A princípio, também é necessário monitorar erros do cliente (4xx) para entender comportamentos e desvios.&lt;/p&gt;

&lt;h3 id=&quot;saturation&quot;&gt;Saturation&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Saturation&lt;/strong&gt; é um sinal de proximidade de esgotamento dos recursos provisionados para a aplicação. Responde o quanto o sistema está “no limite” de algum recurso crítico e, principalmente, o quanto trabalho está se acumulando porque o recurso não consegue acompanhar. Por exemplo, o quanto do tráfego está se aproximando dos níveis de rate limit estabelecidos na API, ou o quanto o uso das &lt;strong&gt;CPUs&lt;/strong&gt; da aplicação está &lt;strong&gt;próximo&lt;/strong&gt; de um limite de risco, etc.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;referências&quot;&gt;Referências&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.redhat.com/en/topics/devops/what-is-observability&quot;&gt;What is observability?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://newrelic.com/blog/observability/what-are-slos-slis-slas&quot;&gt;What are SLOs, SLIs, and SLAs? &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://lamport.azurewebsites.net/pubs/time-clocks.pdf&quot;&gt;Time, Clocks, and the Ordering of Events in a Distributed System&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/ezziomoreira/conceitos-opentelemetry-9k0&quot;&gt;Conceitos OpenTelemetry - Ezzio Moreira&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.elastic.co/what-is/opentelemetry&quot;&gt;What is OpenTelemetry?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://sre.google/sre-book/monitoring-distributed-systems/&quot;&gt;Monitoring Distributed Systems&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://sre.google/sre-book/service-level-objectives/&quot;&gt;Service Level Objective&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://queue.acm.org/detail.cfm?id=2413037&quot;&gt;Thinking Methodically about Performance&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://firehydrant.com/blog/4-sre-golden-signals-what-they-are-and-why-they-matter/&quot;&gt;4 SRE Golden Signals (What they are and why they matter) &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://pagertree.com/learn/devops/what-is-observability/use-and-red-method&quot;&gt;USE and RED Method&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://grafana.com/blog/the-red-method-how-to-instrument-your-services/&quot;&gt;The RED Method: How to Instrument Your Services&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://thenewstack.io/monitoring-methodologies-red-and-use/&quot;&gt;Monitoring Methodologies: RED and USE &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.solarwinds.com/blog/monitoring-and-observability-with-use-and-red&quot;&gt;Monitoring and Observability With USE and RED&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://grafana.com/blog/slos-a-guide-to-setting-and-benefiting-from-service-level-objectives/&quot;&gt;SLOs: a guide to setting and benefiting from service level objectives&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.splunk.com/en_us/blog/learn/feedback-loops.html&quot;&gt;What Are Feedback Loops?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.nanoshots.com.br/2019/12/sre-slo-slis-nao-sabe-por-onde-comecar.html&quot;&gt;SLI’s, SLA’s e SLO’s :: Não sabe por onde começar com suas métricas? Comece por aqui! &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://grafana.com/blog/the-red-method-how-to-instrument-your-services/&quot;&gt;The RED Method: How to Instrument Your Services&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 19 Mar 2026 00:00:00 +0000</pubDate>
        <link>https://fidelissauro.dev/observabilidade/</link>
        <guid isPermaLink="true">https://fidelissauro.dev/observabilidade/</guid>
        
        
        <category>system-design</category>
        
        <category>engineering</category>
        
      </item>
    
      <item>
        <title>System Design - Single Point of Failure, Disaster Recovery e Continuidade Operacional</title>
        <description>&lt;p&gt;&lt;strong&gt;Em sistemas distribuídos, a confiabilidade é um dos temas de maior importância na construção de serviços.&lt;/strong&gt; Existem infinitas possibilidades que podem acarretar alta disponibilidade ou problemas de disponibilidade em um sistema, e uma das formas de identificar oportunidades de otimização de &lt;a href=&quot;/resiliencia/&quot;&gt;resiliência&lt;/a&gt;, além de &lt;a href=&quot;/capacity-planning/&quot;&gt;encontrar gargalos&lt;/a&gt;, é identificar os Pontos Únicos de Falha entre os componentes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Neste rápido capítulo, vamos explorar de forma simples esse conceito e treinar o olhar crítico para identificar possíveis riscos e oportunidades.&lt;/strong&gt; Vamos aproveitar também o gancho para elaborar um próximo passo: detalhar as estratégias de Disaster Recovery de mercado.&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot; id=&quot;MathJax-script&quot; async=&quot;&quot; src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js&quot;&gt;
&lt;/script&gt;

&lt;script type=&quot;text/x-mathjax-config&quot;&gt;


    MathJax.Hub.Config({
        jax: [&quot;input/TeX&quot;,&quot;output/HTML-CSS&quot;],
        displayAlign: &quot;left&quot;
    });
    
    
&lt;/script&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;definindo-um-single-point-of-failure&quot;&gt;Definindo um Single Point of Failure&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Um Single Point of Failure (SPoF), ou “Ponto Único de Falha”, é um termo usado para se referir a qualquer componente, serviço ou recurso centralizado cuja falha provoca a indisponibilidade total ou parcial de um ou mais sistemas.&lt;/strong&gt; Um Ponto Único de Falha pode representar um &lt;a href=&quot;/databases/&quot;&gt;Banco de Dados&lt;/a&gt;, um &lt;a href=&quot;/load-balancing/&quot;&gt;Balanceador de Carga&lt;/a&gt;, um &lt;a href=&quot;/api-gateway/&quot;&gt;API Gateway&lt;/a&gt;, um broker de mensageria ou até mesmo outro microserviço que, em caso de queda, não possua nenhum caminho alternativo para que as requisições sejam estabelecidas.&lt;/p&gt;

&lt;p&gt;Imagine que uma cidade possua como única forma de acesso uma ponte. Essa ponte seria, no mundo real, um ponto único de falha. Por mais que ainda exista a possibilidade de acesso por barco, helicóptero ou balsa, não seriam todas as pessoas que teriam acesso, e a entrada e saída, bem como o envio de recursos e afins, ainda estariam gravemente impactados. &lt;strong&gt;Isso seria um Ponto Único de Falha que gera uma indisponibilidade total ou parcial de acesso à região.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;São raros os sistemas que não possuam nenhum tipo de Ponto Único de Falha.&lt;/strong&gt; A partir disso, podemos assumir algumas premissas, como a de que, quando um SPoF falha, o sistema pode entrar em modo degradado, no melhor dos casos, ou parar completamente, no pior. Logo, quanto maior a responsabilidade de um componente, maior o impacto de sua falha caso não existam &lt;a href=&quot;/resiliencia/&quot;&gt;Fluxos de Fallback&lt;/a&gt;. Outra característica importante é que recuperações manuais ou rebuilds desses componentes levam tempo e podem causar perdas significativas.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;identificando-single-points-of-failure&quot;&gt;Identificando Single Points of Failure&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Identificar os SPoF de algum ambiente pode parecer extremamente trivial, porém pode se tornar uma tarefa árdua e de grande esforço corporativo em ambientes grandes e de larga escala&lt;/strong&gt;, pois precisamos mapear quais são as funcionalidades mais críticas, documentar cada serviço, seus clusters, nodes, servidores, databases, componentes de rede, brokers de eventos e mensagens e até fornecedores, sem falar dos times responsáveis e das formas de acionamento de cada um deles.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/spof-identiticacao.drawio.png&quot; alt=&quot;SPOF&quot;&gt;&lt;/p&gt;

&lt;p&gt;Durante esse mapeamento, é &lt;strong&gt;necessário desenhar um “fluxo feliz” de cada transação dessas funcionalidades críticas&lt;/strong&gt;, &lt;strong&gt;mapear todos os atores, desde a requisição de fato até a resposta para o usuário&lt;/strong&gt;. Em seguida, é necessário inspecionar &lt;strong&gt;quais desses componentes não possuem réplicas (/replicacao/), implementam padrões de resiliência (/resiliencia/), fallbacks e mecanismos que consigam assumir esses fluxos alternativos automaticamente&lt;/strong&gt;. São nesses atores que são encontrados &lt;strong&gt;candidatos a se tornarem pontos únicos de falha&lt;/strong&gt; em um fluxo crítico.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A identificação de “Pontos Únicos de Falha” não é uma tarefa pontual ou fácil; necessita de constante revisão arquitetural e esforço corporativo para que seja realmente efetiva&lt;/strong&gt;, ainda mais se começarmos a descer o nível de abstração de virtualização, networking, replicação entre provedores etc.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;lidando-com-single-points-of-failure&quot;&gt;Lidando com Single Points of Failure&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Existem estratégias comuns que podem nos auxiliar a corrigir e, principalmente, evitar a criação de SPoFs; nesta seção, iremos identificar, de forma macro, como endereçar algumas delas.&lt;/strong&gt; Lidar com SPoFs não significa simplesmente duplicar componentes indiscriminadamente. &lt;strong&gt;O objetivo central é reduzir o blast radius, aumentar a previsibilidade sob falha e diminuir o tempo de recuperação.&lt;/strong&gt; Toda estratégia de mitigação envolve trade-offs entre custo, complexidade, latência e consistência.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A eliminação completa de SPoFs é praticamente impossível em sistemas reais&lt;/strong&gt;, especialmente quando consideramos limitações físicas, econômicas e organizacionais. A discussão deve considerar o custo-benefício da redundância perante as necessidades e o momento da empresa. Às vezes, é muito mais benéfico aceitar a falha momentânea diante de um incidente de infraestrutura global do que lidar com os custos extensivos de manter múltiplos ambientes e redundâncias para lidar com desastres momentâneos.&lt;/p&gt;

&lt;h2 id=&quot;design-stateless-de-aplicação&quot;&gt;Design Stateless de Aplicação&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Aplicações que mantêm estado local tendem a criar afinidade entre cliente e instância, dificultando a redistribuição de carga em caso de falha.&lt;/strong&gt; Sessões armazenadas em memória, caches locais indispensáveis ou fluxos que dependem de contexto interno tornam a substituição de instâncias mais lenta e arriscada. &lt;strong&gt;Ao adotar um design stateless, o estado é externalizado para camadas distribuídas e resilientes, permitindo que qualquer instância processe qualquer requisição.&lt;/strong&gt; Isso reduz significativamente a fricção durante eventos de falha, pois a remoção de uma instância não implica perda de contexto.&lt;/p&gt;

&lt;p&gt;No entanto, externalizar o estado desloca a responsabilidade de resiliência para a camada de persistência. &lt;strong&gt;Bancos de dados, caches distribuídos e sistemas de armazenamento passam a concentrar o risco anteriormente diluído na aplicação.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;redundância-e-replicação-ativa&quot;&gt;Redundância e Replicação Ativa&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/spof-ativa.drawio.png&quot; alt=&quot;Ativa&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Uma redundância ou replicação ativa é um modelo no qual todas as instâncias e réplicas de um serviço trabalham simultaneamente para receber e processar as requisições da carga de trabalho.&lt;/strong&gt; Resumidamente, nenhuma das réplicas tem como objetivo ficar ociosa aguardando uma falha geral para que assuma o processamento primário das requisições. Esse arranjo arquitetural pode ser encontrado em réplicas de aplicações atrás de balanceadores de carga, onde todas são verificadas quanto à integridade e recebem carga quase que uniformemente mediante solicitação do serviço, ou em sistemas de mensageria, onde todas as réplicas estão conectadas aos tópicos/filas e podem receber mensagens e eventos para processar. No geral, esse tipo de arquitetura, &lt;strong&gt;quando trabalhada sem estado, permite que, na falha eventual de pequenas quantidades de réplicas, o sistema continue operando e consiga se recuperar sem gerar grandes danos&lt;/strong&gt; (ou, de preferência, nenhum dano) à experiência do cliente.&lt;/p&gt;

&lt;p&gt;Na replicação ativa, todas as instâncias operam simultaneamente processando carga real. &lt;strong&gt;Esse modelo dilui o impacto da falha de uma ou mais réplicas, pois as demais continuam absorvendo requisições.&lt;/strong&gt; Balanceadores de carga distribuindo tráfego uniformemente e consumidores paralelos processando mensagens em tópicos são exemplos mais básicos dessa abordagem.&lt;/p&gt;

&lt;p&gt;Esse modelo também pode ser encontrado em réplicas de leitura de bancos de dados que possuam estado transacional, contendo todos os dados escritos nas réplicas primárias. Além de possuir um viés de disponibilidade, onde essa &lt;strong&gt;réplica é capaz de assumir o papel de escrita em caso de falha na réplica principal&lt;/strong&gt;, pode exercer funcionalidades ativas na segregação de escrita e leitura em diferentes instâncias.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Esse modelo, entretanto, exige mecanismos consistentes de sincronização de dados, especialmente quando há escrita concorrente.&lt;/strong&gt; Em bancos de dados com réplicas de leitura promovíveis, a replicação ativa pode tanto aumentar a disponibilidade quanto melhorar a performance por meio da segregação de leitura e escrita.&lt;/p&gt;

&lt;h2 id=&quot;redundância-e-replicação-passiva&quot;&gt;Redundância e Replicação Passiva&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/spof-passiva.drawio.png&quot; alt=&quot;Passiva&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Na replicação passiva, apenas uma instância atua como primária, enquanto outra permanece em standby, aguardando falha para assumir.&lt;/strong&gt; Esse modelo reduz a complexidade quando comparado ao ativo-ativo somente em termos de replicação e latência, mas introduz dependência crítica nos mecanismos de detecção e promoção e nos faz assumir que precisamos ter ambientes altamente replicados que estejam disponíveis para assumir um chaveamento brusco ou gradual, sem piorar a experiência do cliente ou criar inconsistências irrecuperáveis.&lt;/p&gt;

&lt;p&gt;Se a falha não for detectada rapidamente ou se a promoção for manual e lenta, o tempo de indisponibilidade pode ser significativo.&lt;/p&gt;

&lt;h2 id=&quot;failover-automático&quot;&gt;Failover Automático&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/spof-circuit-breaker.drawio.png&quot; alt=&quot;Circuit Breaker&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failover automático depende de monitoramento confiável, critérios claros de decisão e mecanismos transparentes de redirecionamento de tráfego.&lt;/strong&gt; A estratégia é empregar mecanismos como &lt;a href=&quot;/resiliencia&quot;&gt;Circuit Breakers&lt;/a&gt; ou feature toggles que saibam detectar padrões de falha do sistema e realizar “chaveamentos” para fluxos alternativos. Esses fluxos podem tanto envolver o redirecionamento do tráfego para uma zona passiva, a desativação de uma zona ativo-ativa quanto assumir sistemas secundários de contingência que cumpram os mesmos objetivos com taxas menores, enfileiramentos maiores ou parceiros secundários.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;disaster-recovery&quot;&gt;Disaster Recovery&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;O Disaster Recovery é um conjunto de estratégias, processos, arquiteturas e automações projetadas para restaurar sistemas após eventos de grande impacto que ultrapassam o escopo de falhas locais corriqueiras da aplicação.&lt;/strong&gt; Desastres não são falhas normais. São eventos de grande escala que ultrapassam os limites aceitáveis de operação de um produto, como, por exemplo, quedas de Cloud Providers, incidentes climáticos, downtimes de componentes críticos e centralizados etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diferentemente da mitigação de SPoFs, que normalmente atua em nível de componente, o Disaster Recovery opera no nível de produto, com escopo amplamente sistêmico e até mesmo regional.&lt;/strong&gt; Um SPoF pode derrubar um serviço específico. Um desastre pode comprometer um datacenter inteiro, uma região de nuvem ou até múltiplos serviços simultaneamente. Incêndios, falhas elétricas, corrupção massiva de dados, erros humanos em larga escala, ataques cibernéticos ou falhas generalizadas de provedores são exemplos comuns. &lt;strong&gt;Existem alguns modos de operação de DR que podemos avaliar para implementar conforme as necessidades do cenário.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;ativo-ativo&quot;&gt;Ativo-Ativo&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/ativo-ativo.drawio.png&quot; alt=&quot;Ativo / Ativo&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arquiteturas ativo-ativo permitem que múltiplas regiões ou clusters recebam tráfego simultaneamente, elevando significativamente a disponibilidade global.&lt;/strong&gt; Contudo, essa abordagem amplia drasticamente a complexidade da consistência distribuída.&lt;/p&gt;

&lt;p&gt;Modelos multi-master exigem estratégias explícitas de resolução de conflitos, como last write wins ou estruturas convergentes como CRDTs. &lt;strong&gt;A disponibilidade aumentada vem acompanhada de maior esforço operacional e complexidade cognitiva.&lt;/strong&gt; Em muitos casos, o ativo-ativo distribui o risco, mas não o elimina; apenas torna o impacto menos concentrado.&lt;/p&gt;

&lt;h2 id=&quot;ativo-passivo&quot;&gt;Ativo-Passivo&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/ativo-passivo.drawio.png&quot; alt=&quot;Ativo / Passivo&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O modelo ativo-passivo mantém uma região primária processando tráfego, enquanto outra permanece preparada para assumir em caso de desastre.&lt;/strong&gt; Essa abordagem equilibra simplicidade e resiliência. Embora menos complexa que o ativo-ativo, ainda protege contra falhas regionais significativas.&lt;/p&gt;

&lt;p&gt;Esse modelo equilibra custo e resiliência, sendo amplamente utilizado em ambientes regulados ou que exigem consistência forte. No entanto, a sincronização contínua de dados precisa ser validada regularmente e, no momento do chaveamento, a região passiva pode lidar com consistência eventual de dados até que os mecanismos de sincronização estejam atualizados.&lt;/p&gt;

&lt;h2 id=&quot;pilot-light-luz-piloto&quot;&gt;Pilot Light (Luz Piloto)&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/pilot-light.drawio.png&quot; alt=&quot;Pilot Light&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No modelo Pilot Light, apenas os componentes essenciais permanecem ativos na região secundária, como bancos de dados replicando continuamente.&lt;/strong&gt; Os demais recursos são provisionados sob demanda durante o desastre. Essa estratégia reduz custos operacionais, mas aumenta o tempo de recuperação, pois parte da infraestrutura precisa ser ativada e escalada após o evento.&lt;/p&gt;

&lt;p&gt;O modelo Pilot Light assume explicitamente que desastres regionais são eventos raros e que parte da infraestrutura pode ser provisionada sob demanda caso ocorram. Ele reduz o custo operacional, mas aumenta o tempo de recuperação. &lt;strong&gt;O sucesso dessa estratégia depende fortemente do nível de automação para acioná-la em caso de falhas&lt;/strong&gt;, necessitando de uma quantidade significativa de testes e simulações de desastres para validar que o warm-up dos recursos em standby será provisionado de forma suficiente e em tempo hábil para o chaveamento do tráfego, sem impactar excessivamente a experiência de uso do cliente.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;métricas-e-kpis-de-recuperação&quot;&gt;Métricas e KPIs de Recuperação&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Quando estabelecemos que tanto a eliminação de desastres quanto a inexistência completa de Single Points of Failure são, de fato, impossíveis, o trabalho de projetar sistemas de alta disponibilidade operacional passa a ser sobre criar camadas de resiliência e contenção desses eventuais impactos.&lt;/strong&gt; Nesse contexto, precisamos metrificar a efetividade das estratégias empregadas ao longo do tempo para comparações, a fim de identificar se estamos degradando ou melhorando a experiência do sistema.&lt;/p&gt;

&lt;p&gt;Para elaborar essa discussão, precisamos sair do campo qualitativo das estratégias e nos focar nos aspectos quantitativos, governando as decisões por métricas. &lt;strong&gt;As principais métricas que iremos abordar são MTTD, MTBF, MTTR, RTO e RPO.&lt;/strong&gt; Elas não são independentes; formam um sistema matematicamente interligado que determina disponibilidade, risco e impacto das falhas de sistemas de forma mais profissional e embasada.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;mttd---mean-time-to-detect&quot;&gt;MTTD - Mean Time to Detect&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;O MTTD (Mean Time to Detect) representa o tempo médio entre o início de uma falha e sua detecção pelo sistema ou equipe operacional.&lt;/strong&gt; É uma métrica que mede o tempo médio que leva para uma equipe ou sistema identificar que um incidente ou falha ocorreu. Imagine um serviço de e-commerce que sofre um problema em sua base de dados, causando lentidão nas transações dos clientes. O MTTD seria o tempo desde o início dessa lentidão até o momento em que a equipe responsável é acionada para intervenção.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O cálculo do MTTD é a soma das diferenças entre a detecção e o início dos incidentes ao longo do tempo, dividida pelo número de incidentes no mesmo período.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;\begin{equation}
MTTD = \frac{\text{Diferença Entre O Início e a Detecção dos Incidentes}}{\text{Número de Incidentes}}
\end{equation}&lt;/p&gt;

&lt;p&gt;Por exemplo, temos a tabela dos últimos incidentes de uma aplicação durante um período:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Hora de Início&lt;/th&gt;
      &lt;th&gt;Hora da Detecção&lt;/th&gt;
      &lt;th&gt;Tempo Total&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;11:00 AM&lt;/td&gt;
      &lt;td&gt;12:00 PM&lt;/td&gt;
      &lt;td&gt;60 min&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;05:12 AM&lt;/td&gt;
      &lt;td&gt;05:30 AM&lt;/td&gt;
      &lt;td&gt;18 min&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;03:40 PM&lt;/td&gt;
      &lt;td&gt;04:00 PM&lt;/td&gt;
      &lt;td&gt;20 min&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;10:12 PM&lt;/td&gt;
      &lt;td&gt;10:33 PM&lt;/td&gt;
      &lt;td&gt;21 min&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;09:11 AM&lt;/td&gt;
      &lt;td&gt;10:02 AM&lt;/td&gt;
      &lt;td&gt;51 min&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Podemos calcular o MTTD do sistema da seguinte forma:&lt;/p&gt;

&lt;p&gt;\begin{equation}
MTTD = \frac{(60 + 18 + 20 + 21 + 51)}{5}
\end{equation}&lt;/p&gt;

&lt;p&gt;\begin{equation}
MTTD = \text{34 minutos}
\end{equation}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ele está diretamente ligado ao investimento de tempo e inteligência de engenharia em observabilidade sistêmica&lt;/strong&gt;, contemplando logs, métricas, traces e alertas que garantam que o time esteja sempre monitorando os indicadores corretos e recebendo notificações com antecedência.&lt;/p&gt;

&lt;p&gt;Um MTTD alto indica ausência de observabilidade em pontos importantes da solução. Em sistemas distribuídos complexos, falhas raramente são binárias no âmbito de disponível e indisponível, e sim degradam progressivamente, como aumentos de latência, formação de filas internas, saturação, erros intermitentes e timeouts em cascata. &lt;strong&gt;Um MTTD baixo é importante, pois quanto mais rápido uma falha é detectada, mais cedo o processo de recuperação pode ser iniciado.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;mttr---mean-time-to-repair&quot;&gt;MTTR - Mean Time to Repair&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;O Mean Time to Repair (MTTR) é o tempo médio necessário para reparar uma falha e restaurar o sistema à operação normal de forma completa.&lt;/strong&gt; É uma métrica que acompanha diretamente o MTTD. Em estratégias de recuperação de desastres, minimizar o MTTR é o indicador que traduz operacionalmente se estamos trabalhando corretamente os pontos de falha e reduzindo o impacto no usuário final.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O cálculo do MTTR é a soma das diferenças de tempo entre a detecção e a recuperação dos incidentes, dividida pelo número de incidentes no mesmo período.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;\begin{equation}
MTTR = \frac{\text{Diferença Entre a Detecção e a Resolução dos Incidentes}}{\text{Número de Incidentes}}
\end{equation}&lt;/p&gt;

&lt;p&gt;Por exemplo:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Hora da Detecção&lt;/th&gt;
      &lt;th&gt;Hora da Recuperação&lt;/th&gt;
      &lt;th&gt;Tempo Total&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;12:00 AM&lt;/td&gt;
      &lt;td&gt;01:30 AM&lt;/td&gt;
      &lt;td&gt;90 min&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;05:30 AM&lt;/td&gt;
      &lt;td&gt;07:15 AM&lt;/td&gt;
      &lt;td&gt;105 min&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;04:00 PM&lt;/td&gt;
      &lt;td&gt;06:12 PM&lt;/td&gt;
      &lt;td&gt;132 min&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;10:33 PM&lt;/td&gt;
      &lt;td&gt;10:55 PM&lt;/td&gt;
      &lt;td&gt;22 min&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;11:02 AM&lt;/td&gt;
      &lt;td&gt;02:15 PM&lt;/td&gt;
      &lt;td&gt;193 min&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Podemos calcular o MTTR do sistema da seguinte forma:&lt;/p&gt;

&lt;p&gt;\begin{equation}
MTTR = \frac{(90 + 105 + 132 + 22 + 193)}{5}
\end{equation}&lt;/p&gt;

&lt;p&gt;\begin{equation}
MTTR = \text{108 minutos}
\end{equation}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Um MTTR baixo significa que o time de operação sabe lidar com as falhas conhecidas e restabelecer os serviços de forma eficiente e coordenada rapidamente.&lt;/strong&gt; Um MTTR alto significa o inverso: que os times levam muito tempo para restabelecer os serviços, seja pela complexidade operacional envolvida, seja pela inexistência de processos bem definidos e documentados para recuperação de falhas.&lt;/p&gt;

&lt;p&gt;Para reduzir o MTTR, o time de engenharia precisa focar em documentação, runbooks, automações de tarefas de recuperação e self-healing, scripts de rollback, reinício de serviços, as mesmas ferramentas de diagnóstico que dão suporte ao MTTD, além de processos de escalonamento de incidentes e comunicações corporativas fortes e disseminadas culturalmente.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;mtbf---mean-time-between-failures&quot;&gt;MTBF - Mean Time Between Failures&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;O Mean Time Between Failures (MTBF) é uma métrica que indica o tempo médio esperado entre duas falhas de um mesmo sistema.&lt;/strong&gt; Basicamente, é o intervalo de tempo entre dois acionamentos graves. É um dos indicadores de confiabilidade mais importantes, pois, quanto maior o MTBF, maior a confiabilidade do sistema em questão.&lt;/p&gt;

&lt;p&gt;Diferentemente das métricas anteriores, o MTBF considera o intervalo entre o término de um incidente e o início do próximo.&lt;/p&gt;

&lt;p&gt;\begin{equation}
MTBF = \frac{\text{Soma dos Tempos de Operação Saudável}}{\text{Número de Falhas}}
\end{equation}&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Ordem&lt;/th&gt;
      &lt;th&gt;Recuperação Anterior&lt;/th&gt;
      &lt;th&gt;Próximo Início&lt;/th&gt;
      &lt;th&gt;Intervalo&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;07:15 AM&lt;/td&gt;
      &lt;td&gt;09:11 AM&lt;/td&gt;
      &lt;td&gt;116 min&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;02:15 PM&lt;/td&gt;
      &lt;td&gt;03:40 PM&lt;/td&gt;
      &lt;td&gt;85 min&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;06:12 PM&lt;/td&gt;
      &lt;td&gt;10:12 PM&lt;/td&gt;
      &lt;td&gt;240 min&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;10:55 PM&lt;/td&gt;
      &lt;td&gt;05:12 AM (dia seguinte)&lt;/td&gt;
      &lt;td&gt;377 min&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Podemos calcular o MTBF do sistema da seguinte forma:&lt;/p&gt;

&lt;p&gt;\begin{equation}
MTBF = \frac{(116 + 85 + 240 + 377)}{4}
\end{equation}&lt;/p&gt;

&lt;p&gt;\begin{equation}
MTBF = 204{,}5 \text{ minutos}
\end{equation}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Um MTBF alto sugere que um sistema é mais estável e exige menos intervenções do time técnico em ambiente produtivo.&lt;/strong&gt; Um MTBF baixo sugere que existem muitos componentes frágeis ou estratégias que precisam de revisão. Esta métrica é fundamental para o planejamento de capacidade, manutenção preventiva e avaliação da qualidade de hardware e software.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;rto---recovery-time-objective&quot;&gt;RTO - Recovery Time Objective&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;O Recovery Time Objective (RTO) é o tempo máximo aceitável que um sistema ou serviço pode ficar indisponível após a detecção de uma falha ou desastre.&lt;/strong&gt; Esse número tem interesse contratual, pois vai determinar quais ferramentas, estratégias e investimentos serão necessários para garantir a continuidade operacional. Soluções mais rápidas de recuperação geralmente são mais caras e complexas de implementar. Um RTO de “zero” significa que o sistema deve ser recuperado instantaneamente e os clientes não têm apetite para falhas em nenhum aspecto, o que é extremamente difícil e caro de alcançar. Atingir o RTO envolve projetar sistemas com redundância, automação de failover, backups eficientes e processos de restauração bem testados, identificando e criando fallbacks para o maior número possível de SPoFs conhecidos.&lt;/p&gt;

&lt;p&gt;Uma aplicação bancária transacional pode ter um RTO de 1 hora, significando que, após qualquer tipo de desastre, ela deve estar completamente operacional em, no máximo, 60 minutos. Já um blog pessoal, site institucional ou pequenos e-commerces podem ter um RTO de 12, 24 ou 48 horas, pois o impacto de uma indisponibilidade, por mais que exista, é menor. &lt;strong&gt;Os dois exemplos guiariam, por exemplo, o nível de investimento e engenharia que deve ser inserido na estratégia.&lt;/strong&gt; Garantir ambientes celulares, múltiplos shardings, arquiteturas multi-datacenter, multi-região e multi-cloud em sistemas que possuem RTOs menos exigentes não faz sentido financeiramente. Já no cenário oposto, pode justificar o investimento e a complexidade.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;rpo---recovery-point-objective&quot;&gt;RPO - Recovery Point Objective&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;O RPO (Recovery Point Objective) define a quantidade máxima de dados que pode ser perdida após um desastre.&lt;/strong&gt; Normalmente, é uma métrica relacionada à defasagem entre os dados primários e os backups e aos lags de replicação. Ela, assim como o RTO, é uma métrica contratual. Essa métrica guia o nível de investimento necessário em backups e estratégias de replicação de dados. Se backups ocorrem a cada 12 horas, tenho um RPO de 12 horas. Se possuo 5 minutos de lag de replicação entre os dados de um sistema primário e secundário, meu RPO é de 5 minutos.&lt;/p&gt;

&lt;p&gt;Um RPO baixo ou próximo de zero significa que a perda de dados deve ser mínima ou inexistente, exigindo soluções de replicação contínua ou backups muito frequentes. Um RPO de “zero” normalmente implica replicação síncrona ou soluções de banco de dados distribuídos altamente consistentes entre todas as zonas e regiões secundárias do dado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O nível de criticidade do dado guia a necessidade do RPO.&lt;/strong&gt; Sistemas financeiros, transacionais, hospitalares ou de aviação precisam ter acordos de RPO mais criteriosos. Sistemas como redes sociais, sistemas institucionais e afins podem lidar com opções mais flexíveis.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;referencias&quot;&gt;Referencias&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://levelup.gitconnected.com/single-point-of-failure-spof-in-system-design-c8bbac5af993&quot;&gt;Single Point of Failure (SPOF) in System Design&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Single_point_of_failure&quot;&gt;Single point of failure&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.ibm.com/docs/en/zos/3.1.0?topic=data-what-is-single-point-failure&quot;&gt;What is a single point of failure?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.anomali.com/blog/why-single-point-of-failure-is-scary&quot;&gt;Why a Single Point of Failure (SPOF) is Scary&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://bryghtpath.com/single-point-failures/&quot;&gt;Understanding Single Point Failures: A Guide to System Resilience&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www-logicmonitor-com.translate.goog/blog/whats-the-difference-between-mttr-mttd-mttf-and-mtbf?_x_tr_sl=en&amp;amp;_x_tr_tl=pt&amp;amp;_x_tr_hl=pt&amp;amp;_x_tr_pto=tc&quot;&gt;Qual a diferença entre MTTR, MTBF, MTTD e MTTF?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.splunk.com/en_us/blog/learn/mean-time-to-detect-mttd.html&quot;&gt;What Is MTTD? The Mean Time to Detect Metric, Explained&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.sentinelone.com/blog/mttd-mean-time-to-detect-detailed-explanation/&quot;&gt;What Is MTTD (Mean Time to Detect)? A Detailed Explanation&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 10 Mar 2026 00:00:00 +0000</pubDate>
        <link>https://fidelissauro.dev/single-point-of-failure/</link>
        <guid isPermaLink="true">https://fidelissauro.dev/single-point-of-failure/</guid>
        
        
        <category>system-design</category>
        
        <category>engineering</category>
        
        <category>cloud</category>
        
      </item>
    
      <item>
        <title>System Design - Cell-Based Architecture</title>
        <description>&lt;p&gt;&lt;strong&gt;A arquitetura celular é um tema particularmente especial pra mim, olhando para o próximo passo de sistemas distribuídos de ambientes críticos; o tema é particularmente fascinante.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esse foi o tema da minha pesquisa de mestrado, e depois de bastante tempo tentando consolidar o tema academicamente, decidi que tenho material o suficiente pra compor mais um capítulo dessa série de artigos com o tema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fui introduzido ao conceito há pelo menos 4 anos antes da escrita desse texto, através de uma iniciativa interna da empresa na qual trabalho, como uma proposta de alavancar os níveis de alta disponibilidade.&lt;/strong&gt; Quando fui confrontado por “qual seria o tema da minha pesquisa de mestrado”, tive a ideia de alavancar um conceito emergente de mercado academicamente, e Arquitetura Celular e seus arredores serviram como uma luva. Baita desafio. &lt;strong&gt;Precisei consolidar conceitos já firmados em mercado e academia, como replicação, bulkheads, isolamento de falhas e demais tecnologias cloud native, para sustentar o termo.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Esse texto se baseia em uma alternativa mais leve e menos formal de abordar o tema.&lt;/strong&gt;&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot; id=&quot;MathJax-script&quot; async=&quot;&quot; src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js&quot;&gt;
&lt;/script&gt;

&lt;script type=&quot;text/x-mathjax-config&quot;&gt;


    MathJax.Hub.Config({
        jax: [&quot;input/TeX&quot;,&quot;output/HTML-CSS&quot;],
        displayAlign: &quot;left&quot;
    });
    
    
&lt;/script&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;definindo-a-arquitetura-celular&quot;&gt;Definindo a Arquitetura Celular&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;O modelo de Arquitetura Celular é um modelo de arquitetura descentralizada onde as capacidades de uma organização são estruturadas em uma rede de células independentes e autocontidas, como uma evolução do que entendemos pelo &lt;a href=&quot;/bulkheads&quot;&gt;Bulkhead Pattern&lt;/a&gt;.&lt;/strong&gt; Uma distinção importante que vale reforçar é que a Arquitetura Celular não é simplesmente uma técnica de particionamento horizontal sofisticado; ela é um pattern avançado na forma como modelamos domínios de falha.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O conceito que conecta os bulkheads à Arquitetura Celular em sistemas complexos é a proposta de criar fronteiras de isolamento de falhas, garantindo que o impacto de um erro seja restrito a um número limitado de componentes, sem afetar o restante do ecossistema&lt;/strong&gt;, com o adicional de componentes de replicação de dados entre células para conter ainda mais o escopo de uma eventual falha isolada.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;unidades-celulares&quot;&gt;Unidades Celulares&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Uma célula não é apenas um agrupamento técnico de serviços. Ela representa uma segmentação arquitetural explícita.&lt;/strong&gt; Essa segmentação deve existir em múltiplas dimensões simultaneamente, como segmentação de execução (capacidade computacional e capacidade de escalabilidade horizontal isolada e autocontida), segmentação de persistência, isolando databases independentes, segmentação de observabilidade, isolando métricas, logs e traces segmentados por contextos celulares, segmentação de deploy, possuindo pipelines complexos de deployment para atualizar células sem impacto direto ao seu público, e segmentação de falha, onde temos blast radius mensuráveis e segmentados.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O compartilhamento de componentes globais como filas, tópicos, caches e databases compartilhados invalida esse isolamento.&lt;/strong&gt; Caso exista a necessidade, por exemplo, tópicos de comando e resposta por domínio e API Gateways centralizados, eles devem ser intermediados por outros componentes celulares de borda.&lt;/p&gt;

&lt;h2 id=&quot;dimensão-estrutural-de-uma-célula&quot;&gt;Dimensão estrutural de uma célula&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Uma célula é um compilado de um ou mais componentes (microsserviços, funções, databases, gateways, etc.) agrupados desde o design até a implementação e implantação.&lt;/strong&gt; Estruturalmente, ela possui as características de isolamento e independência, onde cada célula, ou conjunto de células, é responsável por atender uma parcela determinada do público de forma autocontida, e toda comunicação externa deve ocorrer obrigatoriamente através de um gateway de borda ou proxy, que expõe APIs, eventos ou streams de dados.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/cell-estrutura.png&quot; alt=&quot;Estrutura Celular&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Os componentes internos comunicam-se de forma contínua intra-celular, enquanto dependências externas são mediadas pelo gateway da célula.&lt;/strong&gt; Os componentes internos da célula só podem conhecer e se comunicar com componentes da própria célula, nunca de outra. Cada célula possui um nome e um identificador de versão único, facilitando o gerenciamento de dependências no ecossistema distribuído e resiliente.&lt;/p&gt;

&lt;h2 id=&quot;isolamento-de-estado&quot;&gt;Isolamento de estado&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Uma característica determinística da implementação da arquitetura celular é que as células não compartilham estado com outras células de forma primária, apenas por replicação passiva.&lt;/strong&gt; Em termos de persistência, uma célula pode conter seus próprios clusters de bancos de dados relacionais, sistemas de arquivos locais ou repositórios de dados necessários para cumprir sua função de negócio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cada unidade é independente e lida com um subconjunto específico das requisições totais do sistema e pode ter unidades passivas que assumem a liderança dos dados replicados em caso de falha da célula principal.&lt;/strong&gt; No mais, cada célula deve possuir seus próprios microserviços, seus próprios bancos de dados, camadas de cache, consumidores de filas e eventos e demais componentes, de forma que sejam autocontidas e independentes entre si.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Esse modelo permite inclusive estratégias diferenciadas por célula.&lt;/strong&gt; Uma célula pode operar com parâmetros de tuning diferentes, versões distintas de runtime ou até estratégias experimentais de feature rollout sem impactar o restante do ecossistema. Podemos isolar clientes de teste, pilotos e públicos sintéticos para experimentação antes de propagar versões para as demais células produtivas.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;estratégias-de-roteamento-e-direcionamento-para-células&quot;&gt;Estratégias de roteamento e direcionamento para células&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;O princípio fundamental é que toda requisição deve ser roteada para uma célula específica com base em uma chave estável, como customerId, accountId ou tenantId.&lt;/strong&gt; Esse roteamento pode ocorrer em múltiplas camadas: DNS, API Gateway, proxies de borda ou service mesh.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;É de grande importância para a solução que o algoritmo de roteamento seja determinístico, garantindo que requisições relacionadas ao mesmo estado sempre atinjam a mesma célula ativa.&lt;/strong&gt; Em cenários de failover, o roteador deve ser capaz de redirecionar para a célula passiva correspondente sem que o cliente perceba a transição. Se um cliente hoje está na célula X, amanhã ele deve continuar na célula X, independentemente de picos de tráfego. &lt;strong&gt;Mudanças no algoritmo de hashing ou no número de células devem ser cuidadosamente orquestradas, pois podem causar remapeamento massivo (rehash storm).&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;edge-cells---células-de-borda&quot;&gt;Edge Cells - Células de Borda&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/cell-edge-cells.png&quot; alt=&quot;Edge Cells&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A camada de roteamento que intercepta as comunicações dos clientes e realiza o redirecionamento para a célula, ou grupo de células correto, é conhecida como “Edge Cells”, ou “Células de Borda”&lt;/strong&gt;, uma camada de roteamento inteligente que deve ser capaz de realizar, da forma mais performática possível, a interceptação das solicitações, sejam elas vindas de qualquer protocolo conhecido, e &lt;strong&gt;redirecionar de maneira correta para a célula disponível responsável por atender a solicitação.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;É preferível que esta camada seja o mais stateless possível, mas é possível que a mesma mantenha um estado cadastral em alguma camada de dados adicional.&lt;/strong&gt; Aqui vamos além de um proxy básico como um Nginx, Envoy e Haproxy; é uma aplicação inteligente e agnóstica para uso celular que deve ser capaz de absorver alto tráfego e gerenciar o capacity global das camadas celulares de aplicação. Ela precisa ser extremamente resiliente e, paradoxalmente, altamente distribuída para não se tornar o novo ponto único de falha.&lt;/p&gt;

&lt;h2 id=&quot;células-e-segmentação-de-carga&quot;&gt;Células e segmentação de carga&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A segmentação de carga na Arquitetura Celular é uma decisão estrutural de como os dados serão divididos e replicados entre as células.&lt;/strong&gt; Já abordamos esse tema profundamente no capítulo de &lt;a href=&quot;/sharding&quot;&gt;sharding e particionamento&lt;/a&gt;. Isso vai muito além de um particionamento horizontal de dispersar throughput entre vários estanques isolados de capacidade. Em arquiteturas tradicionais, o &lt;a href=&quot;load-balancing/&quot;&gt;load balancer&lt;/a&gt; distribui requisições de maneira estatística através de vários algoritmos como round robin, least connection e afins, mas o estado permanece logicamente compartilhado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Já em uma arquitetura celular, a segmentação é determinística e vinculada a uma chave de negócio estável, podendo ser tratada de forma cadastral e mapeamento intencional, ou distribuída estatisticamente através de algoritmos de hashing e hashing consistente.&lt;/strong&gt; Isso significa que cada célula absorve um subconjunto fixo e determinístico da carga total, e essa distribuição não varia dinamicamente conforme a pressão momentânea do sistema; os mesmos clientes sempre serão atendidos pela mesma célula, ou conjunto de células.&lt;/p&gt;

&lt;h2 id=&quot;células-síncronas&quot;&gt;Células Síncronas&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;No contexto síncrono, o roteamento ocorre no caminho crítico da requisição.&lt;/strong&gt; HTTP, gRPC ou mesmo protocolos binários proprietários são direcionados para uma célula específica antes da execução do fluxo transacional.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/cell-http-layer.png&quot; alt=&quot;HTTP Layer&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aqui presumimos um gateway de borda que recebe todas as requisições de domínio.&lt;/strong&gt; Esse gateway tem a função de atuar como um proxy de encaminhamento inteligente, como um roteador que sabe identificar deterministicamente, através de &lt;strong&gt;chaves conhecidas como ids de clientes, tenants, usuários, e direcionar para a célula, ou conjunto de células correspondente&lt;/strong&gt;. Esse mecanismo de roteamento e proxy pode operar baseado em DNS, Hashing Consistente, roteamento via Service Mesh ou de forma cadastral, consultando fontes externas para determinar onde o cliente será direcionado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Em cenários síncronos, a latência da célula é diretamente percebida pelo usuário.&lt;/strong&gt; Portanto, &lt;strong&gt;cada célula deve ser dimensionada como unidade autônoma de performance&lt;/strong&gt;. CPU, memória, conexões de banco, thread pools e limites de rate limiting devem ser configurados por célula, não globalmente. Cada célula precisa ter sua capacity isolada e independente.&lt;/p&gt;

&lt;h2 id=&quot;células-assíncronas&quot;&gt;Células Assíncronas&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Quando entramos no domínio assíncrono, a arquitetura celular assume ainda mais capacidade e estratégia de desacoplamento estrutural.&lt;/strong&gt; Em cenários de arquitetura celular que são acionadas por eventos em tópicos ou mensagens em filas, cada célula consome apenas as mensagens e eventos pertencentes a seu contexto.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/cell-async-layer.png&quot; alt=&quot;Async Layer&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Podemos presumir um consumidor de borda que consome alguma fila ou tópico de domínio e republica as mensagens ou eventos para tópicos e filas segmentados da célula, atuando como um filtro roteador da mensagem em contexto para sua célula específica, que por sua vez só conhece seus próprios mecanismos de mensageria.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A consequência é a eliminação do acoplamento temporal entre células.&lt;/strong&gt; Uma célula pode atrasar processamento, sofrer backpressure ou mesmo ficar indisponível sem bloquear o restante do sistema. Ao segmentar tópicos e filas por célula, eliminamos o risco de backpressure global. &lt;strong&gt;Uma célula pode acumular backlog sem afetar a taxa de processamento das demais.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;replicação-celular&quot;&gt;Replicação Celular&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;No modelo celular, a replicação é direcionada para a criação de células passivas que atuam como espelhos de células ativas nos requisitos de dados.&lt;/strong&gt; Cada célula é projetada como uma unidade autocontida, incluindo todos os componentes de execução e armazenamento necessários para sua operação independente; porém, podemos assumir conjuntos de células passivas que recebem os dados de células ativas, prioritariamente com consistência eventual e replicação assíncrona através de componentes adicionais, ou com consistência forte, criando um modelo transacional de “Two-Phase Commit”, garantindo que todas as células participantes da replicação celular irão confirmar a transação ou ela será inteiramente abortada.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/cell-replication.png&quot; alt=&quot;Replicação&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O foco na replicação para células passivas garante que falhas críticas como bugs, erros de deploy ou as chamadas poison pill requests (requisições corrompidas que derrubam o serviço) sejam contidas dentro da fronteira da célula afetada, mas que o cliente seja redirecionado para uma célula passiva para a qual seus dados estejam sendo replicados de forma transparente.&lt;/strong&gt; Como cada célula atende a apenas um subconjunto das requisições totais, assumindo um roteamento forte por chave de partição, a perda de uma célula principal não resulta em um apagão da experiência do cliente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isso muda completamente a forma como modelamos risco sistêmico.&lt;/strong&gt; Em vez de perguntar: &lt;em&gt;“Qual o impacto da falha de um shard?”&lt;/em&gt;, passamos a perguntar: &lt;em&gt;“Qual a probabilidade de um cliente estar alocado exatamente no subconjunto de células que falhou simultaneamente?”&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;replicação-assíncrona-entre-células&quot;&gt;Replicação Assíncrona entre Células&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A replicação assíncrona entre células de uma arquitetura desse tipo é o modelo mais comum dentro de arquiteturas celulares, principalmente quando podemos abrir mão de uma consistência forte nos critérios de alta disponibilidade e tolerância a falhas.&lt;/strong&gt; Nesse modelo, a célula ativa é a fonte primária de escrita, enquanto células passivas recebem atualizações de estado por meio de streams de eventos, logs de mudança ou filas assíncronas.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/cell-replicacao-async.png&quot; alt=&quot;Replicação Assíncrona Entre Células&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O custo desse modelo é a aceitação da consistência eventual.&lt;/strong&gt; O objetivo dessa estratégia é a propagação dos dados entre as células ativas e passivas fora do que consideramos o “caminho crítico” transacional do cliente, &lt;strong&gt;permitindo que as operações da célula continuem atendendo com baixa latência, mesmo sob carga elevada e saturação da célula&lt;/strong&gt;. Em uma falha súbita da célula ativa, a célula passiva pode assumir com um pequeno atraso de estado.&lt;/p&gt;

&lt;h2 id=&quot;replicação-consistente-entre-células&quot;&gt;Replicação Consistente entre Células&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A replicação consistente entre células surge quando o domínio de negócio não tolera divergência de estado, mesmo que temporária, em uma eventual mudança de responsabilidade entre uma célula ativa e passiva.&lt;/strong&gt; Nesses cenários, a arquitetura celular precisa incorporar mecanismos de coordenação distribuída, como Two-Phase Commit (2PC) ou variações mais modernas de consenso, para garantir um estado transacional em todas as células do conjunto do contexto.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/cell-replicacao-sync.png&quot; alt=&quot;Replicação Síncrona Entre Células&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Esse modelo assume mais complexidade e riscos, onde múltiplas células participam de uma transação distribuída, garantindo que o estado só seja considerado confirmado quando todas as células envolvidas reconhecem a operação e, em caso de qualquer participante falhar, a transação inteira é abortada, preservando uma integridade global.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Embora conceitualmente elegante, esse modelo introduz acoplamento temporal entre células, aumenta a latência e reduz a capacidade de isolamento absoluto de falhas.&lt;/strong&gt; Por isso, sua aplicação deve ser extremamente criteriosa, restrita a fluxos realmente críticos e evitando um volume que possa desencadear uma saturação em cascata em todas as células participantes do conjunto.&lt;/p&gt;

&lt;h2 id=&quot;replicação-e-shuffle-sharding&quot;&gt;Replicação e Shuffle Sharding&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A combinação de arquitetura celular com shuffle sharding representa uma das estratégias mais eficientes para reduzir impacto sistêmico em larga escala e aplicar a replicação cross-celular.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/cell-shuffle.png&quot; alt=&quot;Shuffle Sharding&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Em vez de associar cada cliente ou tenant a uma única célula fixa, o shuffle sharding mapeia cada entidade a um subconjunto estável de células, calculado por hashing consistente.&lt;/strong&gt; Assim, um cliente interage apenas com um pequeno grupo de células, e não com o sistema inteiro, assumindo que seus dados estão replicados entre todas elas de forma consistente ou assíncrona.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quando uma célula falha, apenas os clientes cujo conjunto inclui aquela célula são afetados.&lt;/strong&gt; Os demais continuam operando normalmente. Isso reduz drasticamente o blast radius estatístico, mesmo em sistemas com milhares ou milhões de clientes. &lt;strong&gt;Quando aplicamos o shuffle sharding, os clientes afetados podem ser redirecionados para uma célula ao lado&lt;/strong&gt;, para a qual seus dados foram replicados; dessa forma, só começamos a calcular o blast radius a partir da falha de duas ou mais células (dependendo da quantidade de replicação cross-celular dos dados), e &lt;strong&gt;reduzimos a porcentagem de impacto para a probabilidade de os clientes estarem em todo o conjunto de células indisponíveis&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;replicação-e-blast-radius&quot;&gt;Replicação e Blast Radius&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A principal característica da arquitetura celular, quando combinada com replicação, é a previsibilidade do impacto de falhas.&lt;/strong&gt; Como vimos no exemplo dos Bulkheads, se uma carga de trabalho é distribuída igualmente entre 10 shards e uma delas falha, 90% dos usuários ou recursos permanecem operacionais e inalterados. Quando confrontamos com a proposta da Arquitetura Celular com replicação, o número de bulkheads ou shards computacionais, se &lt;strong&gt;presumirmos uma segmentação uniforme de carga, impacta diretamente a porcentagem de indisponibilidade&lt;/strong&gt; em caso da falha de uma parcela isolada dessa segmentação.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do ponto de vista matemático, se temos N células e roteamento uniforme por hashing consistente, cada célula tende a absorver aproximadamente 1/N da carga total.&lt;/strong&gt; Isso permite modelar blast radius como função direta da cardinalidade de células.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Bulkheads&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Blast Radius&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Disponibilidade&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;100%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;50%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;50%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;3&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;33%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;66%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;20%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;80%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;10&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;10%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;90%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;20&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;95%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;50&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;2%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;98%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;100&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;99%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;A literatura clássica de sistemas distribuídos mostra que a replicação é um mecanismo-chave para garantir disponibilidade e continuidade operacional, permitindo que o sistema mantenha o serviço mesmo diante de falhas de nós ou partições de rede.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do ponto de vista conceitual, células podem ser compreendidas como domínios de falha isolados, alinhados ao padrão arquitetural de bulkheads, cujo objetivo é compartimentalizar o impacto de incidentes.&lt;/strong&gt; Quando trabalhamos com a replicação celular e temos a capacidade de redirecionar nossos clientes para células passivas que contenham seus dados, conseguimos adicionar ainda mais camadas de disponibilidade na experiência do cliente. O impacto de uma partição indisponível deixa de ser a métrica estatística apropriada, pois um shard indisponível pode ser suprido por sua versão passiva. Nesse caso, em níveis de replicação, passamos a estimar o impacto a partir de um conjunto maior de células indisponíveis, trabalhando com a &lt;strong&gt;probabilidade de um cliente estar alocado no conjunto todo que falhou.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O cálculo se baseia em células em status de falha (f) dividido pelo número total de células (N), elevado ao número de réplicas virtuais (k) do Shuffle Sharding.&lt;/p&gt;

&lt;p&gt;\begin{equation}
P(\text{impacto}) \approx \left( \frac{f}{N} \right)^k
\end{equation}&lt;/p&gt;

&lt;p&gt;Em exemplo, presumindo que trabalhamos com 20 células, 2 réplicas em shuffle, onde o mesmo dado de um cliente é alocado em 2 células, em caso de downtime de 2 células aleatórias, conseguimos calcular a probabilidade de um mesmo cliente estar alocado justamente nessas 2 células. &lt;strong&gt;Nesse caso, 1% de probabilidade. Comparado ao exemplo dos bulkheads, onde para ter 1% de impacto determinístico, precisaríamos de 100 bulkheads ou shards computacionais para ter o mesmo resultado de 20 células com fator de replicação de 2&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;\begin{equation}
P(\text{impacto}) \approx \left( \frac{2}{20} \right)^2
\end{equation}&lt;/p&gt;

&lt;p&gt;\begin{equation}
P(\text{impacto}) = \text{1%}
\end{equation}&lt;/p&gt;

&lt;p&gt;Quando ajustamos o número de réplicas em shuffle, diminuímos ainda mais a probabilidade de impacto, pois para existir um downtime total de um cliente, precisaríamos presumir uma quantidade cada vez maior de células inativas.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Células Totais&lt;/th&gt;
      &lt;th&gt;Células Indisponíveis&lt;/th&gt;
      &lt;th&gt;Réplicas em Shuffle&lt;/th&gt;
      &lt;th&gt;Probabilidade de Impacto do Cliente&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;20&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;1%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;20&lt;/td&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;0.33%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;20&lt;/td&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;0.009%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;referências&quot;&gt;Referências&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=7IUgTNcPFlU&quot;&gt;BR AWS re:Invent 2022 - Camada Zero: A real-world architecture framework (PRT268)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.bytebytego.com/p/a-crash-course-on-cell-based-architecture&quot;&gt;A Crash Course on Cell-based Architecture&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://wso2.com/library/conference/2025/07/mastering-cell-based-architecture-for-modern-enterprises&quot;&gt;Mastering Cell-Based Architecture for Modern Enterprises&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/wso2/reference-architecture/blob/master/reference-architecture-cell-based.md&quot;&gt;Cell-Based Architecture Reference&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://wso2.com/library/conference/2024/05/cloud-native-middleware-domain-driven-design-cell-based-architecture-service-mesh-and-more/&quot;&gt;Cloud Native Middleware: Domain-Driven Design, Cell-Based Architecture, Service Mesh, and More&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://wso2.com/wso2_resources/wso2-reference-architecture-for-agility-version-0-9.pdf&quot;&gt;Reference Architecture for Agility, Version-0.9&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/wellarchitected/latest/reducing-scope-of-impact-with-cell-based-architecture/what-is-a-cell-based-architecture.html&quot;&gt;What is a cell-based architecture?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/solutions/guidance/cell-based-architecture-on-aws/&quot;&gt;Guidance for Cell-Based Architecture on AWS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://martinfowler.com/articles/patterns-of-distributed-systems/two-phase-commit.html&quot;&gt;Two-Phase Commit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://aws-amazon-com.translate.goog/blogs/architecture/shuffle-sharding-massive-and-magical-fault-isolation/?_x_tr_sl=en&amp;amp;_x_tr_tl=pt&amp;amp;_x_tr_hl=pt&amp;amp;_x_tr_pto=tc&quot;&gt;Shuffle Sharding: Massive and Magical Fault Isolation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.wedaa.tech/docs/blog/2024/05/05/Bulkhead-Pattern&quot;&gt;Bulkhead Pattern -&amp;gt; Cell based architecture&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 02 Mar 2026 00:00:00 +0000</pubDate>
        <link>https://fidelissauro.dev/cell-based/</link>
        <guid isPermaLink="true">https://fidelissauro.dev/cell-based/</guid>
        
        
        <category>system-design</category>
        
        <category>engineering</category>
        
      </item>
    
      <item>
        <title>Blueprint - Rate Limit por Pods com Istio Service Mesh</title>
        <description>&lt;p&gt;Este blueprint tem objetivo de mostrar a forma de utilizar o EnvoyFilter para implementar um rate limit local, a nível de pod, para preservar o capacity a nível proativo e restritivo de cada unidade da aplicação.&lt;/p&gt;

&lt;p&gt;A prova de conceito busca validar formas de estabalecer uma restrição estável e fixa do quanto “cada pod da aplicação pode receber sem degradar”. Uma vez que essa informação é conhecida, podemos implementar rate limit de forma granular para preservar a saturação progressiva de cada unidade computacional. Para isso, vamos utilizar o Istio Service Mesh e o Envoy Filter para configurar a estabilidade local do serviço.&lt;/p&gt;

&lt;p&gt;Se um cliente concentrar toda a carga em apenas um Pod (por afinidade de conexão ou hash), ele estará limitado a 3 TPS. Isso significa que o sistema, mesmo escalado horizontalmente, ainda respeita limites individuais de cada pod em execução, e não globalmente.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;entendendo-o-algoritmo-de-token-bucket-do-envoy-proxy&quot;&gt;Entendendo o Algoritmo de Token Bucket do Envoy Proxy&lt;/h1&gt;

&lt;p&gt;O algoritmo de Token Bucket implementado pelo Envoy é um &lt;strong&gt;mecanismo de controle de taxa de requisição baseado numa ideia de crédito acumulativo de acordo com o tamanho do bucket&lt;/strong&gt; e sua taxa de reposição.&lt;/p&gt;

&lt;p&gt;Diferente de abordagens puramente restritivas como fixed window counters, &lt;strong&gt;o token bucket permite absorver pequenos bursts controlados, mantendo previsibilidade sob carga sustentada.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No contexto do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;envoy.filters.http.local_ratelimit&lt;/code&gt;, cada sidecar mantém localmente um bucket com três parâmetros fundamentais:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;max_tokens&lt;/strong&gt; — capacidade máxima do bucket.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;tokens_per_fill&lt;/strong&gt; — quantidade de tokens adicionados a cada intervalo.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;fill_interval&lt;/strong&gt; — periodicidade de reposição.&lt;/li&gt;
  &lt;li&gt;
    &lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;stat_prefix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http_local_rate_limiter&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;token_bucket&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;max_tokens&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tokens_per_fill&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;fill_interval&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1s&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Caso o &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max_tokens&lt;/code&gt; e o &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tokens_per_fill&lt;/code&gt; possuam valores iguais, &lt;strong&gt;passamos convergir para um algoritmo de Leaky Bucket, onde não acumulamos creditos no bucket e passamos a aceitar e recusar requisições sobre uma taxa estável e fixa&lt;/strong&gt;, cenário cujo qual vamos abornar dessa prova de conceito.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;implementação-via-envoyfilter&quot;&gt;Implementação via EnvoyFilter&lt;/h1&gt;

&lt;p&gt;A implementação via EnvoyFilter insere o filtro HTTP &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local_ratelimit&lt;/code&gt; diretamente no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTTP Connection Manager&lt;/code&gt; do listener inbound do sidecar. O uso de context: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SIDECAR_INBOUND&lt;/code&gt; garante que o controle seja aplicado na entrada do Pod, protegendo o workload antes mesmo da requisição atingir o container da aplicação&lt;/p&gt;

&lt;h2 id=&quot;exemplo---3-transações-por-segundo-por-pod&quot;&gt;Exemplo - 3 Transações por Segundo por Pod&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/istio-rate-limit-3.png&quot; alt=&quot;Exemplo de Rate Limit com 3 TPS&quot;&gt;&lt;/p&gt;

&lt;p&gt;O cenário de entrada busca estabelecer um limite local de 3 transações por segundo para cada POD em serviço na estratégia de leaky bucket, ou seja, não existe margem para absorver picos de crédito.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;2 pods&lt;/li&gt;
  &lt;li&gt;max_tokens: 3&lt;/li&gt;
  &lt;li&gt;tokens_per_fill: 3&lt;/li&gt;
  &lt;li&gt;fill_interval: 1s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O throughput máximo agregado será aproximadamente:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;3 TPS × 2 Pods = 6 TPS
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;exemplo-completo&quot;&gt;Exemplo Completo&lt;/h4&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;networking.istio.io/v1alpha3&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;EnvoyFilter&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;shard-router-local-rate-limit&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;istio-system&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;workloadSelector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;shard-router&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;configPatches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;applyTo&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;HTTP_FILTER&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SIDECAR_INBOUND&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;listener&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;filterChain&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;envoy.filters.network.http_connection_manager&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;INSERT_BEFORE&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;envoy.filters.http.local_ratelimit&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;typed_config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@type&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;type.googleapis.com/udpa.type.v1.TypedStruct&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;type_url&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;stat_prefix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http_local_rate_limiter&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;token_bucket&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;max_tokens&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;tokens_per_fill&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;fill_interval&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1s&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;filter_enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;runtime_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;local_rate_limit_enabled&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;default_value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;numerator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;denominator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;HUNDRED&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;filter_enforced&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;runtime_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;local_rate_limit_enforced&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;default_value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;numerator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;denominator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;HUNDRED&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;response_headers_to_add&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;x-local-rate-limit&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&apos;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;exemplo---10-transações-por-segundo-por-pod&quot;&gt;Exemplo - 10 Transações por Segundo por Pod&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/istio-rate-limit-10.png&quot; alt=&quot;Exemplo de Rate Limit com 10 TPS&quot;&gt;&lt;/p&gt;

&lt;p&gt;O segundo cenário busca estabelecer um limite local de 10 transações por segundo também na estratégia de leaky bucket.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;2 pods&lt;/li&gt;
  &lt;li&gt;max_tokens: 10&lt;/li&gt;
  &lt;li&gt;tokens_per_fill: 10&lt;/li&gt;
  &lt;li&gt;fill_interval: 1s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O throughput máximo agregado será aproximadamente:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;10 TPS × 2 Pods = 20 TPS
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;exemplo-completo-1&quot;&gt;Exemplo Completo&lt;/h4&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;networking.istio.io/v1alpha3&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;EnvoyFilter&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;shard-router-local-rate-limit&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;istio-system&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;workloadSelector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;shard-router&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;configPatches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;applyTo&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;HTTP_FILTER&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SIDECAR_INBOUND&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;listener&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;filterChain&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;envoy.filters.network.http_connection_manager&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;INSERT_BEFORE&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;envoy.filters.http.local_ratelimit&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;typed_config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@type&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;type.googleapis.com/udpa.type.v1.TypedStruct&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;type_url&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;stat_prefix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http_local_rate_limiter&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;token_bucket&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;max_tokens&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;tokens_per_fill&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;fill_interval&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1s&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;filter_enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;runtime_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;local_rate_limit_enabled&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;default_value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;numerator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;denominator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;HUNDRED&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;filter_enforced&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;runtime_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;local_rate_limit_enforced&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;default_value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;numerator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;denominator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;HUNDRED&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;response_headers_to_add&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;x-local-rate-limit&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&apos;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 18 Feb 2026 00:00:00 +0000</pubDate>
        <link>https://fidelissauro.dev/istio-rate-limit/</link>
        <guid isPermaLink="true">https://fidelissauro.dev/istio-rate-limit/</guid>
        
        
        <category>istio</category>
        
        <category>kubernetes</category>
        
        <category>load-balancing</category>
        
        <category>service-mesh</category>
        
      </item>
    
      <item>
        <title>System Design - Bulkhead Pattern</title>
        <description>&lt;p&gt;O termo &lt;strong&gt;“Bulkhead”&lt;/strong&gt; foi amplamente discutido em vários capítulos desta série de artigos, e o objetivo deste é ilustrar as nuances focadas nesse pattern em sua totalidade. &lt;strong&gt;Quando discutimos bulkheads, abordamos uma ampla gama de implementações e possibilidades&lt;/strong&gt;, desde as mais internas, em nível de runtimes, até amplas aplicações arquiteturais e segmentações de operações e clientes. &lt;strong&gt;O objetivo deste artigo é ilustrar as principais capacidades desse tipo de pattern&lt;/strong&gt;, bem como os tipos de vantagens e desvantagens em discussão.&lt;/p&gt;

&lt;h1 id=&quot;definindo-bulkheads&quot;&gt;Definindo Bulkheads&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;O Bulkhead Pattern é um padrão arquitetural de contenção de falhas&lt;/strong&gt;, mas cujo objetivo central não é evitar que falhas aconteçam, e sim garantir que uma eventual adversidade em uma parte do sistema não se propague e o comprometa por inteiro. Ele parte do pressuposto de que falhas são inevitáveis em sistemas distribuídos e, portanto, &lt;strong&gt;devem ser estruturalmente esperadas, limitadas e absorvidas em diversas dimensões&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A essência do Bulkhead não está em mecanismos de retry, timeout ou fallbacks&lt;/strong&gt;, mas na separação explícita de domínios operacionais. Quando corretamente aplicado, &lt;strong&gt;o sistema deixa de ser um bloco homogêneo e passa a se comportar como um conjunto de compartimentos independentes&lt;/strong&gt;, cada um com capacidade, escalabilidade, limites e impacto bem definidos.&lt;/p&gt;

&lt;h2 id=&quot;bulkheads-e-a-engenharia-naval&quot;&gt;Bulkheads e a Engenharia Naval&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/bulkhead-naval.jpg&quot; alt=&quot;Bulkhead Naval&quot;&gt;&lt;/p&gt;

&lt;p&gt;O termo &lt;strong&gt;Bulkhead&lt;/strong&gt; tem sua origem na engenharia naval. Dentro dela, bulkheads são paredes estruturais internas que dividem o casco de um navio em compartimentos isolados, para que, se por ventura ocorrer um dano no casco e um compartimento for perfurado, apenas aquela seção se encha de água, preservando a flutuabilidade do restante da embarcação. &lt;strong&gt;O objetivo dessa estratégia não é impedir que o dano aconteça, mas impedir sua propagação&lt;/strong&gt; e, por sua vez, o naufrágio completo da embarcação. &lt;strong&gt;Esse mesmo raciocínio se aplica a sistemas críticos de larga escala&lt;/strong&gt; e então foi portado para a engenharia de software como um conceito a ser estudado e entendido.&lt;/p&gt;

&lt;h2 id=&quot;bulkheads-e-a-arquitetura-de-software&quot;&gt;Bulkheads e a Arquitetura de Software&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/bulkhead-tradicional.png&quot; alt=&quot;Bulkhead Tradicional&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O Bulkhead Pattern é um padrão de design de resiliência aplicado em microsserviços&lt;/strong&gt;, cujo objetivo é isolar falhas e impedir que um problema em um componente derrube todo o sistema. Na arquitetura de software, &lt;strong&gt;um bulkhead representa uma separação explícita e delimitada de recursos e de destinos de execução de transações&lt;/strong&gt;. A ideia é segregar pools de recursos específicos para evitar que a saturação ou falha de um componente afete outros domínios ou segmentações de clientes de todo o sistema, e &lt;strong&gt;representa uma separação explícita de recursos e destinos de execução&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Um erro conceitual recorrente é imaginar que bulkheads precisam existir em apenas uma camada do sistema&lt;/strong&gt;. Na prática, sistemas resilientes aplicam o mesmo princípio de isolamento de forma consistente ao longo da stack. É comum observar separação no nível de aplicação, mas não no banco de dados ou no isolamento de infraestrutura, mantendo, por exemplo, compartilhamento de filas ou tópicos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O Bulkhead pode ser aplicado em diferentes dimensões&lt;/strong&gt;, como pools de threads, filas, tópicos, pools de conexão, bancos de dados, VMs, containers, clusters ou shards. Se dois fluxos compartilham os mesmos recursos, as mesmas conexões ou os mesmos databases, &lt;strong&gt;eles não possuem bulkheads&lt;/strong&gt;, pois a falha de um fluxo inevitavelmente se propaga para os outros.&lt;/p&gt;

&lt;p&gt;Quando aplicado de forma correta, &lt;strong&gt;o sistema deixa de ser visto como um bloco único e altamente acoplado&lt;/strong&gt; e passa a se comportar como blocos e partições independentes, cada um com sua própria capacidade, limites, escalabilidade, funcionalidades e usuários bem definidos.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;implementações-e-contenção-de-falhas&quot;&gt;Implementações e Contenção de Falhas&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Bulkheads podem ser implementados em diferentes níveis da arquitetura&lt;/strong&gt;, mas todos compartilham o mesmo objetivo: impedir que a saturação de um recurso consuma a capacidade global do sistema. &lt;strong&gt;A implementação correta exige clareza sobre quais recursos são finitos e como eles devem ser particionados&lt;/strong&gt;. Para direcionar a estratégia de forma correta, precisamos pontuar de forma objetiva quais recursos são finitos no sistema, quais são críticos e como eles devem ser segmentados e, assim, definir formas de identificar, redirecionar, redistribuir e monitorar o tráfego e as operações nesses compartimentos distintos.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/Scale-Bulkhead-Falhas.png&quot; alt=&quot;Contenção&quot;&gt;&lt;/p&gt;

&lt;h2 id=&quot;recursos-lógicos&quot;&gt;Recursos Lógicos&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bulkheads lógicos atuam sobre recursos de execução e concorrência&lt;/strong&gt;, como threads, filas, conexões e limites de requisição. São os mais comuns e, ao mesmo tempo, os mais frequentemente mal implementados.&lt;/p&gt;

&lt;p&gt;Um exemplo é o uso de thread pools dedicados para diferentes tipos de operação. Sem bulkhead, uma operação lenta ou bloqueante pode consumir todas as threads disponíveis, &lt;strong&gt;gerando gargalos e filas internas&lt;/strong&gt;, acarretando saturação e problemas de performance. Com pools dedicados, &lt;strong&gt;a falha fica confinada ao fluxo que a originou&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Outro exemplo são filas e tópicos independentes por domínio, evitando que um pico de mensagens em um fluxo impeça o processamento de eventos críticos em outro. &lt;strong&gt;Com pools e “gatilhos” sistêmicos segregados, cada tipo de operação possui um limite claro de concorrência&lt;/strong&gt;. Quando esse limite é atingido, apenas aquele fluxo degrada, enquanto os demais continuam operando dentro de parâmetros aceitáveis. O mesmo raciocínio se aplica a filas e tópicos independentes por domínio ou clientes, evitando que picos de eventos não críticos atrasem ou bloqueiem fluxos essenciais, que são críticos para outro tipo de público ou domínio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bulkheads lógicos são frequentemente confundidos com simples aplicações de rate limiting ou com limites globais de concorrência&lt;/strong&gt;. No entanto, a diferença fundamental está no escopo do impacto. Um limite global protege o sistema como um todo, mas não protege os fluxos críticos entre si. Já &lt;strong&gt;o bulkhead lógico cria fronteiras internas&lt;/strong&gt;, onde cada fluxo opera dentro de sua própria capacidade alocada.&lt;/p&gt;

&lt;p&gt;No dia a dia de um time de engenharia, isso se traduz em decisões como pools de threads separados para leitura e escrita, filas distintas para eventos críticos e não críticos, ou até mesmo executores dedicados para integrações externas sabidamente instáveis. &lt;strong&gt;Um serviço que consome múltiplas APIs de terceiros não deveria permitir que a lentidão de uma integração consuma os recursos responsáveis por operações internas além desse processo&lt;/strong&gt;. Cada integração externa representa, por definição, uma superfície de risco distinta e, portanto, &lt;strong&gt;merece seu próprio compartimento lógico&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;recursos-físicos&quot;&gt;Recursos Físicos&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/bulkhead-types.png&quot; alt=&quot;Bulkhead Físico&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bulkheads físicos envolvem a separação concreta de infraestrutura&lt;/strong&gt;, como servidores, nodes, instâncias, zonas de disponibilidade ou até regiões. Aqui, &lt;strong&gt;o isolamento passa a ser definitivamente estrutural&lt;/strong&gt;. Por exemplo, alocar workloads críticos e não críticos nos mesmos nós de um cluster cria um &lt;em&gt;shared fate&lt;/em&gt; implícito. A saturação de CPU ou memória por um workload pode derrubar todos os outros. &lt;strong&gt;Separar esses workloads em pools de nós distintos cria um bulkhead físico que protege o sistema como um todo&lt;/strong&gt;. Esse tipo de isolamento é mais caro, mas fornece garantias mais fortes, especialmente em sistemas de alta criticidade.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/bulkhead-cluster.png&quot; alt=&quot;Bulkhead Cluster&quot;&gt;&lt;/p&gt;

&lt;p&gt;No dia a dia, isso aparece de forma clara em clusters Kubernetes, ambientes de virtualização ou até mesmo em servidores &lt;em&gt;bare metal&lt;/em&gt;. Um workload mal dimensionado, com vazamento de memória ou comportamento não linear sob carga pode pressionar o kernel, o scheduler ou o hypervisor, afetando todos os serviços alocados por tabela. &lt;strong&gt;Nesse ponto, nenhum thread pool ou fila dedicada é suficiente para conter a falha; é necessária uma segregação física dos recursos&lt;/strong&gt;. O critério utilizado para isso pode e deve variar, como, por exemplo, tipos de clientes, segmentos, prioridade, criticidade, hashing consistente, identificadores etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bulkheads físicos surgem como resposta a esse tipo de risco&lt;/strong&gt;. Separar workloads críticos em pools de nós dedicados, usar clusters distintos para domínios com SLOs incompatíveis ou até isolar componentes por região são decisões que aumentam o custo, mas &lt;strong&gt;reduzem drasticamente o blast radius&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;distribuição-de-bulkheads-e-blast-radius&quot;&gt;Distribuição de Bulkheads e Blast Radius&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;A forma como shards são definidos, roteados e balanceados determina, de maneira explícita, o tamanho do blast radius&lt;/strong&gt;, o comportamento sob sobrecarga e a previsibilidade da degradação. Em arquiteturas avançadas, &lt;strong&gt;sharding deixa de ser um detalhe de armazenamento ou roteamento e passa a ser um mecanismo primário de isolamento operacional&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Cada shard representa, na prática, &lt;strong&gt;um bulkhead completo ou parcial&lt;/strong&gt;. Ele possui capacidade própria, limites próprios e uma curva de degradação própria. &lt;strong&gt;A distribuição correta desses shards permite transformar falhas sistêmicas em falhas estatisticamente localizadas&lt;/strong&gt;. Um pico extremo deixa de ser um evento binário de “o sistema caiu” e passa a ser um evento probabilístico: “X% do sistema foi impactado”.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Bulkheads&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Blast Radius&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Disponibilidade&lt;/th&gt;
      &lt;th&gt;Impacto&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;100%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0%&lt;/td&gt;
      &lt;td&gt;Total&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;50%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;50%&lt;/td&gt;
      &lt;td&gt;Muito alto&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;3&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;33%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;66%&lt;/td&gt;
      &lt;td&gt;Alto&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;20%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;80%&lt;/td&gt;
      &lt;td&gt;Moderado&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;10&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;10%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;90%&lt;/td&gt;
      &lt;td&gt;Moderado&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;20&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;95%&lt;/td&gt;
      &lt;td&gt;Baixo&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;50&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;2%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;98%&lt;/td&gt;
      &lt;td&gt;Muito baixo&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;100&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1%&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;99%&lt;/td&gt;
      &lt;td&gt;Mínimo&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quanto maior o número de shards, menor o blast radius, mas maior a complexidade operacional&lt;/strong&gt;. O ponto central não é apenas quantos shards existem, mas &lt;strong&gt;como o tráfego é distribuído entre eles&lt;/strong&gt;. Distribuições mal balanceadas, chaves de particionamento enviesadas ou algoritmos de roteamento instáveis podem concentrar carga excessiva em poucos shards, &lt;strong&gt;anulando completamente o efeito do bulkhead&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;bulkheads-e-shardings&quot;&gt;Bulkheads e Shardings&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Sharding é uma das formas mais poderosas e perigosas de implementar bulkheads&lt;/strong&gt;. Quando bem aplicado, oferece isolamento estrutural; quando mal projetado, cria acoplamentos invisíveis que só se manifestam sob estresse e acabam não impedindo a propagação de falhas de um recurso isolado. Aqui, é necessário segregar todos os recursos físicos que podem compor o bulkhead, como balanceadores de carga, aplicações, bancos de dados, tópicos, filas e afins, e criar réplicas literais dedicadas apenas para aquele bulkhead, de forma que os fluxos iniciados em uma segmentação do bulkhead permaneçam nele até o fim da execução e, assim, não ofereçam risco de performance e disponibilidade por conta da saturação de uso daquela partição específica do sistema. &lt;strong&gt;Outros bulkheads devem estar aptos a executar as mesmas funções&lt;/strong&gt;, porém com capacidade isolada para outros tipos de públicos e operações.&lt;/p&gt;

&lt;p&gt;Eles são especialmente relevantes para &lt;strong&gt;lidar com comportamentos não lineares de sistemas sob carga crescente&lt;/strong&gt;. Em regimes próximos à saturação, pequenas variações de tráfego podem provocar aumentos desproporcionais de latência, consumo de memória, &lt;em&gt;lock contention&lt;/em&gt; ou pressão sobre o scheduler. &lt;strong&gt;Sem bulkheads, esse comportamento não linear tende a se espalhar por todo o sistema&lt;/strong&gt;, criando um efeito dominó em que fluxos originalmente saudáveis passam a degradar por compartilharem os mesmos recursos finitos. &lt;strong&gt;Tratados como complemento às estratégias de sharding&lt;/strong&gt;, tendem a elevar os níveis de performance e disponibilidade.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;bulkheads-de-sharding-funcional&quot;&gt;Bulkheads de Sharding Funcional&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;No sharding funcional, o sistema ou domínio de negócio é dividido por funcionalidades e padrões de uso&lt;/strong&gt;. Cada shard atende a um conjunto específico de funcionalidades, com recursos próprios e limites bem definidos.&lt;br&gt;
Por exemplo, separar processamento de pagamentos, consultas e relatórios em shards distintos evita que um pico analítico degrade operações críticas de transação. &lt;strong&gt;Aqui, o bulkhead é alinhado ao valor de negócio&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/bulkhead-funcional.png&quot; alt=&quot;Sharding Funcional&quot;&gt;&lt;/p&gt;

&lt;p&gt;É razoavelmente comum segregar bulkheads específicos para operações transacionais e &lt;em&gt;just-in-time&lt;/em&gt; e uma separação dedicada para processamento de lotes e &lt;em&gt;batches&lt;/em&gt;. Inserir uma quantidade gigante de processos em repouso para concorrer com fluxos que possuem SLOs e contratos de tempo de resposta e disponibilidade transacionais pode acabar gerando saturação e ofendendo os indicadores. &lt;strong&gt;Desse modo, é possível ter infraestrutura dedicada, dentro do possível, para direcionar solicitações em batch ou sincronizações agendadas de outros domínios e parceiros&lt;/strong&gt;, e outra segregada para as operações convencionais do sistema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Outra estratégia é ter infraestrutura dedicada para diferentes prioridades de processamento do mesmo tipo de transação&lt;/strong&gt;, dedicando capacidade exclusiva para transações prioritárias, normais e de baixa prioridade, de forma que, em caso de um &lt;em&gt;spike&lt;/em&gt; ou &lt;em&gt;burst&lt;/em&gt; de solicitações normais ou de baixa prioridade que cheguem ao sistema, &lt;strong&gt;não comprometam as solicitações enviadas para o bulkhead de alta prioridade&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;bulkheads-de-sharding-operacional&quot;&gt;Bulkheads de Sharding Operacional&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;No sharding operacional, a divisão ocorre por volume ou características de carga&lt;/strong&gt;, não por função. Exemplos comuns incluem sharding por identificadores de cliente, região ou faixa de tráfego.&lt;br&gt;
&lt;strong&gt;Esse modelo é eficaz para limitar o blast radius de picos localizados&lt;/strong&gt;, mas exige cuidado com operações globais, que podem atravessar múltiplos shards e reintroduzir acoplamento. É comum que shards sejam bem isolados no início, mas gradualmente passem a compartilhar dependências globais, como serviços de configuração, catálogos ou bancos de dados auxiliares. &lt;strong&gt;Esses pontos se tornam canais ocultos de acoplamento&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/bulkhead-operacional.png&quot; alt=&quot;Sharding Operacional&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;arquiteturas-de-bulkheads&quot;&gt;Arquiteturas de Bulkheads&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Nesta seção vamos ilustrar algumas das possibilidades de segregação estrutural de bulkheads dentro da arquitetura de software&lt;/strong&gt;, onde serão apresentadas estratégias para dedicar e isolar capacidade para diferentes tipos de contextos comuns presentes no dia a dia. Muitos deles já foram vistos e citados, &lt;strong&gt;mas aqui serão reabordados com uma recapitulação estruturada das estratégias&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;bulkheads-por-priorização&quot;&gt;Bulkheads por Priorização&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/bulkhead-prioridade-processamento.png&quot; alt=&quot;Priorização&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Criar bulkheads de capacidade por priorização parte do princípio de que nem todas as transações possuem o mesmo valor sistêmico em termos de importância&lt;/strong&gt;. A ideia é garantir capacidade reservada para fluxos com diferentes prioridades, para evitar, por exemplo, que filas FIFO, pools compartilhados ou aplicações generalistas colapsem por &lt;em&gt;bursts&lt;/em&gt; ou picos de acesso, fazendo com que requisições críticas concorram e se atrasem por conta de requisições menos relevantes.&lt;/p&gt;

&lt;p&gt;Na prática, &lt;strong&gt;esse padrão aparece em sistemas financeiros, plataformas de pedidos ou sistemas de autenticação&lt;/strong&gt;, onde fluxos de escrita transacional, confirmação de pagamento ou autenticação de sessão não podem ser impactados por cargas secundárias, como reprocessamentos, sincronizações ou integrações assíncronas que exijam muito da capacidade computacional.&lt;/p&gt;

&lt;h2 id=&quot;bulkheads-por-criticidade&quot;&gt;Bulkheads por Criticidade&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/bulkhead-criticidade.png&quot; alt=&quot;Criticidade&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bulkheads por criticidade vão além da prioridade momentânea e refletem o impacto sistêmico da falha de um fluxo&lt;/strong&gt;. Enquanto priorização responde à pergunta “o que deve ser atendido primeiro?”, &lt;strong&gt;criticidade responde “o que não pode falhar”&lt;/strong&gt;. Podemos replicar e alocar capacidade computacional para clientes que precisam estar inerentes a infraestruturas auditadas por regulamentações específicas, como, por exemplo, PCI Compliance, certificações ISO ou HIPAA, fazendo com que seja possível atender critérios específicos de isolamento e auditabilidade para cada tipo específico de necessidade.&lt;/p&gt;

&lt;h2 id=&quot;bulkheads-por-tipo-de-uso&quot;&gt;Bulkheads por Tipo de Uso&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/bulkhead-tipo-de-uso.png&quot; alt=&quot;Tipo de Uso&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bulkheads por tipo de uso surgem quando o mesmo sistema atende padrões de carga radicalmente diferentes&lt;/strong&gt;, e permitem separar usos por fluxos interativos, síncronos e sensíveis à latência de fluxos &lt;em&gt;batch&lt;/em&gt;, assíncronos ou orientados a maior throughput. A separação existe porque esses perfis possuem &lt;strong&gt;curvas de comportamento opostas&lt;/strong&gt;, mas precisam da mesma funcionalidade. &lt;strong&gt;Operações interativas exigem baixa latência, previsibilidade e rejeição rápida sob sobrecarga&lt;/strong&gt;. Operações &lt;em&gt;batch&lt;/em&gt; toleram latência elevada, mas consomem recursos de forma agressiva e prolongada. Quando ambos compartilham os mesmos recursos, &lt;strong&gt;o comportamento &lt;em&gt;batch&lt;/em&gt; tende a dominar&lt;/strong&gt;, pressionando CPU, memória, IO e filas internas, degradando silenciosamente os fluxos interativos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O bulkhead por tipo de uso não tenta “otimizar” o &lt;em&gt;batch&lt;/em&gt; ou operações de leitura intensiva&lt;/strong&gt;, mas impedir que eles concorram estruturalmente com operações sensíveis. Isso costuma aparecer como filas, workers, clusters ou até pipelines de deploy distintos para cada tipo de uso. &lt;strong&gt;O &lt;em&gt;batch&lt;/em&gt; pode atrasar, acumular ou até ser pausado&lt;/strong&gt;, sem que isso altere o SLO das operações online.&lt;/p&gt;

&lt;h2 id=&quot;bulkheads-por-segmento&quot;&gt;Bulkheads por Segmento&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/bulkhead-segmento.png&quot; alt=&quot;Tipo de Uso&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Os bulkheads por segmento tratam explicitamente do problema de heterogeneidade de comportamento entre grupos de usuários, clientes ou regiões&lt;/strong&gt;. Clientes enterprise, parceiros estratégicos ou segmentos regulados não podem compartilhar o mesmo destino operacional que usuários experimentais, testes A/B ou integrações instáveis.&lt;/p&gt;

&lt;p&gt;Sistemas que atendem diversos públicos &lt;strong&gt;podem segmentar capacidade operacional&lt;/strong&gt; para lidar com divergências de criticidade e expectativas, como, por exemplo, públicos de pessoa física, pessoas jurídicas, pessoas publicamente expostas e clientes prioritários. Dessa forma, &lt;strong&gt;é possível criar estratégias para que, em caso de contenção de falhas, nem todos os segmentos sejam afetados simultaneamente&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isso também cria um espaço saudável para negociação de SLOs, precificação diferenciada e evolução independente de capacidade&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;bulkheads-por-hashing-consistente&quot;&gt;Bulkheads por Hashing Consistente&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/bulkhead-hash.png&quot; alt=&quot;Hashing&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bulkheads por hashing consistente são a forma mais estatística de aplicar isolamento operacional&lt;/strong&gt; quando o objetivo é distribuir carga e isolar parcelas de falhas de maneira mais determinística. A ideia é, por meio de um algoritmo de roteamento, proxy ou roteador, &lt;strong&gt;utilizar uma chave estável&lt;/strong&gt;, como tenantId, customerId, accountId ou deviceId, e utilizá-la para enviar as solicitações sempre para o mesmo conjunto de recursos.&lt;/p&gt;

&lt;p&gt;Em um balanceamento clássico (&lt;em&gt;round-robin&lt;/em&gt;, &lt;em&gt;least-connections&lt;/em&gt;), &lt;strong&gt;um pico localizado de um único cliente “vaza” para toda a frota&lt;/strong&gt;, porque o balanceador distribui indiscriminadamente. &lt;strong&gt;Com hashing consistente, o pico do cliente fica concentrado no(s) shard(s) aos quais ele foi mapeado&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;bulkheads-por-tenants&quot;&gt;Bulkheads por Tenants&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Isolar tenants vai muito além de separar dados dos mesmos em tabelas ou instâncias de dados diferentes&lt;/strong&gt;. Trata-se de garantir que o comportamento de consumo, erros ou picos de um tenant não alterem o perfil operacional dos demais. Em plataformas SaaS, isso frequentemente significa &lt;strong&gt;criar limites explícitos de capacidade por tenant&lt;/strong&gt;, combinando bulkheads lógicos e físicos conforme o nível de criticidade e monetização.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/bulkhead-tenant.png&quot; alt=&quot;Bulkhead Tenant&quot;&gt;&lt;/p&gt;

&lt;p&gt;Podemos ter &lt;strong&gt;réplicas inteiras de toda a infraestrutura dedicadas para cada um dos tenants&lt;/strong&gt;, que são roteados por meio de regras de balanceamento, ingress ou DNS, isolando totalmente a operação dos mesmos para evitar &lt;em&gt;noisy neighbor&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;No mundo real, é comum observar plataformas que isolam dados, mas compartilham integralmente threads, filas e infraestrutura. O resultado é que &lt;strong&gt;um único cliente com comportamento anômalo pode comprometer toda a experiência da plataforma&lt;/strong&gt;. &lt;strong&gt;Bulkheads por tenant transformam esse risco em um problema localizado&lt;/strong&gt;, em que a degradação é previsível, mensurável e, principalmente, negociável do ponto de vista de negócio.&lt;/p&gt;

&lt;h3 id=&quot;noisy-neighbor-e-bulkheads-tenants&quot;&gt;Noisy Neighbor e Bulkheads Tenants&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;O problema do “noisy neighbor”, ou vizinho barulhento, surge quando múltiplos tenants compartilham os mesmos recursos físicos e lógicos&lt;/strong&gt;, e o comportamento de um impacta negativamente os demais. &lt;strong&gt;Sem bulkheads, basta um tenant com desvio de comportamento e saturação acima do previsto para degradar toda a plataforma&lt;/strong&gt;.&lt;br&gt;
&lt;strong&gt;Esse problema é especialmente crítico em plataformas SaaS e ambientes multi-tenant de alta escala&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;referências&quot;&gt;Referências&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://medium.com/nerd-for-tech/bulkhead-pattern-distributed-design-pattern-c673d5e81523&quot;&gt;Bulkhead Pattern — Distributed Design Pattern&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.systemdesignacademy.com/blog/bulkhead-pattern&quot;&gt;Bulkhead Pattern in Microservices&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead&quot;&gt;Bulkhead pattern&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/blogs/containers/building-a-fault-tolerant-architecture-with-a-bulkhead-pattern-on-aws-app-mesh/&quot;&gt;Building a fault tolerant architecture with a Bulkhead Pattern on AWS App Mesh&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/system-design/bulkhead-pattern/&quot;&gt;Bulkhead Pattern&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://failsafe-go.dev/bulkhead/&quot;&gt;Failsafe - Bulkhead Go&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 05 Feb 2026 00:00:00 +0000</pubDate>
        <link>https://fidelissauro.dev/bulkheads/</link>
        <guid isPermaLink="true">https://fidelissauro.dev/bulkheads/</guid>
        
        
        <category>system-design</category>
        
        <category>engineering</category>
        
      </item>
    
      <item>
        <title>System Design - Capacity Planning e a Teoria das Filas</title>
        <description>&lt;p&gt;&lt;strong&gt;Capacity planning não é sobre prever o futuro com precisão absoluta.&lt;/strong&gt; É sobre entender os limites estruturais do sistema antes que eles se tornem incidentes. A maioria dos problemas de capacidade não surge de crescimento repentino, mas da incapacidade de interpretar o comportamento do sistema sob carga real. Métricas isoladas, como CPU, memória ou TPS médio, raramente contam a história completa. &lt;strong&gt;O que realmente importa é como esses sinais se relacionam, como a concorrência interna se acumula e onde os gargalos se formam quando a carga deixa de ser uniforme.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Passei os últimos 3 meses do ano de 2025 procurando modelos matemáticos para me guiar nos assuntos de capacity planning e performance para minha caixa de ferramentas. Aqui, guardo um compilado dos conceitos e fórmulas mais relevantes que encontrei. Rascunhei este capítulo logo em seguida a uma das etapas mais intensas do meu mestrado, e seu resultado final foi uma linguagem muito mais densa e teórica do que os anteriores, mas gostei muito do resultado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Este texto não é um guia para dimensionar servidores.&lt;/strong&gt; É uma abordagem sistemática para modelar carga, interpretar saturação e planejar crescimento de forma estruturada. A teoria das filas, a Lei de Little e a curva do joelho não são apenas abstrações acadêmicas, são ferramentas práticas para responder perguntas como “quanto meu sistema aguenta de forma sustentável?” e “onde ele quebra antes de eu perceber?”. &lt;strong&gt;O objetivo é transformar capacity planning de uma reação a incidentes em uma prática de engenharia preventiva e bem fundamentada.&lt;/strong&gt;&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot; id=&quot;MathJax-script&quot; async=&quot;&quot; src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js&quot;&gt;
&lt;/script&gt;

&lt;script type=&quot;text/x-mathjax-config&quot;&gt;


    MathJax.Hub.Config({
        jax: [&quot;input/TeX&quot;,&quot;output/HTML-CSS&quot;],
        displayAlign: &quot;left&quot;
    });
    
    
&lt;/script&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;teoria-das-filas&quot;&gt;Teoria das Filas&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/teoria-das-filas-conceitual.png&quot; alt=&quot;Teoria das Filas&quot;&gt;&lt;/p&gt;

&lt;p&gt;A teoria das filas é um dos fundamentos mais importantes e mal compreendidos em capacity planning. Em termos simples, a teoria estuda como &lt;strong&gt;sistemas se comportam quando múltiplas demandas competem por recursos finitos&lt;/strong&gt;. Em engenharia de software, podemos utilizar como base comportamentos comuns, como requisições síncronas aguardando processamento para responder a um cliente, mensagens acumuladas em filas, múltiplos itens sendo processados em memória, conexões disputando pools limitados em bancos de dados ou operações de I/O esperando acesso a um recurso compartilhado.&lt;/p&gt;

&lt;p&gt;De forma conceitual, toda fila pode ser entendida a partir de três dimensões: &lt;strong&gt;como as demandas chegam ao sistema, como elas são processadas e em que ordem são atendidas&lt;/strong&gt;. O objetivo é transformar arquiteturas complexas em modelos matematicamente analisáveis, principalmente em arquiteturas distribuídas, onde taxas de uso estáveis e tempos de resposta previsíveis raramente se sustentam de forma consistente.&lt;/p&gt;

&lt;p&gt;As “filas” não existem apenas onde há estruturas literais de enfileiramento assíncrono, como brokers de mensagens e eventos. Embora a teoria das filas seja vista apenas como uma abstração acadêmica na maior parte dos casos, &lt;strong&gt;ela nos fornece formas de compreender gargalos, throughput real, tempo de resposta e latências em cascata decorrentes de cenários como saturação de pools de threads, conexões de banco de dados, locks em recursos compartilhados e mecanismos de retry&lt;/strong&gt;, não apenas de forma isolada, mas sobretudo em arquiteturas distribuídas, onde cada hop, cada requisição, cada buffer e &lt;strong&gt;cada microserviço se comporta como uma fila independente, com sua própria taxa de chegada, taxa de processamento, saturação e congestionamento&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/teoria-das-filas-simples.png&quot; alt=&quot;Teoria das Filas&quot;&gt;&lt;/p&gt;

&lt;p&gt;Da forma mais simples, uma fila é um mecanismo onde &lt;strong&gt;solicitações chegam &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(λ)&lt;/code&gt; e são processadas &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(μ)&lt;/code&gt;&lt;/strong&gt;, e o sistema &lt;strong&gt;oscila continuamente entre estados de ociosidade, equilíbrio e saturação&lt;/strong&gt; dentro desses dois parâmetros. &lt;strong&gt;Quando a taxa de chegada &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(λ)&lt;/code&gt; se aproxima ou ultrapassa o limite da taxa de processamento &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(μ)&lt;/code&gt;, isso gera um gargalo físico&lt;/strong&gt;, onde tempos de resposta aumentam e o throughput degrada por haver uma taxa de envio maior do que a taxa de vazão. É por esse tipo de detalhe técnico que um microsserviço saudável em p95 pode degradar de forma significativa em p99 sob picos inesperados, mesmo com CPU e outros recursos aparentemente estáveis. No geral, o problema não é a falta de capacidade física, mas sim a variabilidade temporal, bursts e o custo de espera entre chamadas e processos.&lt;/p&gt;

&lt;p&gt;Isso explica por que o autoscaling normalmente não resolve todos os problemas de capacidade, uma vez que ele reage apenas a aumentos expressivos de uso ou saturação de recursos para adicionar ou remover réplicas de um serviço. &lt;strong&gt;O autoscaling, de forma superficial, aumenta a taxa de processamento &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(μ)&lt;/code&gt; momentaneamente&lt;/strong&gt;, permitindo que a taxa de vazão cresça, mas ainda funciona com base em gatilhos temporais, deixando o sistema sensível a bursts e picos de uso. Em outras palavras, &lt;strong&gt;um sistema não sofre porque recebe “muitas requisições”, mas porque recebe requisições de forma imprevisível ou não uniforme&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A teoria das filas propõe o &lt;strong&gt;uso de métricas de variabilidade, como o coeficiente de variação ou o desvio padrão, em vez de medidas como percentis, mínimos, máximos e médias na taxa de processamento&lt;/strong&gt;. Analisamos, então, a variação da taxa de chegada &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(λ)&lt;/code&gt; e da taxa de processamento &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(μ)&lt;/code&gt;. Essa visão explica por que sistemas com a mesma capacidade de recursos podem apresentar comportamentos completamente distintos sob carga real. Dois serviços com a mesma taxa média de atendimento podem exibir curvas de latência radicalmente diferentes se um deles processar requisições com desvio padrão elevado.&lt;/p&gt;

&lt;p&gt;Estratégias já discutidas anteriormente, como sharding, bulkheads, caching, escalabilidade vertical e horizontal, desacoplamento por meio de filas e eventos, aumento do número de consumidores, bem como estratégias de concorrência e paralelismo, nos ajudam a lidar com a estabilidade dos sistemas quando a taxa de chegada supera a taxa de processamento.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;a-lei-de-little-na-teoria-das-filas&quot;&gt;A Lei de Little na Teoria das Filas&lt;/h2&gt;

&lt;p&gt;A Lei de Little, ou Little’s Law, é um &lt;strong&gt;princípio matemático simples integrado à Teoria das Filas&lt;/strong&gt;, apresentado por John D. C. Little na década de 1960, que nos fornece insights valiosos para entender o comportamento de qualquer sistema sob carga. A lei não foi inicialmente formulada para conceitos computacionais complexos; ela pode ser utilizada para analisar a pressão de qualquer tipo de sistema sob a ótica da média de três variáveis, sendo elas o &lt;strong&gt;número médio de itens em processamento no sistema (L)&lt;/strong&gt;, a &lt;strong&gt;taxa média de chegada (λ)&lt;/strong&gt; e o &lt;strong&gt;tempo médio de processamento e permanência desses itens (W) no sistema&lt;/strong&gt;. Essa relação é expressa pela equação:&lt;/p&gt;

&lt;p&gt;\begin{equation}
L = \lambda \times W
\end{equation}&lt;/p&gt;

&lt;p&gt;Esse cálculo, por mais que seja simples, &lt;strong&gt;é válido para interpretar qualquer sistema estável&lt;/strong&gt;, pois independe de estatísticas complexas e de valores exatos da taxa de processamento e permanência &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(W)&lt;/code&gt; e da taxa de chegada de itens ao sistema &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(λ)&lt;/code&gt;, &lt;strong&gt;desde que suas médias sejam bem definidas&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/little-law.png&quot; alt=&quot;Lei de Little&quot;&gt;&lt;/p&gt;

&lt;p&gt;Em sistemas distribuídos, a Lei de Little &lt;strong&gt;nos ajuda a interpretar a capacidade de forma granular, a nível de cada componente, dependência ou microserviço&lt;/strong&gt;, ou de forma mais ampla, &lt;strong&gt;analisando um fluxo completo em cenários onde estimar as capacidades exatas de todos os componentes pode ser muito complexo ou inviável&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Em termos práticos, ela se resume a uma interpretação adicional de capacidade sobre throughput e latência. Para uma taxa de chegada fixa &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(λ)&lt;/code&gt;, &lt;strong&gt;qualquer aumento no tempo médio de resposta &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(W)&lt;/code&gt; implica, de forma imediata, um aumento proporcional no número de processos simultâneos &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(L)&lt;/code&gt; no sistema&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Considere um sistema assíncrono que recebe uma taxa média de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.500&lt;/code&gt; mensagens por segundo, com tempo médio de processamento por mensagem de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;50ms&lt;/code&gt;. Aplicando a Little’s Law, podemos encontrar o número de processos concorrentes dentro do mesmo segundo:&lt;/p&gt;

&lt;p&gt;\begin{equation}
L = 1.500 \times 0.05
\end{equation}
\begin{equation}
L = 75
\end{equation}&lt;/p&gt;

&lt;p&gt;Neste cenário, o sistema mantém, em média, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;75&lt;/code&gt; mensagens simultaneamente em processamento ou espera. &lt;strong&gt;Esse valor representa a concorrência média interna do sistema e pode ser utilizado como base para dimensionamento de consumidores, threads de processamento, partições de filas ou limites de paralelismo&lt;/strong&gt;, servindo como &lt;strong&gt;fator base para antecipar degradações ou otimizações de forma proativa, sem depender de saturação&lt;/strong&gt;. Lembrando que, com base interpretativa do modelo, &lt;strong&gt;quanto menor o valor de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L&lt;/code&gt;, melhor&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pequenos aumentos no tempo médio de processamento impactam diretamente o número de mensagens acumuladas&lt;/strong&gt;, aumentando o &lt;strong&gt;risco de atraso e crescimento não controlado da fila&lt;/strong&gt;, por exemplo, em um aumento do tempo de processamento para &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;85ms&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;\begin{equation}
L = 1.500 \times 0.085
\end{equation}&lt;/p&gt;

&lt;p&gt;\begin{equation}
L = 127
\end{equation}&lt;/p&gt;

&lt;p&gt;Ao &lt;strong&gt;elevar o tempo médio de processamento&lt;/strong&gt;, mesmo para um &lt;strong&gt;aumento aparentemente pequeno&lt;/strong&gt; e plausível em cenários reais, causado por variação de payload, latência de dependências externas, I/O ou demais contenções externas, o número médio de mensagens em voo salta para &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127&lt;/code&gt; de concorrência interna, &lt;strong&gt;um aumento absoluto de 52 mensagen&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;lei-de-little-e-o-ponto-saudável&quot;&gt;Lei de Little e o “Ponto Saudável”&lt;/h3&gt;

&lt;p&gt;A Lei de Little nos fornece um critério de avaliação para &lt;strong&gt;encontrar um “ponto saudável” de operação de um sistema&lt;/strong&gt;, no qual entendemos que, com o crescimento da carga &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(λ)&lt;/code&gt;, &lt;strong&gt;não teremos aumento descontrolado da concorrência interna&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(L)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/law-guardrail.png&quot; alt=&quot;L-Alvo&quot;&gt;&lt;/p&gt;

&lt;p&gt;Para tornar isso palpável, podemos adotar um &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L(Alvo)&lt;/code&gt; para o sistema, como um Service Level de engenharia, que representa um &lt;strong&gt;número máximo desejável de itens em concorrência interna&lt;/strong&gt;, sendo esse compatível com os &lt;strong&gt;limites físicos e operacionais da solução&lt;/strong&gt;, nos levando à busca por otimizações constantes para reduzir o tempo de processamento &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(W)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Considere uma API REST que possui &lt;strong&gt;um &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L(Alvo)&lt;/code&gt; de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;150&lt;/code&gt;&lt;/strong&gt;. O sistema recebe &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;500&lt;/code&gt; requisições por segundo, com um tempo médio de resposta de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;300ms&lt;/code&gt;. Pela Lei de Little:&lt;/p&gt;

&lt;p&gt;\begin{equation}
L = 500 \times 0.3
\end{equation}&lt;/p&gt;

&lt;p&gt;\begin{equation}
L = 150
\end{equation}&lt;/p&gt;

&lt;p&gt;Esse cenário caracteriza o contrato do “Ponto Saudável”, &lt;strong&gt;onde o sistema opera dentro do limite planejado de concorrência interna&lt;/strong&gt; e mantém uma certa previsibilidade e margem para absorver variações. À medida que a carga cresce no sistema para &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1000&lt;/code&gt; requisições por segundo, o &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L&lt;/code&gt; vai para &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;300&lt;/code&gt;, ultrapassando o &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L(Alvo)&lt;/code&gt; e podendo levar o sistema para uma região de saturação e risco.&lt;/p&gt;

&lt;p&gt;Uma progressão saudável nos leva à pesquisa interna para lidar com uma redução proporcional do tempo de processamento &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;W&lt;/code&gt;. Aqui aplicamos diversas técnicas de otimização para diminuir o tempo de processamento dos requests. Podemos descobrir o tempo-alvo para otimização &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(W)&lt;/code&gt; dividindo nosso &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L(Alvo)&lt;/code&gt; pela taxa de requisições recebidas &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(λ)&lt;/code&gt; atual e multiplicando, de forma categórica, para chegar à mesma unidade de tempo que estamos utilizando — no caso do exemplo, milissegundos:&lt;/p&gt;

&lt;p&gt;\begin{equation}
W = \frac{\text{L(Alvo)}}{\lambda} \times 1000
\end{equation}&lt;/p&gt;

&lt;p&gt;Convertendo para o exemplo da nossa API:&lt;/p&gt;

&lt;p&gt;\begin{equation}
W = \frac{150}{1000} \times 1000
\end{equation}&lt;/p&gt;

&lt;p&gt;\begin{equation}
W = 150ms
\end{equation}&lt;/p&gt;

&lt;p&gt;Nesse cenário, podemos entender que, para que nosso sistema volte a operar com o &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L(Alvo)&lt;/code&gt; de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;150&lt;/code&gt;, precisamos diminuir nosso tempo de processamento &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(W)&lt;/code&gt; de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;300ms&lt;/code&gt; para &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;150ms&lt;/code&gt;. &lt;strong&gt;Nesse novo formato otimizado, o sistema processa 50% mais requisições mantendo a mesma concorrência média interna&lt;/strong&gt;. &lt;strong&gt;O objetivo é que o crescimento seja absorvido estruturalmente, sem acúmulo adicional de filas ou pressão excessiva sobre recursos.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;knee-curve-curva-do-joelho&quot;&gt;Knee Curve (Curva do Joelho)&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/knee-curve.png&quot; alt=&quot;Knee Curve&quot;&gt;&lt;/p&gt;

&lt;p&gt;A Knee Curve, ou Curva do Joelho, é um conceito que demonstra a relação entre a utilização de um sistema e o seu ponto de degradação de capacidade. Em um &lt;a href=&quot;/load-testing/&quot;&gt;teste de carga&lt;/a&gt;, &lt;strong&gt;representa o ponto onde o tempo de resposta muda drasticamente em relação à tendência anterior&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Em termos normais, &lt;strong&gt;a latência cresce de forma linear conforme a quantidade de requisições que um sistema está lidando aumenta&lt;/strong&gt;. A Curva do Joelho &lt;strong&gt;revela o ponto a partir do qual o sistema deixa de se comportar de forma previsível e passa a apresentar degradação acelerada&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/knee-requests.png&quot; alt=&quot;Knee Curve&quot;&gt;&lt;/p&gt;

&lt;p&gt;Enquanto a utilização está antes da formação do “joelho”, o sistema tem capacidade de operar de forma saudável e segura e absorver pequenas variações de carga. Operar próximo ou além da curva aumenta significativamente o enfileiramento interno de recursos, o número de retries e a saturação dos componentes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/knee-cpu.png&quot; alt=&quot;Knee Curve&quot;&gt;&lt;/p&gt;

&lt;p&gt;Podemos aplicar o modelo a outras métricas além de requests propriamente ditos. É possível utilizar recursos físicos como CPU e memória para &lt;strong&gt;entender a partir de que ponto de uso o sistema começa a degradar em throughput e latência&lt;/strong&gt;, e, a partir disso, estimar suas capacidades e definir automações preventivas de &lt;a href=&quot;/performance-capacidade-escalabilidade/&quot;&gt;auto scaling&lt;/a&gt; de forma mais assertiva.&lt;/p&gt;

&lt;p&gt;Em paralelo à Teoria das Filas, à medida que a utilização cresce e se aproxima da capacidade máxima ou ultrapassa o “Ponto Saudável” definido pela Lei de Little, &lt;strong&gt;as filas internas começam a se formar e o tempo de espera passa a ser dominante em relação a todo o tempo de processamento definido&lt;/strong&gt;. A partir desse ponto, a latência cresce de forma não linear, frequentemente exponencial, mesmo quando o aumento de utilização adicional é pequeno ou aparentemente irrelevante.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/knee-l-alvo.png&quot; alt=&quot;L-Alvo&quot;&gt;&lt;/p&gt;

&lt;p&gt;Em testes de performance, &lt;strong&gt;encontrar a curva do joelho do sistema permite identificar dois pontos importantes: o “Ponto Saudável” e o “Ponto Máximo de Utilização”&lt;/strong&gt;. O Ponto Saudável, normalmente, é uma &lt;strong&gt;zona anterior à Curva do Joelho onde existe o maior equilíbrio operacional entre eficiência e previsibilidade&lt;/strong&gt;. Dentro desse intervalo, entendemos que &lt;strong&gt;o throughput cresce de forma saudável e os tempos de resposta permanecem conhecidos e controlados&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Já o &lt;strong&gt;Ponto Máximo de Utilização corresponde ao limite teórico em que o sistema ainda processa requisições&lt;/strong&gt;, porém à custa de latências elevadas, alta imprevisibilidade e &lt;strong&gt;risco significativo de indisponibilidade e falhas na experiência do usuário&lt;/strong&gt;. O ideal é que ambas as zonas se estabeleçam antes da curva do joelho definitiva: uma para operação normal e outra para definição explícita do limite máximo de risco aceitável.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;margens-seguras-de-saturação&quot;&gt;Margens Seguras de Saturação&lt;/h3&gt;

&lt;p&gt;Quando olhamos para recursos físicos sob a ótica de capacity planning, como, por exemplo, a utilização de CPU, não devemos interpretá-los com o objetivo de maximização como prioridade, mas sim como recursos finitos com margens de proximidade instáveis.&lt;/p&gt;

&lt;p&gt;Quando comparamos, por exemplo, CPU e memória com outros recursos como largura de banda, armazenamento e IOPs, suas saturações não se manifestam de maneira linear e não representam recursos definitivamente livres para serem alocados como um simples “espaço disponível”. Esse fenômeno pode ser interpretado por meio da Teoria das Filas. &lt;strong&gt;Pequenos aumentos de utilização próximos de um “Ponto Saudável” de uso de CPU provocam crescimento de filas de forma desproporcional&lt;/strong&gt;, sem que esses limites estejam necessariamente próximos de 100% de utilização.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/knee-cpu-usage.png&quot; alt=&quot;Saturação de CPU&quot;&gt;&lt;/p&gt;

&lt;p&gt;Os &lt;strong&gt;“Pontos Saudáveis” de CPU e memória&lt;/strong&gt; são zonas de utilização onde o sistema consegue absorver variações de carga, como spikes, bursts e jitters, sem exaurir a taxa de processamento &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(μ)&lt;/code&gt; ou aumentar o tempo de processamento &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(W)&lt;/code&gt;, evitando a geração de filas e gargalos. &lt;strong&gt;O ponto central é que não é necessário atingir 100% de CPU para que o sistema crie e inflacione filas internas.&lt;/strong&gt; Próximo de &lt;strong&gt;80–85% de utilização&lt;/strong&gt;, incrementos marginais de carga já produzem aumentos desproporcionais em latência e concorrência.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;modelagem-de-carga&quot;&gt;Modelagem de Carga&lt;/h2&gt;

&lt;p&gt;A modelagem de carga é um dos principais requisitos para se estimar o capacity planning de um sistema. Dentro de ambientes modernos, possuímos diversas ferramentas de monitoramento e observabilidade que coletam sinais de &lt;strong&gt;logs, métricas e traces emitidos pelas aplicações e seus componentes para gerar diversas dimensões de visualizações e alertas&lt;/strong&gt;. Quando vamos estimar a capacidade de um sistema, &lt;strong&gt;precisamos analisar algumas delas de forma unificada e correlacionada&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transações por segundo&lt;/strong&gt;, &lt;strong&gt;requests concorrentes&lt;/strong&gt; e o &lt;strong&gt;payload médio&lt;/strong&gt; formam, em conjunto, uma representação mais fiel do comportamento real do sistema do que qualquer uma dessas métricas analisada de forma independente pode gerar. &lt;strong&gt;Juntas, essas três métricas formam a base mais sólida para uma modelagem de carga realista.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As Transações por Segundo descrevem o ritmo de solicitações&lt;/strong&gt;, a &lt;strong&gt;concorrência descreve a pressão acumulada no sistema perante a chegada dessas solicitações&lt;/strong&gt;, e o &lt;strong&gt;tamanho do payload descreve o peso individual de cada transação&lt;/strong&gt;, a nível de networking, storage, custo de serialização e consumo de memória.&lt;/p&gt;

&lt;h3 id=&quot;transações-por-segundo&quot;&gt;Transações por Segundo&lt;/h3&gt;

&lt;p&gt;As Transações por Segundo &lt;strong&gt;representam a taxa de chegada de requisições ao sistema&lt;/strong&gt; e constituem o ponto inicial de qualquer estimativa. &lt;strong&gt;Nenhuma métrica é mais importante do que a quantidade de interações que um sistema recebe, ou irá receber.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mesmo dentro do mesmo segundo, um sistema ainda pode apresentar insights valiosos sobre bursts. &lt;strong&gt;Dois sistemas podem operar com o mesmo TPS médio e apresentar comportamentos totalmente diferentes se a distribuição temporal dessas transações variar.&lt;/strong&gt; Um workload com &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1000&lt;/code&gt; TPS distribuídos de forma homogênea ao longo do segundo impõe uma pressão completamente distinta de outro com a mesma média, porém concentrado em bursts de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5–10 ms&lt;/code&gt;, e conhecer esse nível de granularidade pode nos ajudar a estimar, com muito mais precisão, a capacidade necessária para suprir as demandas de forma inteligente.&lt;/p&gt;

&lt;h3 id=&quot;processos-concorrentes&quot;&gt;Processos Concorrentes&lt;/h3&gt;

&lt;p&gt;Os &lt;strong&gt;requests concorrentes representam uma dimensão interna do sistema que reflete sua capacidade de processamento&lt;/strong&gt;. Diferentemente das &lt;strong&gt;Transações por Segundo&lt;/strong&gt;, que descrevem a taxa de chegada de solicitações ao sistema, os &lt;strong&gt;Processos Concorrentes descrevem a quantidade de trabalho simultâneo que o sistema sustenta&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Em sistemas síncronos, como servidores gRPC ou APIs REST, isso se manifesta como &lt;strong&gt;threads ocupadas, conexões abertas&lt;/strong&gt;, entre outros recursos concorrentes. Em sistemas assíncronos, pode ser interpretado como &lt;strong&gt;mensagens em voo, partições ocupadas, consumidores ativos&lt;/strong&gt; e taxa de processamento de eventos e mensagens em brokers.&lt;/p&gt;

&lt;p&gt;Podemos ilustrar esse comportamento em APIs que apresentam &lt;strong&gt;latências aceitáveis em p95&lt;/strong&gt;, mas mantêm &lt;strong&gt;concorrência interna elevada devido a pequenas degradações em dependências externas&lt;/strong&gt;. Nesses casos, a capacidade aparente parece suficiente, enquanto o sistema já opera &lt;strong&gt;próximo a limites estruturais invisíveis&lt;/strong&gt;. &lt;strong&gt;Ter consciência de como estimar e medir a concorrência interna é fundamental para evitar esbarrar nas “curvas do joelho” do sistema.&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;tamanho-de-payload&quot;&gt;Tamanho de Payload&lt;/h3&gt;

&lt;p&gt;Estimar o tamanho do payload, sejam eles mensagens ou requests HTTP, é uma dimensão que é rotineiramente ignorada durante a estimativa de capacidade. Em sistemas com requisições mais homogêneas, ou seja, microserviços que possuem poucos endpoints ou contratos bem definidos de mensagens e eventos, é possível prever o tamanho desses payloads com certa precisão e estimar de forma mais confiável a pressão de tráfego de I/O que o sistema irá lidar. Porém, em sistemas que possuem múltiplas funcionalidades distribuídas em diversas filas e endpoints, o payload médio pode não representar uma dimensão fiel à realidade do sistema. &lt;strong&gt;O risco do erro da estimativa não está na média dessa variável, mas sim na dispersão em torno dessa média.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Payloads maiores tendem a ampliar o tempo de processamento, o consumo de memória, a pressão em garbage collection, o uso de buffers de rede e a latência de serialização.&lt;/strong&gt; Um sistema que processa majoritariamente payloads pequenos, mas ocasionalmente recebe payloads muito maiores, pode apresentar comportamento estável na média e, ainda assim, sofrer degradações abruptas sob cenários perfeitamente válidos do ponto de vista funcional. &lt;strong&gt;Essa variabilidade cria caudas longas no tempo de resposta e amplifica o efeito de filas internas&lt;/strong&gt;, mesmo sem alterações perceptíveis na TPS.&lt;/p&gt;

&lt;p&gt;Idealmente, precisamos modelar sistemas e contratos que não sofram grande variação de tamanho. Quando isso não for possível, é necessário &lt;strong&gt;estimar cada uma das funcionalidades de forma isolada&lt;/strong&gt; e se concentrar em encontrar alguma estatística que represente de maneira mais fiel o sistema diante de suas particularidades.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;cálculos-de-estimativa-de-carga&quot;&gt;Cálculos de Estimativa de Carga&lt;/h3&gt;

&lt;p&gt;Podemos estimar matematicamente nossa modelagem de carga com uma série de equações simples que podem ser aplicadas a dimensões já conhecidas do sistema ou fornecidas por times de produto. &lt;strong&gt;A seguir, iremos abordar como expandir ainda mais a aplicação dessas equações em cenários mais específicos.&lt;/strong&gt;&lt;/p&gt;

&lt;h4 id=&quot;estimativa-de-transações-por-segundo&quot;&gt;Estimativa de Transações por Segundo&lt;/h4&gt;

&lt;p&gt;Quando falamos sobre &lt;a href=&quot;/performance-capacidade-escalabilidade/&quot;&gt;Performance, Capacidade e Escalabilidade&lt;/a&gt;, já ressaltamos o quanto o throughput é uma métrica extremamente valiosa e importante para entender todo tipo de comportamento do sistema. &lt;strong&gt;Essa métrica é a primeira a precisar ser levantada porque conecta diretamente o comportamento do usuário à pressão exercida sobre a arquitetura.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Embora simples, o TPS deve ser interpretado como um valor estatístico médio, mínimo e máximo, &lt;strong&gt;e não como um fluxo contínuo e uniforme&lt;/strong&gt;. Em sistemas reais, a taxa de chegada oscila ao longo do tempo, sofre efeitos de sincronização, burstiness e correlação entre usuários ou clientes. &lt;strong&gt;Levantar o desvio padrão do TPS também pode fornecer insights valiosos sobre a variação do mesmo ao decorrer de certos períodos.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;\begin{equation}
\text{TPS} = \frac{\text{Unidades de Trabalho Processadas no Período}}{\text{Tempo em Segundos do Período}}
\end{equation}&lt;/p&gt;

&lt;p&gt;Na prática, esse valor costuma ser extraído de métricas sazonais de séries históricas, projeções de crescimento ou metas de negócio, e posteriormente ajustado para picos, sazonalidade e eventos especiais que podem acontecer em certos períodos do mês ou do ano, como promoções, ações de marketing, Black Friday, Natal, entre outros.&lt;/p&gt;

&lt;h4 id=&quot;tps-sistêmico&quot;&gt;TPS Sistêmico&lt;/h4&gt;

&lt;p&gt;O &lt;strong&gt;TPS Sistêmico representa a capacidade efetiva de vazão de todo o sistema&lt;/strong&gt;, considerando não apenas a aplicação principal, mas todas as suas dependências críticas. Em arquiteturas distribuídas, &lt;strong&gt;o throughput observado externamente é sempre limitado pelo menor gargalo ativo no caminho de processamento&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;\begin{equation}
\text{TPS Sistêmico} =
\min(\text{TPS App}, \text{TPS Database}, \text{TPS Cache}, \text{TPS etc.})
\end{equation}&lt;/p&gt;

&lt;p&gt;Não importa o quão escalável seja a camada de aplicação se o banco de dados, o cache, o broker de mensagens ou uma API externa impõem limites mais restritivos. &lt;strong&gt;Além disso, o gargalo dominante pode mudar dinamicamente conforme o perfil de carga, o tamanho de payload ou o tipo de operação.&lt;/strong&gt;&lt;/p&gt;

&lt;h4 id=&quot;estimativa-de-tamanho-de-payload&quot;&gt;Estimativa de Tamanho de Payload&lt;/h4&gt;

&lt;p&gt;A estimativa de tamanho de payload busca quantificar o volume médio de dados trafegados por requisição, considerando tanto o corpo da mensagem quanto o overhead de protocolos de transporte, como HTTP, TLS, mTLS, entre outros.&lt;/p&gt;

&lt;p&gt;\begin{equation}
\text{Payload_bytes} = (\text{Body_bytes} + \text{Headers_bytes})
\end{equation}&lt;/p&gt;

&lt;p&gt;Entretanto, em sistemas reais, é necessário considerar camadas adicionais de overhead, como encoding, compressão, criptografia e framing de protocolo, que podem tanto ampliar quanto reduzir o tamanho efetivamente trafegado.&lt;/p&gt;

&lt;p&gt;\begin{equation}
\text{Payload_bytes} = (\text{Body_bytes} + \text{Headers_bytes}) \times \text{Overhead}
\end{equation}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mais importante do que o valor médio absoluto é a variabilidade do payload&lt;/strong&gt;, pois payloads grandes tendem a &lt;strong&gt;amplificar latência, consumo de memória e tempo de processamento&lt;/strong&gt;, criando &lt;strong&gt;caudas longas que afetam a estabilidade do sistema&lt;/strong&gt;, mesmo quando a média parece controlada.&lt;/p&gt;

&lt;h4 id=&quot;estimativa-de-bytes-de-uma-transação&quot;&gt;Estimativa de Bytes de Uma Transação&lt;/h4&gt;

&lt;p&gt;Enquanto o payload representa uma única mensagem, a &lt;strong&gt;estimativa de bytes por transação considera o custo completo de uma interação&lt;/strong&gt;, incluindo request e response. Essa visão é mais adequada para &lt;strong&gt;análises de capacidade fim a fim&lt;/strong&gt; e para &lt;strong&gt;estimativas de custo e banda sob carga real&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;\begin{equation}
\text{Payload_médio(bytes)} = \text{Request_payload} + \text{Response_payload}
\end{equation}&lt;/p&gt;

&lt;p&gt;Essa métrica se torna especialmente relevante em &lt;strong&gt;APIs verbosas&lt;/strong&gt;, fluxos com &lt;strong&gt;respostas ricas em dados&lt;/strong&gt; ou sistemas onde o &lt;strong&gt;volume de resposta cresce com o contexto da operação&lt;/strong&gt;. &lt;strong&gt;Ignorar o payload de resposta é um erro comum&lt;/strong&gt; que pode fazer muita diferença para entender &lt;strong&gt;divergências entre estimativas e o tráfego real&lt;/strong&gt;.&lt;/p&gt;

&lt;h4 id=&quot;estimativa-de-banda-pelo-payload-e-transações-por-segundo&quot;&gt;Estimativa de Banda pelo Payload e Transações por Segundo&lt;/h4&gt;

&lt;p&gt;A &lt;strong&gt;estimativa de banda conecta diretamente o throughput lógico (TPS) com o consumo físico de rede&lt;/strong&gt;. A partir do payload médio por transação, é possível estimar o volume de dados trafegados por segundo e, consequentemente, &lt;strong&gt;dimensionar links, limites de ingress e custos de transferência&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;\begin{equation}
\text{Banda_bytes/s} = \text{TPS} \times \text{Payload_médio(bytes)}
\end{equation}&lt;/p&gt;

&lt;p&gt;Esse cálculo fornece uma &lt;strong&gt;aproximação inicial&lt;/strong&gt;, que deve ser refinada com fatores como &lt;strong&gt;retries, retransmissões, fan-out interno e replicação de tráfego entre zonas ou regiões&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;perfis-de-tráfego&quot;&gt;Perfis de Tráfego&lt;/h3&gt;

&lt;p&gt;Os Perfis de Tráfego permitem compreender como a carga do sistema se distribui ao longo do tempo, revelando padrões de uso, assimetrias e variações que não aparecem em métricas agregadas. Ao analisar comportamentos diários, semanais e sazonais, é possível antecipar picos previsíveis, identificar janelas de ociosidade e planejar capacidade de forma proativa, alinhando desempenho, custo e previsibilidade, vamos explorar conceitualmente cada um deles.&lt;/p&gt;

&lt;h4 id=&quot;perfil-diário&quot;&gt;Perfil Diário&lt;/h4&gt;

&lt;p&gt;O Perfil Diário busca estudar o comportamento de uso do sistema ao decorrer de um dia corrido, um período fechado de 24 horas. Normalmente, está associado aos hábitos e à rotina dos usuários e aos agendamentos das integrações sistêmicas. Aqui temos análises mais granulares, com agregações de poucos minutos, como 1, 2, 5 e 10 minutos, para análises de tendência. Podemos, aqui, analisar diversas estatísticas, como média, p95, p99, tempo máximo e mínimo da agregação dos requests.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/perfil-diario.png&quot; alt=&quot;Perfil Diário&quot;&gt;&lt;/p&gt;

&lt;p&gt;Em sistemas com finalidade operacional voltados a usuários finais, &lt;strong&gt;podemos entender em que momento do dia eles começam a operar dentro do sistema&lt;/strong&gt;, normalmente tendo sua maior pressão de &lt;strong&gt;tráfego dentro das janelas de expediente&lt;/strong&gt;, aliviando nos horários de almoço e ficando com pouco ou nenhum tráfego durante a noite e a madrugada. Em sistemas de delivery de comida, &lt;strong&gt;podemos presumir os maiores picos de uso minutos ou horas antes dos horários de almoço e jantar&lt;/strong&gt;; em sistemas de carona, &lt;strong&gt;próximos do início e do fim do expediente&lt;/strong&gt;; e, em sistemas B2B ou internos, os picos tendem a se alinhar a &lt;strong&gt;rotinas operacionais, fechamentos de lote ou execuções agendadas&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Do ponto de vista de capacity planning, &lt;strong&gt;o perfil diário é crítico porque define a duração dos períodos de alta e baixa utilização&lt;/strong&gt;. Podemos utilizar esse tipo de estudo para entender os &lt;strong&gt;momentos do dia em que nosso tráfego irá aumentar de forma rotineira&lt;/strong&gt;, ajustando preventivamente nossa capacidade, ou quando o sistema ficará subutilizado.&lt;/p&gt;

&lt;h4 id=&quot;perfil-semanal&quot;&gt;Perfil Semanal&lt;/h4&gt;

&lt;p&gt;O Perfil Semanal busca entender &lt;strong&gt;padrões de carga que se repetem durante os dias da semana&lt;/strong&gt;, em um período de 7 dias, para encontrar padrões e desvios de uso, erros e latência distribuídos ao longo da semana. Para isso, utilizamos agregações de tempo maiores, como 1, 2, 3 e 5 horas, ainda aplicando estatísticas de média e percentis de forma comparativa para entender desvios e comportamentos do sistema.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/perfil-semanal.png&quot; alt=&quot;Perfil Semanal&quot;&gt;&lt;/p&gt;

&lt;p&gt;Um sistema pode operar confortavelmente abaixo do ponto saudável durante boa parte da semana e, ainda assim, &lt;strong&gt;entrar em regiões de saturação previsível em dias específicos&lt;/strong&gt;. Diferente do perfil diário, que tende a ser mais suave e previsível, &lt;strong&gt;o perfil semanal pode introduzir assimetrias abruptas&lt;/strong&gt;, como segundas-feiras sistematicamente mais carregadas ou sextas-feiras com picos concentrados em horários específicos, uso mais suavizado durante o restante dos dias úteis e tráfego baixo durante os finais de semana.&lt;/p&gt;

&lt;p&gt;Esse perfil é &lt;strong&gt;útil para entender desvios de uso do sistema&lt;/strong&gt; e nos ajuda a projetar capacidade com base em períodos repetitivos dentro de uma semana, nos proporcionando formas de realizar warm-ups preventivos ou descomissionamento de contêineres ou servidores em períodos de ociosidade conhecida.&lt;/p&gt;

&lt;h4 id=&quot;perfil-sazonal&quot;&gt;Perfil Sazonal&lt;/h4&gt;

&lt;p&gt;O Perfil Sazonal descreve variações de carga em escalas mais longas, como semanas, meses ou anos, e está normalmente associado a ciclos de negócio, eventos externos ou mudanças de comportamento dos usuários. Esse tipo de dimensão nos ajuda a projetar diversas estratégias valiosas de capacity planning. Aqui, a agregação pode ser feita em períodos maiores, como dias ou semanas.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/perfil-sazonal.png&quot; alt=&quot;Perfil Sazonal&quot;&gt;&lt;/p&gt;

&lt;p&gt;Essa estratégia nos permite estudar o crescimento gradativo do sistema e como ele se comporta em períodos específicos de fatias de tempo maiores. Exemplos comuns i&lt;strong&gt;ncluem períodos promocionais, datas comemorativas, ciclos fiscais, eventos regulatórios ou mesmo fatores externos, como clima e calendário escolar&lt;/strong&gt;. Podemos atingir níveis de escalabilidade adequados analisando apenas períodos mensais ou semanais, mas, ainda assim, &lt;strong&gt;sofrer falhas de capacidade em determinados períodos não estacionários do ano que não seguem o padrão de um “mês comum” ou “semana comum”,&lt;/strong&gt; como, por exemplo, e-commerces durante promoções de Black Friday, onde uma semana específica de novembro excede todos os padrões observados no restante do ano.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A combinação dos perfis diários para análises granulares, semanais para identificação de tendências e sazonais em nível de mês e ano nos permite elevar significativamente nossa capacidade de projetar e estimar a capacidade dos sistemas ao longo de longos períodos de forma profissional e estruturada.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;projeção-de-crescimento&quot;&gt;Projeção de Crescimento&lt;/h3&gt;

&lt;p&gt;A projeção de crescimento é um exercício de capacity planning no qual a análise deixa de ser estática e reativa e passa a adotar estratégias de antecipação. Diferente do tópico anterior, em que as estimativas buscavam entender o sistema e compreender seus comportamentos e tendências, a projeção busca responder a uma pergunta um pouco mais difícil: &lt;strong&gt;como a carga do meu sistema será daqui a 3, 6 ou 12 meses?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Responder a esse tipo de pergunta exige uma análise temporal extensa do passado para entender o crescimento natural e também uma parceria com os times de negócio para compreender as expectativas e perspectivas de mercado da empresa para os produtos. &lt;strong&gt;A missão da engenharia é suportar as expectativas dos produtos de forma sustentável e realista&lt;/strong&gt;, portanto, as expectativas sobre o futuro do sistema devem ser de conhecimento comum entre tecnologia e negócios.&lt;/p&gt;

&lt;h4 id=&quot;crescimento-linear&quot;&gt;Crescimento Linear&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/crescimento-linear.png&quot; alt=&quot;Crescimento Linear&quot;&gt;&lt;/p&gt;

&lt;p&gt;O crescimento linear assume que métricas como TPS, volume de dados ou usuários ativos aumentam de forma proporcional ao tempo. O número de usuários, licenças, transações ou compras cresce seguindo uma tendência semelhante todos os meses ou semanas. &lt;strong&gt;Pequenas variações dessa taxa, para mais ou para menos, não descaracterizam esse comportamento como linear&lt;/strong&gt; nesse tipo de cenário.&lt;/p&gt;

&lt;p&gt;Podemos encontrar esse padrão em estágios iniciais de um produto ou em sistemas muito bem estabelecidos — cenários opostos, mas que compartilham uma tendência de crescimento previsível e estável. Nesse tipo de análise, entendemos, por inferência, que &lt;strong&gt;dobrar a quantidade de transações ou usuários de um sistema implica diretamente em dobrar sua capacidade&lt;/strong&gt;.&lt;/p&gt;

&lt;h4 id=&quot;crescimento-não-linear&quot;&gt;Crescimento Não Linear&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/crescimento-nao-linear.png&quot; alt=&quot;Crescimento Não Linear&quot;&gt;&lt;/p&gt;

&lt;p&gt;Em sistemas mais variáveis, o tráfego e a capacidade raramente crescem de forma linear, podendo apresentar comportamentos não previsíveis ao longo do tempo, alternando períodos lineares, exponenciais ou irregulares.&lt;/p&gt;

&lt;p&gt;O crescimento não linear tende a invalidar análises de comportamento prévias. Esses cenários podem ocorrer devido a mudanças de comportamento dos usuários ou à introdução de novas funcionalidades, onde &lt;strong&gt;pequenas variações no número de usuários ou eventos podem gerar aumentos desproporcionais em TPS, latência ou concorrência interna&lt;/strong&gt;. Esse tipo de variação também pode ocorrer em função de testes de estratégias de marketing e negócios, que provocam comportamentos imprevisíveis de novos usuários e cargas no sistema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crescimentos não lineares e não planejados podem ser extremamente perigosos para sistemas que operam próximos da sua taxa máxima de processamento conhecida.&lt;/strong&gt;&lt;/p&gt;

&lt;h4 id=&quot;crescimento-mediante-novas-features-e-eventos-de-negócio&quot;&gt;Crescimento Mediante Novas Features e Eventos de Negócio&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/crescimento-mudancas.png&quot; alt=&quot;Crescimento Mudanças&quot;&gt;&lt;/p&gt;

&lt;p&gt;Uma dimensão extremamente significativa que nos permite atuar em conjunto com os times de negócio é a &lt;strong&gt;projeção de crescimento mediante mudanças, novas features e eventos planejados&lt;/strong&gt;. &lt;strong&gt;O perfil de tráfego de um sistema pode se alterar de forma brusca com a introdução de novas funcionalidades, migrações de usuários ou campanhas de marketing de conversão&lt;/strong&gt;, entre outros fatores. Ter esses eventos alinhados com os times responsáveis nos dá a oportunidade de &lt;strong&gt;trabalhar de forma planejada e preventiva para suportar essa nova entrada de carga&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Uma mudança ou evento de negócio voltado a &lt;strong&gt;atrair mais usuários para o sistema ou aumentar sua taxa de uso pode deslocar os limites de processamento&lt;/strong&gt;, aproximando o sistema de sua &lt;strong&gt;“curva do joelho” de performance e capacidade&lt;/strong&gt; com as funcionalidades já existentes. Além disso, &lt;strong&gt;a adição de uma nova feature pode multiplicar o número de chamadas internas por requisição, aumentar significativamente o payload médio ou introduzir dependências adicionais no fluxo sistêmico&lt;/strong&gt;. Realizar testes de carga contemplando as características das novas funcionalidades é fundamental para reavaliar a capacidade necessária para atendê-las de forma adequada.&lt;/p&gt;

&lt;p&gt;Nem toda mudança ou feature exige um novo planejamento de capacidade em nível de detalhe máximo, mas aquelas que têm o &lt;strong&gt;objetivo explícito de alterar o comportamento do sistema como um todo precisam, sim, ser consideradas para garantir maior segurança&lt;/strong&gt;. &lt;strong&gt;Levantar estimativas e expectativas com todos os envolvidos nessas mudanças é essencial para planejamentos mais assertivos.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;capacidade-end-to-end-e2e&quot;&gt;Capacidade End to End (E2E)&lt;/h3&gt;

&lt;p&gt;Avaliar a capacidade End to End de um fluxo, sistema ou transação nos ajuda a tomar responsabilidade sobre o encadeamento total entre os serviços que os compõe. Avaliar todas as dependências e integrações, como a soma de todas as capacidades individuais, revela onde o fluxo se limita, onde os gargalos emergem e quais sistemas podem falhar sobre carga real. Precisamos avaliar tanto o Throughput Individual de cada sistema para ter uma margem de avaliação e o sistêmico, onde vamos avaliar como todos os “steps” se comportam em cadeia.&lt;/p&gt;

&lt;h4 id=&quot;throughput-individual&quot;&gt;Throughput individual&lt;/h4&gt;

&lt;p&gt;O throughput individual representa a capacidade máxima sustentável de &lt;strong&gt;um componente isolado dentro do sistema&lt;/strong&gt;, avaliada fora do contexto completo do fluxo fim a fim. Ele descreve quanto trabalho um serviço, banco de dados, fila ou consumidor consegue processar por unidade de tempo sob condições controladas, considerando seus próprios limites de CPU, memória, I/O, concorrência e configuração interna.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/tput-individual.png&quot; alt=&quot;Throughput individual&quot;&gt;&lt;/p&gt;

&lt;p&gt;Essa dimensão pode ser avaliada em dois cenários. No primeiro, considera-se o contexto de um microserviço e suas dependências diretas, como caches, filas e bancos de dados, onde a capacidade individual é avaliada dentro de um domínio de serviço. No segundo, a análise ocorre em cada microcomponente de forma isolada. O primeiro cenário serve para avaliar uma fragmentação específica de negócio, como “quanto esse sistema de emissão de boletos consegue processar”, enquanto o segundo responde perguntas como “quanto esse banco de dados suporta de I/O” e métricas derivadas. &lt;strong&gt;Ambos fornecem insights valiosos sobre capacidade de produção.&lt;/strong&gt;&lt;/p&gt;

&lt;h4 id=&quot;throughput-sistêmico&quot;&gt;Throughput sistêmico&lt;/h4&gt;

&lt;p&gt;O throughput sistêmico corresponde à capacidade máxima de um sistema ou funcionalidade, contemplando todas as suas dependências. &lt;strong&gt;O objetivo é ser agnóstico à capacidade individual de cada componente&lt;/strong&gt;, levando em consideração apenas o fluxo completo, da entrada até a resposta final. Essa estratégia serve para avaliar a capacidade total da solução e identificar oportunidades de melhoria relacionadas a filas e gargalos.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/tput-sistemico.png&quot; alt=&quot;Throughput Sistêmico&quot;&gt;&lt;/p&gt;

&lt;p&gt;Em termos práticos, o throughput sistêmico busca identificar o ponto de desequilíbrio entre a taxa de chegada &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(λ)&lt;/code&gt; e a taxa de processamento &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(μ)&lt;/code&gt; em cada hop do fluxo, determinando qual componente exerce maior pressão contrária ao processamento fim a fim. Mesmo que serviços isolados operem com folga, &lt;strong&gt;o sistema como um todo pode apresentar throughput limitado quando a variabilidade de throughput e latência se acumula ao longo da comunicação end to end&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Do ponto de vista de capacity planning, &lt;strong&gt;medir throughput sistêmico implica observar o comportamento do sistema sob carga contínua&lt;/strong&gt;, e não apenas picos instantâneos. Um sistema pode atingir um TPS elevado por curtos períodos e, ainda assim, não ser capaz de sustentar essa vazão ao longo do tempo, caracterizando uma capacidade apenas nominal, e não operacional.&lt;/p&gt;

&lt;h4 id=&quot;dependência-do-gargalo&quot;&gt;Dependência do Gargalo&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/gargalo.png&quot; alt=&quot;Gargalo&quot;&gt;&lt;/p&gt;

&lt;p&gt;Como discutido no capítulo sobre &lt;a href=&quot;/performance-capacidade-escalabilidade/&quot;&gt;performance, capacidade e escalabilidade&lt;/a&gt;, gargalos são “pontos no sistema onde o desempenho ou a capacidade são limitados devido a um componente específico que não consegue lidar eficientemente com a carga atual”. Se, para completar uma transação, é necessária a resposta de três microserviços — onde um deles consegue processar de forma saudável &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;400&lt;/code&gt; transações por segundo, outro &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;600&lt;/code&gt; e outro &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1000&lt;/code&gt; — o sistema como um todo fica limitado à menor taxa de processamento, ou seja, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;400&lt;/code&gt; transações por segundo. &lt;strong&gt;Exceder essa taxa tende a provocar filas sistêmicas e pressão crescente sobre processos, threads e dependências associadas ao ponto de gargalo&lt;/strong&gt;, impactando todo o fluxo da aplicação.&lt;/p&gt;

&lt;p&gt;\begin{equation}
\text{Gargalo} = \min(s_1, s_2, s_3, \ldots)
\end{equation}&lt;/p&gt;

&lt;p&gt;O gargalo atual do sistema é representado pelo componente ou processo com a menor taxa de processamento &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(μ)&lt;/code&gt; em todo o fluxo. &lt;strong&gt;Identificar essa dependência é fundamental para direcionar melhorias de forma priorizada e estratégica.&lt;/strong&gt; Como visto anteriormente, os gargalos também se movem com o tempo: &lt;strong&gt;uma otimização local pode simplesmente deslocar o gargalo para outra parte subsequente do sistema.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;planejamento-de-capacidade&quot;&gt;Planejamento de Capacidade&lt;/h1&gt;

&lt;p&gt;O objetivo desta seção é fornecer um roteiro aplicável de planejamento de capacidade, levando em conta a base teórica compilada ao longo deste capítulo. A partir daqui, apresento uma “pseudo-estrutura” de um movimento de capacity planning para que seja criado um mapa mental adaptável a diversos cenários.&lt;/p&gt;

&lt;h2 id=&quot;delimitar-o-fluxo-funcionalidades-e-componentes&quot;&gt;Delimitar o Fluxo, Funcionalidades e Componentes&lt;/h2&gt;

&lt;p&gt;O primeiro passo a ser seguido é definir qual fluxo sistêmico está sendo avaliado. Testar “o sistema” como um todo pode levar a modelagens genéricas que não refletem com precisão a realidade esperada. Portanto, identifique as funcionalidades, os contratos, os métodos de entrada, os serviços envolvidos, os dados manipulados, as respostas geradas e para onde elas são enviadas.&lt;/p&gt;

&lt;p&gt;Nessa fase de levantamento, precisamos listar todos os microserviços, seus bancos de dados, filas e tópicos, bem como identificar quais fluxos são síncronos, quais são assíncronos e como todos eles se comunicam entre si. &lt;strong&gt;Esse passo estabelece o escopo do throughput sistêmico&lt;/strong&gt;, evitando análises locais desconectadas da experiência real do usuário.&lt;/p&gt;

&lt;h2 id=&quot;levantar-as-estimativas-de-carga&quot;&gt;Levantar as Estimativas de Carga&lt;/h2&gt;

&lt;p&gt;Com o fluxo definido, o próximo passo é construir a carga base, utilizando exatamente as métricas discutidas anteriormente, como o TPS médio, os picos, os perfis diários e semanais, além das datas ou períodos sazonais que indicam mudanças de comportamento e o quanto essas variações podem ocorrer.&lt;/p&gt;

&lt;p&gt;Devemos estimar os payloads, seus tamanhos e o volume de banda que irão trafegar durante os perfis levantados. Aqui também surge a oportunidade, caso ainda não esteja claro, de alinhar com os times de produto e de negócio quais são as variáveis esperadas de tempo de resposta e disponibilidade. &lt;strong&gt;Tornar esses indicadores explícitos é um grande facilitador para avaliar se o capacity planning está efetivamente adequado&lt;/strong&gt;, ou se estamos subprovisionando ou exagerando em recursos ociosos.&lt;/p&gt;

&lt;p&gt;Neste ponto, &lt;strong&gt;o objetivo não é precisão absoluta, mas ordem de grandeza&lt;/strong&gt;. O modelo inicial serve para responder à pergunta: “em que condições meu sistema opera hoje?”, evitando projeções desconexas ou irreais.&lt;/p&gt;

&lt;h2 id=&quot;identificação-do-throughput-individual-dos-componentes-e-serviços&quot;&gt;Identificação do Throughput Individual dos Componentes e Serviços&lt;/h2&gt;

&lt;p&gt;Antes de projetar crescimento, é necessário entender os limites individuais de cada componente relevante do fluxo, identificando quais deles podem exercer pressão contrária, agravar gargalos ou gerar “curvas do joelho” de forma prematura, e, principalmente, em que condições isso acontece.&lt;/p&gt;

&lt;p&gt;Aqui lidamos com variáveis como o TPS máximo sustentável do serviço, os limites de concorrência — threads, conexões e consumers disponíveis — e a capacidade efetiva de cada uma de suas dependências, como bancos de dados, caches, brokers e APIs externas. Dependências externas podem ser mockadas em ambientes controlados para que não comprometam testes de limite operacional do serviço.&lt;/p&gt;

&lt;h2 id=&quot;derivação-do-throughput-sistêmico&quot;&gt;Derivação do Throughput Sistêmico&lt;/h2&gt;

&lt;p&gt;A partir dos throughputs individuais, deriva-se o throughput sistêmico, aplicando explicitamente a lógica do menor gargalo. &lt;strong&gt;Aqui respondemos perguntas como:&lt;/strong&gt; qual componente limita a vazão hoje? O gargalo é rígido ou pode lidar com escalabilidade horizontal dentro de uma determinada janela de tempo? O throughput, o tempo de resposta e a taxa de erros variam de acordo com o tempo e com as oscilações de tráfego dentro dos perfis de carga identificados?&lt;/p&gt;

&lt;p&gt;Essa etapa é uma das mais importantes do processo, pois &lt;strong&gt;a capacidade real emerge do encadeamento entre os serviços&lt;/strong&gt;, e não da análise isolada de componentes.&lt;/p&gt;

&lt;h2 id=&quot;levantamento-da-projeção-de-crescimento&quot;&gt;Levantamento da Projeção de Crescimento&lt;/h2&gt;

&lt;p&gt;Com a capacidade atual compreendida, o planejamento passa a incorporar projeções, evitando o erro clássico de assumir um único crescimento linear. Nesse momento, é fundamental incluir os times de negócio e, quando necessário, níveis executivos, para entender as expectativas futuras do sistema.&lt;/p&gt;

&lt;p&gt;O objetivo aqui não é prever o futuro com precisão, mas &lt;strong&gt;entender até que ponto o sistema atual consegue suportar os objetivos da empresa&lt;/strong&gt; e identificar oportunidades de melhoria para o horizonte planejado, evitando que a evolução ocorra de forma reativa, já com a experiência do cliente degradada.&lt;/p&gt;

&lt;h2 id=&quot;avaliar-o-custo-e-as-margens-operacionais&quot;&gt;Avaliar o Custo e as Margens Operacionais&lt;/h2&gt;

&lt;p&gt;Neste ponto, o planejamento incorpora explicitamente custo e risco. A pergunta deixa de ser “quanto o sistema aguenta” e passa a ser &lt;strong&gt;“quanto ele aguenta com previsibilidade e custo aceitável para o negócio”&lt;/strong&gt;. Trabalhamos com dimensões como o impacto de overprovisioning versus underprovisioning, quais regiões do “Ponto Saudável” são aceitáveis do ponto de vista orçamentário e como isso se relaciona com a zona de pré-joelho de throughput e latência do sistema.&lt;/p&gt;

&lt;p&gt;Aqui, &lt;strong&gt;a capacidade passa a ser tratada como orçamento&lt;/strong&gt;, e não apenas como um limite técnico.&lt;/p&gt;

&lt;h2 id=&quot;definição-dos-limites-operacionais&quot;&gt;Definição dos Limites Operacionais&lt;/h2&gt;

&lt;p&gt;O resultado do capacity planning não deve ser um único número de “quanto aguenta”, mas sim um conjunto de limites operacionais bem definidos, como o TPS sustentável, o &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L(Alvo)&lt;/code&gt;, a latência máxima aceitável (em termos de média e percentis) e a taxa de erro máxima tolerável. &lt;strong&gt;Essas definições precisam ser amplamente conhecidas entre os stakeholders do produto&lt;/strong&gt;, pois também ajudam a identificar pontos futuros onde uma reavaliação arquitetural será necessária, alinhando expectativas de orçamento e planejamento estratégico.&lt;/p&gt;

&lt;h2 id=&quot;testes-de-carga-e-estresse&quot;&gt;Testes de Carga e Estresse&lt;/h2&gt;

&lt;p&gt;O último passo é validar, na prática, se o sistema atende aos requisitos estabelecidos e se possui as parametrizações adequadas para escalar de forma dinâmica ou estática. Aqui, devemos executar testes de carga média (Average Load), estresse, spikes conhecidos e testes de breakpoint para identificar quando o sistema ultrapassa o &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L(Alvo)&lt;/code&gt; e onde ele efetivamente entra em colapso.&lt;/p&gt;

&lt;p&gt;Esses testes podem ser realizados de forma pontual, mas o ideal é que sejam executados por períodos prolongados, aproximando-se de cenários reais de operação. &lt;strong&gt;É fundamental coletar evidências e documentar a capacidade real&lt;/strong&gt;, e, quando gargalos ou oportunidades de melhoria forem identificados, direcioná-los ao backlog para tratamento e priorização.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;referências&quot;&gt;Referências&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://queue.acm.org/detail.cfm?id=1854041&quot;&gt;Improving the performance of complex software is difficult, but understanding some fundamental principles can make it easier.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://pt.wikipedia.org/wiki/Teoria_das_filas&quot;&gt;Teoria das Filas&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.scielo.br/j/rae/a/34fWxG9RqkRmd8spnbPfJnR/?format=html&amp;amp;lang=pt&quot;&gt;Elementos das Teorias das Filas&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://br.k21.global/gestao-de-times-ageis/lei-de-little-littles-law-a-ciencia-por-tras-de-fazer-menos-e-entregar-mais&quot;&gt;Lei de Little (Little’s Law): A Ciência por Trás de Fazer Menos e Entregar Mais&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://en-wikipedia-org.translate.goog/wiki/Little%27s_law?_x_tr_sl=en&amp;amp;_x_tr_tl=pt&amp;amp;_x_tr_hl=pt&amp;amp;_x_tr_pto=tc&quot;&gt;Little’s law&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Knee_of_a_curve&quot;&gt;Knee of a curve&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://medium.com/@lahirukavikara/the-knee-in-performance-testing-where-throughput-meets-the-wall-904f90474346&quot;&gt;The “Knee” in Performance Testing: Where Throughput Meets the Wall&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dl.acm.org/doi/epdf/10.1145/1958746.1958784&quot;&gt;A Capacity Planning Process for Performance Assurance of Component-Based Distributed Systems&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.cloud.google.com/capacity-planner/docs/overview&quot;&gt;Capacity Planner - Google&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 12 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://fidelissauro.dev/capacity-planning/</link>
        <guid isPermaLink="true">https://fidelissauro.dev/capacity-planning/</guid>
        
        
        <category>system-design</category>
        
        <category>engineering</category>
        
      </item>
    
      <item>
        <title>System Design - Event Sourcing</title>
        <description>&lt;p&gt;Dando sequência à exploração de patterns arquiteturais da série de System Design, hoje vamos colocar um marco de complexidade estrutural falando de Event Sourcing e dos conceitos e componentes que viabilizam a implementação do mesmo. &lt;strong&gt;O objetivo deste capítulo será oferecer uma revisão honesta e conceitual sobre a adoção desse modelo, e também suas complexidades sistêmicas, que são altas.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ao longo do conteúdo, são discutidos os principais conceitos que compõem esse modelo, como &lt;strong&gt;Event Store, Event Bus, Projections, Read Models, Snapshotting e Rehydration&lt;/strong&gt;, e como eles se relacionam para formar um &lt;strong&gt;ecossistema transacional e historicamente reconstruível.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Além dos fundamentos conceituais, o texto aborda &lt;strong&gt;estratégias práticas para lidar com consistência eventual, versionamento, idempotência e controle de concorrência&lt;/strong&gt;, temas essenciais para o design de &lt;strong&gt;sistemas distribuídos de alta confiabilidade e larga escala.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;definindo-event-sourcing&quot;&gt;Definindo Event Sourcing&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Event Sourcing é um padrão arquitetural que busca registrar todos os eventos que alteram o estado de uma entidade em uma base de dados de forma histórica.&lt;/strong&gt; Esse padrão é usado para &lt;strong&gt;“contar a história” de uma transação ou entidade ao longo de todo o seu ciclo de vida.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Em sistemas em que uma entidade muda com frequência, como, por exemplo, os estados de um pagamento, os estados de um usuário ou operador do sistema, uma compra ou as fases de fabricação de um produto, &lt;strong&gt;o Event Sourcing visa registrar cada alteração de forma imutável.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O objetivo não é armazenar apenas o estado atual, mas todas as alterações ao longo do tempo de forma cronológica&lt;/strong&gt;, como um log de eventos que podem ser auditados e recompostos. Isso é útil em &lt;strong&gt;sistemas event-driven&lt;/strong&gt;, que emitem eventos constantemente para outros sistemas e que, eventualmente, precisam &lt;strong&gt;recompor os estados de forma distribuída.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;persistência-tradicional-e-event-sourcing&quot;&gt;Persistência Tradicional e Event Sourcing&lt;/h1&gt;

&lt;p&gt;À medida que a evolução arquitetural de sistemas distribuídos ocorre e desenvolve integrações e dependências mais complexas, &lt;strong&gt;a forma tradicional de persistir o “estado atual” de um registro dentro do sistema tende a se tornar limitada&lt;/strong&gt; devido a critérios de resiliência e recuperação de falhas.&lt;/p&gt;

&lt;p&gt;Em modelos tradicionais, &lt;strong&gt;o paradigma central é o “State Mutation”&lt;/strong&gt;, onde o estado atual é sempre substituído a cada operação que ocorre. &lt;strong&gt;A proposta é responder como uma entidade do sistema “está agora”, mas não “como ela chegou até aqui”.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/persistencia-tradicional.drawio.png&quot; alt=&quot;Persistência Tradicional&quot;&gt;&lt;/p&gt;

&lt;p&gt;Como visto, &lt;strong&gt;o estado de cada entidade é mutável por padrão&lt;/strong&gt;, ou seja, cada operação de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPDATE&lt;/code&gt; e &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt; substitui as informações anteriores, apagando o histórico. &lt;strong&gt;Por exemplo, em um sistema de pagamentos, podemos receber uma série de eventos de domínio que representam ações realizadas diretamente sobre a entidade.&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Evento&lt;/th&gt;
      &lt;th&gt;Ação&lt;/th&gt;
      &lt;th&gt;Status&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;PagamentoCriado(valor=100)&lt;/td&gt;
      &lt;td&gt;Insert&lt;/td&gt;
      &lt;td&gt;status=criado&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;PagamentoConfirmado&lt;/td&gt;
      &lt;td&gt;Update&lt;/td&gt;
      &lt;td&gt;status=confirmado&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;PagamentoEstornado&lt;/td&gt;
      &lt;td&gt;Update&lt;/td&gt;
      &lt;td&gt;status=estornado&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O modelo de Event Sourcing propõe uma inversão conceitual&lt;/strong&gt;, onde, em vez de armazenar o estado atual de entidades e registros após uma série de operações de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UPDATE&lt;/code&gt; e &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DELETE&lt;/code&gt;, &lt;strong&gt;o sistema acumula uma sequência de eventos imutáveis e armazena todos eles para derivar o estado atual.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/persistencia-event-sourcing.drawio.png&quot; alt=&quot;Persistência Event Sourcing&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cada operação representa uma ação imutável que indica que “algo aconteceu” e está permanentemente registrada&lt;/strong&gt;, fazendo com que &lt;strong&gt;o estado represente, de fato, uma sequência ordenada e temporal de eventos&lt;/strong&gt;, e não apenas sua atualização mais recente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Todas as operações em um sistema baseado em Event Sourcing são naturalmente inserções de novos dados sobre o estado da entidade&lt;/strong&gt;, sendo necessário &lt;strong&gt;recuperar o último estado sempre que ele precisar ser consultado&lt;/strong&gt;. Isso exige mais das operações de leitura em casos de alto volume — &lt;strong&gt;um trade-off conhecido&lt;/strong&gt;, em que é necessário empregar otimizações avançadas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Esse modelo de persistência, quando construído de forma consciente e responsável, permite criar sistemas auditáveis, reproduzíveis e naturalmente reativos&lt;/strong&gt;, mas &lt;strong&gt;exige um nível elevado de maturidade de engenharia&lt;/strong&gt; para evitar pontos de gargalo e custos excessivos.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;arquitetura-event-sourcing&quot;&gt;Arquitetura Event-Sourcing&lt;/h1&gt;

&lt;h2 id=&quot;agregados&quot;&gt;Agregados&lt;/h2&gt;

&lt;p&gt;Dentro de uma arquitetura de Event Sourcing, &lt;strong&gt;o agregado é a unidade lógica e transacional que agrupa uma entidade e todas as regras de negócio necessárias para garantir sua consistência interna&lt;/strong&gt;. Ele representa o &lt;strong&gt;objeto no qual eventos são aplicados, validados, ordenados e evoluídos&lt;/strong&gt;, assegurando que o &lt;strong&gt;estado resultante seja sempre derivado de uma sequência determinística de fatos temporais&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Agregados são a &lt;strong&gt;estrutura de dados que permite um contexto de consistência&lt;/strong&gt;, responsável por decidir quais eventos podem ocorrer, em que ordem e sob quais condições, preservando as modificações das entidades dentro do domínio. &lt;strong&gt;Dentro do agregado, as mutações de estado são convertidas em eventos imutáveis&lt;/strong&gt;, que posteriormente serão armazenados no Event Store e publicados no Event Bus, sendo a principal fonte de dados de uma arquitetura de Event Sourcing.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;event-store&quot;&gt;Event Store&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;O Event Store é o banco de dados central de uma arquitetura baseada em Event Sourcing.&lt;/strong&gt; Um banco de dados de Event Store deve ser tratado como um &lt;strong&gt;ledger imutável&lt;/strong&gt;, responsável por armazenar o log de todos os eventos que registram mudanças de estado das entidades do sistema, &lt;strong&gt;respeitando uma ordem temporal e absoluta.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A estrutura de dados de um Event Store, em vez de atualizar o estado atual, deve anexar um novo evento ao final do fluxo (stream) associado a uma determinada entidade ou agregado.&lt;/strong&gt; Cada stream representa &lt;strong&gt;a linha do tempo de uma transação.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Um Event Store &lt;strong&gt;não armazena o estado de fato, apenas a história completa dos fatos&lt;/strong&gt;. Por isso, o ponto crítico da construção dessas soluções está em &lt;strong&gt;garantir ordenação e atomicidade&lt;/strong&gt;, para que seja possível &lt;strong&gt;reconstruir a entidade reaplicando os eventos em sequência.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/event-store.drawio.png&quot; alt=&quot;Event Store&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ao reaplicar os três eventos da transação &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;432&lt;/code&gt;, o estado é reconstituído totalmente e de forma fiel&lt;/strong&gt;, resultando no estado &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pago&lt;/code&gt;, com &lt;strong&gt;dois produtos adicionados ao cliente &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esse modelo é análogo ao &lt;strong&gt;append-only log&lt;/strong&gt;, usado por sistemas como &lt;strong&gt;Kafka&lt;/strong&gt; ou bancos contábeis — &lt;strong&gt;os dados nunca são substituídos, apenas acumulados.&lt;/strong&gt; Por isso, &lt;strong&gt;é comparado a um ledger distribuído: um registro permanente, auditável e verificável ao longo do tempo de tudo o que aconteceu dentro de um domínio.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/event-store-ledger.drawio.png&quot; alt=&quot;Event Store Ledger&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Modelar o Event Store de forma agnóstica em relação ao tipo de operação efetuada&lt;/strong&gt; é um requisito obrigatório. Isso envolve &lt;strong&gt;utilizar campos livres ou blobs para armazenar dados e metadados do evento&lt;/strong&gt; com fins de replicação e reprocessamento, além de &lt;strong&gt;empregar índices para otimização de consultas transacionais e recuperação de estados históricos.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Não é necessário utilizar bancos relacionais ou não relacionais para projetar Event Stores, embora isso seja o mais indicado. É possível utilizar opções como EventStoreDB e Apache Kafka para tais finalidades, considerando seus trade-offs de flexibilidade na gestão dos dados.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;event-bus-e-publishers&quot;&gt;Event-Bus e Publishers&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Dentro — e fora — de uma arquitetura de Event Sourcing, o Event Bus é o componente responsável por permitir que os eventos gerados dentro de um domínio sejam publicados e propagados para outros domínios, sistemas e subsistemas interessados nos acontecimentos e nas mudanças de estado de suas entidades.&lt;/strong&gt;&lt;br&gt;
Seu objetivo é &lt;strong&gt;carregar esses eventos de forma desacoplada&lt;/strong&gt; até os consumidores do sistema.&lt;br&gt;
&lt;strong&gt;O Event Store é o registro de verdade — a &lt;em&gt;golden source&lt;/em&gt; dos eventos —, enquanto o Event Bus é o meio de projeção das consequências desses eventos.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/event-bus.drawio.png&quot; alt=&quot;Event Bus&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Os publishers são componentes de um sistema baseado em Event Sourcing responsáveis por publicar os eventos confirmados no Event Store em tópicos, filas ou barramentos.&lt;/strong&gt;&lt;br&gt;
Esse comportamento de publicação &lt;strong&gt;deve ser atômico&lt;/strong&gt;, e os eventos &lt;strong&gt;só podem ser emitidos no Event Bus quando a gravação e outras operações forem bem-sucedidas.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;O Event Bus pode ser implementado sobre tecnologias como Kafka, RabbitMQ, SQS, NATS ou Pulsar&lt;/strong&gt;, dependendo do SLA e das garantias necessárias.&lt;/p&gt;

&lt;p&gt;Embora &lt;strong&gt;não sejam componentes obrigatórios&lt;/strong&gt; em uma arquitetura de Event Sourcing, &lt;strong&gt;o Event Bus e o Event Store são grandes facilitadores em implementações de microserviços orientados a eventos.&lt;/strong&gt;&lt;br&gt;
De qualquer forma, &lt;strong&gt;um Event Bus deve preservar a ordenação dos eventos por &lt;em&gt;stream&lt;/em&gt; ou &lt;em&gt;aggregate&lt;/em&gt;&lt;/strong&gt; e &lt;strong&gt;garantir que o evento seja entregue pelo menos uma vez&lt;/strong&gt;, com &lt;strong&gt;deduplicação&lt;/strong&gt; para evitar repetições não intencionais e &lt;strong&gt;idempotência no nível dos consumidores&lt;/strong&gt; para permitir reprocessamentos seguros.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Um sistema baseado em Event Sourcing pode possuir múltiplos barramentos de &lt;em&gt;service bus&lt;/em&gt;&lt;/strong&gt;, responsáveis por &lt;strong&gt;registrar e transmitir eventos de domínio para consumidores específicos&lt;/strong&gt;, com &lt;strong&gt;ações distintas em diferentes domínios.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/event-bus-conta.drawio.png&quot; alt=&quot;Event Bus Conta Confirmada&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Um Event Bus com características de ledger distribuído&lt;/strong&gt;, responsável por registrar de forma histórica todas as ações efetuadas dentro de contas bancárias ou livros-caixa, &lt;strong&gt;pode emitir eventos como “Nova Conta Registrada” para domínios que precisam persistir previamente uma estrutura base de conta antes de começar a consumir o evento central&lt;/strong&gt;, como, por exemplo, &lt;strong&gt;uma transação&lt;/strong&gt;, &lt;strong&gt;um saldo (&lt;em&gt;Balance&lt;/em&gt;) ou um extrato (&lt;em&gt;Statement&lt;/em&gt;)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Assim que forem emitidos eventos dentro do Event Sourcing responsável por registrar as transações, essas &lt;strong&gt;mensagens de transações persistidas são transmitidas para outro barramento de Event Bus&lt;/strong&gt;, encarregado de &lt;strong&gt;notificar os domínios de que esses eventos ocorreram&lt;/strong&gt;, permitindo &lt;strong&gt;compor o saldo e registrar de forma histórica os eventos de extrato.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/event-bus-transacao.drawio.png&quot; alt=&quot;Event Bus Transação Confirmada&quot;&gt;&lt;/p&gt;

&lt;p&gt;Dessa forma, &lt;strong&gt;conseguimos notificar e recompor entidades inteiras dentro de domínios&lt;/strong&gt; que aplicam suas próprias características de Event Sourcing ou persistência transacional, &lt;strong&gt;mantendo arquiteturas orientadas a eventos de forma eventualmente consistente.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;projections-e-modelos-de-leitura&quot;&gt;Projections e Modelos de Leitura&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Os Event Stores em sistemas baseados em Event Sourcing são otimizados para grandes volumes de escrita&lt;/strong&gt;, porém podem apresentar desafios de leitura e recuperação de dados. &lt;strong&gt;Os bancos de dados principais devem conter apenas os logs dos fatos.&lt;/strong&gt;&lt;br&gt;
Para criar consultas sistêmicas e alimentar APIs ou outros processos, &lt;strong&gt;precisamos construir modelos otimizados para leitura.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eventos, por definição, são ações que ocorreram no passado.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Projections são componentes ou processos utilizados para interpretar esses fatos e transformá-los em algo utilizável sistemicamente, em termos de leitura.&lt;/strong&gt;&lt;br&gt;
Uma projection é &lt;strong&gt;a consolidação de vários eventos de um mesmo identificador ou entidade&lt;/strong&gt; que, após interpretados, resultam em um &lt;strong&gt;modelo de leitura (Read Model)&lt;/strong&gt; armazenado para consultas otimizadas.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/projection.drawio.png&quot; alt=&quot;Projections&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Em outras palavras, as projections são processos que “ouvem” os eventos do Event Store e atualizam uma visão derivada em um formato otimizado para leitura, seja do próprio sistema ou de outros.&lt;/strong&gt;&lt;br&gt;
Esses modelos são conhecidos como &lt;strong&gt;Modelos de Leitura (Read Models)&lt;/strong&gt; e &lt;strong&gt;podem, sim, ser construídos sob uma visão de &lt;em&gt;State Mutation&lt;/em&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/read-models.drawio.png&quot; alt=&quot;Read Models&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Projections são normalmente construídas com base no padrão CQRS (&lt;em&gt;Command-Query Responsibility Segregation&lt;/em&gt;)&lt;/strong&gt;, no qual &lt;strong&gt;se porta, de forma síncrona ou assíncrona, um modelo otimizado para escrita para outro modelo otimizado para leitura.&lt;/strong&gt;&lt;br&gt;
Nos Read Models, podemos utilizar &lt;strong&gt;bancos de dados em memória&lt;/strong&gt; para respostas rápidas, &lt;strong&gt;bancos orientados a documentos&lt;/strong&gt; para buscas textuais ou ainda &lt;strong&gt;modelos relacionais e não relacionais&lt;/strong&gt; para relatórios consolidados.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read Models não são apenas caches de leitura — são representações materializadas e derivadas de fatos históricos ocorridos e registrados no Event Store.&lt;/strong&gt;&lt;br&gt;
Isso significa que &lt;strong&gt;eles devem evoluir junto com o domínio e com a semântica dos eventos, operando em tempo próximo do real.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ao contrário do Event Sourcing, as projections são determinísticas em relação ao estado atual.&lt;/strong&gt;&lt;br&gt;
Os processos de &lt;em&gt;replay&lt;/em&gt; dos eventos — em caso de reprocessamento temporal para recomposição de estados — &lt;strong&gt;devem refletir também nas projections&lt;/strong&gt;, garantindo que elas &lt;strong&gt;representem o estado atual do sistema.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Em sistemas maiores, múltiplas projections coexistem, cada uma representando uma visão específica&lt;/strong&gt;: analytics, relatórios, dashboards, filas de envio, catálogos etc.&lt;br&gt;
&lt;strong&gt;Seguindo boas práticas de reprocessamento e elasticidade inerentes ao domínio principal, as Read Models distribuídas tornam-se efêmeras e descartáveis&lt;/strong&gt;, podendo &lt;strong&gt;ser reconstituídas a qualquer momento.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;projections-e-read-models-transacionais&quot;&gt;Projections e Read Models Transacionais&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Dentro de um modelo transacional, podemos agrupar pequenas projections dentro do mesmo banco de dados do Event Store de forma atômica.&lt;/strong&gt;&lt;br&gt;
Um Event Store &lt;strong&gt;não é otimizado para leitura — é otimizado para escrita intensiva.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Em processos que exigem alta carga de trabalho e grandes volumes de dados&lt;/strong&gt;, uma quantidade maior de operações dentro de uma única transação do Event Store &lt;strong&gt;pode gerar gargalos e demandar escalabilidade vertical das aplicações e bancos de dados.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/read-model-transacional.drawio.png&quot; alt=&quot;Transação&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nesse modelo, a prioridade é preservar atomicidade e consistência imediata.&lt;/strong&gt;&lt;br&gt;
Isso significa que, &lt;strong&gt;dentro de uma única transação, tanto o evento quanto a projeção derivada são persistidos de forma atômica.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;O maior benefício desse modelo é a eliminação da latência entre escrita e leitura&lt;/strong&gt;, permitindo &lt;strong&gt;consistência imediata em valores que não toleram divergência em nenhum estado.&lt;/strong&gt;&lt;br&gt;
Por outro lado, &lt;strong&gt;ele adiciona complexidade operacional ao Event Sourcing e aumenta a carga de operações sobre o Event Store&lt;/strong&gt;, tornando-se um possível gargalo em cenários de alta volumetria.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Em contextos de grande volume, é comum aplicar o padrão “Transactional Outbox” como mecanismo mitigador.&lt;/strong&gt;&lt;br&gt;
Nesse padrão, &lt;strong&gt;o evento é escrito junto da projeção dentro da mesma transação, mas publicado posteriormente de forma assíncrona — garantindo atomicidade sem bloquear o throughput e criando uma ponte para o modelo semissíncrono.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;projections-e-read-models-semissíncronos&quot;&gt;Projections e Read Models Semissíncronos&lt;/h3&gt;

&lt;p&gt;O propósito inicial de um Event Sourcing é &lt;strong&gt;gerar uma fonte segura e confiável de dados transacionais&lt;/strong&gt;, que &lt;strong&gt;possam ser reconstituídos e replicados.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;No modelo transacional, como visto anteriormente, mesmo que algumas Read Models sejam construídas dentro do próprio Event Store de forma atômica, idealmente elas devem ser encaminhadas para aplicações responsáveis por tratar e otimizar esses dados para leitura&lt;/strong&gt;, lidando com os dados transacionais apenas para &lt;strong&gt;atualização e reconstrução das projections.&lt;/strong&gt;&lt;br&gt;
Em outras palavras, &lt;strong&gt;é necessário reduzir qualquer outra operação que possa comprometer a capacidade dedicada à escrita e à confiabilidade.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nesses casos, podemos aproveitar a afinidade transacional do Event Store para tratá-lo como uma “golden source atômica”&lt;/strong&gt;, atualizando &lt;strong&gt;as Read Models de forma assíncrona e eventual.&lt;/strong&gt;&lt;br&gt;
Dessa forma, &lt;strong&gt;mantemos duas fontes do mesmo dado&lt;/strong&gt; — uma voltada exclusivamente para &lt;strong&gt;persistência e confiabilidade&lt;/strong&gt;, e outra &lt;strong&gt;otimizada para consulta&lt;/strong&gt;, &lt;strong&gt;modelo ideal para grandes volumes de dados.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/semi-sync-read-model.drawio.png&quot; alt=&quot;Golden Source&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operações de saldo precisam ser executadas de forma atômica e transacional para evitar inconsistências.&lt;/strong&gt;&lt;br&gt;
Devemos garantir &lt;strong&gt;exclusão mútua&lt;/strong&gt; e lidar com &lt;strong&gt;diversas operações por meio de transações&lt;/strong&gt;, assegurando que todos os lançamentos e movimentações sejam processados corretamente para se chegar ao saldo atual.&lt;br&gt;
&lt;strong&gt;Essas operações podem ser executadas dentro de um Event Store.&lt;/strong&gt;&lt;br&gt;
Após cada transação, &lt;strong&gt;o novo saldo é calculado de forma atômica e publicado no Event Bus&lt;/strong&gt;, onde &lt;strong&gt;pode ser consumido por um Read Model otimizado para consulta e exposição em cenários de alto volume de requisições.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Assim, &lt;strong&gt;o Event Store atua como a “fonte de verdade” e o Read Model como o “estado derivado seguro”.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Esse modelo deve ser adotado apenas quando é possível lidar com otimismo entre os níveis de consistência.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;projections-e-read-models-assíncronos&quot;&gt;Projections e Read Models Assíncronos&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Em sistemas que toleram consistência eventual, podemos encaminhar os dados registrados no Event Sourcing via Event Bus para a construção de Read Models diretamente nos domínios interessados&lt;/strong&gt;, removendo assim &lt;strong&gt;qualquer complexidade adicional do Event Store.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/read-model-async.drawio.png&quot; alt=&quot;Async&quot;&gt;&lt;/p&gt;

&lt;p&gt;Dessa forma, &lt;strong&gt;a capacidade do Event Store permanece dedicada exclusivamente a registrar, confirmar e repassar os logs temporais&lt;/strong&gt;, garantindo uma sequencialidade atômica.&lt;br&gt;
&lt;strong&gt;Todos os modelos de leitura são construídos e processados de forma totalmente desacoplada do Event Store&lt;/strong&gt;, porém assumimos que há um aumento computacional significativo em cada processo de reconstrução, sendo necessário o envio completo dos logs para reconstituição.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eliminamos a complexidade e a demanda computacional do motor de eventos&lt;/strong&gt;, &lt;strong&gt;transferindo-as para cada aplicação e domínio&lt;/strong&gt; responsáveis por tratar os dados de forma agnóstica.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;reconstituição-de-estados-e-rehydration&quot;&gt;Reconstituição de Estados e Rehydration&lt;/h1&gt;

&lt;p&gt;A reconstituição de estado de um agregado dentro do Event Sourcing, popularmente conhecida como &lt;em&gt;Rehydration&lt;/em&gt;, é o &lt;strong&gt;processo pelo qual utilizamos os logs sequenciais registrados no Event Store para reconstruir o estado de entidades e operações dentro e fora do domínio principal.&lt;/strong&gt;&lt;br&gt;
Um Event Store deve, idealmente, &lt;strong&gt;possuir ferramentas que permitam o reprocessamento sequencial de todos os registros, reaplicando os eventos associados a cada entidade.&lt;/strong&gt; Esse processo é central ao Event Sourcing e permite que a &lt;strong&gt;história contada pelos logs seja novamente reconstituída.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/rehydratation.drawio.png&quot; alt=&quot;Rehydration&quot;&gt;&lt;/p&gt;

&lt;p&gt;No cenário hipotético de um Event Store que registra todas as transações de crédito e débito e publica esses eventos confirmados para outros domínios, como saldo ou extrato do cliente — que disponibilizam Read Models sumarizados dessas informações —, imagine que &lt;strong&gt;um desses domínios sofra algum grau de inconsistência sistêmica ou manual, perdendo total ou parcialmente os dados e comprometendo a integridade das informações.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nossa aplicação de Event Sourcing deve oferecer mecanismos para &lt;strong&gt;reaplicar todos os eventos e reenviá-los sequencialmente ao Event Bus&lt;/strong&gt;, permitindo que os domínios subsequentes se &lt;strong&gt;reconstituam a partir dessas informações temporais&lt;/strong&gt;, recalculando o saldo atual ou reconstruindo as visualizações de lançamentos.&lt;/p&gt;

&lt;p&gt;Essa estratégia é especialmente útil em domínios complexos que &lt;strong&gt;exigem rastreabilidade e reconstituições auditáveis&lt;/strong&gt;, como cadeias farmacêuticas (rastreio de medicamentos), linhas de fabricação, aplicação de descontos, prontuários médicos e históricos de pacientes ou processos de fechamento contábil.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;snapshotting&quot;&gt;Snapshotting&lt;/h2&gt;

&lt;p&gt;O modelo transacional propõe que &lt;strong&gt;todas as alterações e operações de estado sejam armazenadas para que esses dados possam ser auditados e recompostos ao longo do tempo.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Em um exemplo transacional de uma conta bancária, &lt;strong&gt;podemos saber pontualmente o saldo atual da conta, mas perdemos a trilha de eventos que levaram até esse estado.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Depósitos, saques, transferências e estornos, em conjunto, constroem o estado atual do saldo.&lt;/strong&gt;&lt;br&gt;
Em domínios onde &lt;strong&gt;auditabilidade, rastreabilidade ou causalidade são importantes&lt;/strong&gt;, a ausência desse histórico representa &lt;strong&gt;um problema significativo.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No entanto, &lt;strong&gt;reconstruir o estado completo pode se tornar computacionalmente caro com o crescimento da base de eventos.&lt;/strong&gt;&lt;br&gt;
É nesse ponto que surge o conceito de &lt;strong&gt;Snapshotting.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Snapshotting é uma técnica de otimização que cria “pontos de restauração” intermediários do estado&lt;/strong&gt;, como “fotografias” que permitem reconstruí-lo de forma incremental, &lt;strong&gt;sem precisar recalcular todas as transações a cada operação.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/event-sourcing-snapshot.png&quot; alt=&quot;Snapshotting&quot;&gt;&lt;/p&gt;

&lt;p&gt;Um snapshot representa o &lt;strong&gt;estado de um agregado ou entidade em um determinado ponto no tempo&lt;/strong&gt;, acompanhado de um &lt;strong&gt;índice do último evento aplicado para gerar aquele estado.&lt;/strong&gt; Assim, caso seja necessário &lt;strong&gt;“reidratar” o estado&lt;/strong&gt;, o sistema, em vez de processar todo o histórico do início ao fim, &lt;strong&gt;pode iniciar o processamento apenas a partir dos eventos ocorridos depois dele.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Por exemplo: &lt;strong&gt;a entidade “Saldo”, dentro do agregado “Conta”, pode possuir 1.000.000 de eventos históricos de lançamentos e movimentações.&lt;/strong&gt;&lt;br&gt;
Para recalcular o saldo, em vez de processar todos os eventos dispersos no banco de dados, &lt;strong&gt;o sistema pode gerar um snapshot a cada 10.000 eventos, contendo o saldo consolidado a partir do último evento.&lt;/strong&gt; Para reconstruir o estado atual, basta carregar o último snapshot e aplicar os eventos posteriores a ele, reduzindo de forma considerável o tempo e o custo computacional de leitura.&lt;/p&gt;

&lt;p&gt;No entanto, &lt;strong&gt;snapshots devem ser tratados como artefatos derivados e descartáveis, não como fonte primária de verdade.&lt;/strong&gt; O &lt;strong&gt;Event Store&lt;/strong&gt; continua sendo o &lt;strong&gt;“single source of truth”&lt;/strong&gt;, e os snapshots são &lt;strong&gt;mecanismos auxiliares de performance pontual para a operação.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;versionamento-e-garantias-de-ordem-em-consistência-eventual-last-write-wins&quot;&gt;Versionamento e Garantias de Ordem em Consistência Eventual (Last-Write-Wins)&lt;/h1&gt;

&lt;p&gt;Quando existe a necessidade de reidratar um, alguns ou todos os agregados, &lt;strong&gt;precisamos garantir que os domínios consumidores desses eventos atendam a certos critérios para que o processo ocorra da melhor forma possível&lt;/strong&gt;, assegurando &lt;strong&gt;um resultado final consistente das operações.&lt;/strong&gt;&lt;br&gt;
Dentro do Event Sourcing, o Event Store deve &lt;strong&gt;garantir a ordenação local dos eventos de um mesmo agregado&lt;/strong&gt;, ou seja, &lt;strong&gt;todos os eventos relacionados à mesma entidade precisam ser aplicados na sequência temporal em que ocorreram.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Essa ordenação local é o que permite reconstruir estados de forma determinística.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Quando falamos de Event Bus, &lt;strong&gt;o Event Store pode garantir a publicação dos eventos à medida que ocorrem&lt;/strong&gt;, porém &lt;strong&gt;a ordem em que serão consumidos não é globalmente garantida por padrão.&lt;/strong&gt;&lt;br&gt;
Isso significa que &lt;strong&gt;eventos publicados em ordem podem chegar fora de ordem em réplicas distintas ou sistemas diferentes&lt;/strong&gt;, sofrendo &lt;strong&gt;variações de tempo de processamento até a devida atualização de estado.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Em arquiteturas event-driven, isso não é uma falha — é o comportamento esperado da consistência eventual.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/event-source-race-condition.png&quot; alt=&quot;Event Store Race Condition&quot;&gt;&lt;/p&gt;

&lt;p&gt;Em uma operação de saldo, &lt;strong&gt;podemos ter várias transações atualizando o saldo de um cliente em um curto intervalo de tempo&lt;/strong&gt;, mas &lt;strong&gt;todas são inseridas com característica temporal e atômica no Event Store&lt;/strong&gt; e &lt;strong&gt;publicadas sequencialmente no Event Bus.&lt;/strong&gt;&lt;br&gt;
Porém, &lt;strong&gt;a ordem de consumo e processamento nos clientes finais pode ocorrer de forma paralela e desordenada&lt;/strong&gt;, o que pode, por exemplo, &lt;strong&gt;gerar uma Read Model final incorreta ao processar eventos mais novos antes de eventos antigos.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nesse cenário, &lt;strong&gt;o modelo &lt;em&gt;Last-Write-Wins (LWW)&lt;/em&gt; é uma forma simples de lidar com conflitos de escrita ou reprocessamentos duplicados.&lt;/strong&gt;&lt;br&gt;
Ele define que, &lt;strong&gt;em caso de eventos concorrentes para o mesmo agregado, o último evento válido (por &lt;em&gt;timestamp&lt;/em&gt; ou &lt;em&gt;version&lt;/em&gt;) deve prevalecer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/lww-version.drawio.png&quot; alt=&quot;LWW&quot;&gt;&lt;/p&gt;

&lt;p&gt;Em eventos e sinais produzidos por arquiteturas baseadas em Event Sourcing, cada evento deve possuir um &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; único e uma &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;version&lt;/code&gt; incremental, que identificam &lt;strong&gt;a versão do evento a ser comparada.&lt;/strong&gt;&lt;br&gt;
Isso &lt;strong&gt;evita duplicações em sistemas subjacentes e permite evoluir o stream de eventos com segurança.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Esse processo também pode ser conduzido com timestamps Unix&lt;/strong&gt;, indicando &lt;strong&gt;a ordem temporal direta.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Os sistemas que consomem eventos produzidos no Event Bus devem realizar checagens constantes da versão do evento em relação ao estado atual persistido&lt;/strong&gt;, para &lt;strong&gt;evitar sobrescritas indevidas.&lt;/strong&gt;&lt;br&gt;
Essas verificações podem ser realizadas &lt;strong&gt;de forma transacional, com condicionais em nível de código&lt;/strong&gt;, ou por meio de &lt;strong&gt;escritas condicionais em bancos de dados que suportem esse tipo de operação.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;idempotência-em-domínios-complexos&quot;&gt;Idempotência em Domínios Complexos&lt;/h1&gt;

&lt;p&gt;A &lt;strong&gt;idempotência&lt;/strong&gt; é a propriedade que permite que uma operação seja executada múltiplas vezes &lt;strong&gt;sem alterar o resultado final.&lt;/strong&gt;&lt;br&gt;
Em sistemas centralizados, isso pode ser garantido por meio de &lt;strong&gt;transações ACID&lt;/strong&gt;.&lt;br&gt;
Mas em &lt;strong&gt;arquiteturas distribuídas&lt;/strong&gt;, onde eventos são propagados de forma assíncrona e cada serviço mantém sua própria consistência, &lt;strong&gt;a idempotência precisa ser explicitamente e cuidadosamente projetada.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Em sistemas distribuídos baseados em eventos, ou em arquiteturas assíncronas em geral, &lt;strong&gt;a idempotência é um requisito fundamental que permite operar arquiteturas complexas de forma segura.&lt;/strong&gt;&lt;br&gt;
Isso se deve ao fato de que &lt;strong&gt;a entrega e o processamento de eventos são inerentemente inconstantes e não determinísticos&lt;/strong&gt;, podendo &lt;strong&gt;ocorrer em duplicidade&lt;/strong&gt;, sofrer &lt;strong&gt;race conditions ocasionais&lt;/strong&gt; ou &lt;strong&gt;falhar durante a execução e precisar ser reiniciados&lt;/strong&gt;, o que &lt;strong&gt;reforça a necessidade de evitar esforço computacional redundante.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Em &lt;strong&gt;arquiteturas baseadas em Event Sourcing&lt;/strong&gt;, &lt;strong&gt;podemos decidir reprocessar todos os eventos de um período específico para recompor projeções e notificações para sistemas subjacentes de forma histórica.&lt;/strong&gt;&lt;br&gt;
Para que esse processo ocorra corretamente &lt;strong&gt;tanto dentro do domínio quanto nos domínios adjacentes&lt;/strong&gt;, é necessário &lt;strong&gt;garantir processos de idempotência distribuída e controle de versão dos eventos&lt;/strong&gt;, assegurando que &lt;strong&gt;eventos já processados não gerem efeitos colaterais ou resultados inconsistentes.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Todos os domínios downstream devem realizar checagens e manter chaves de idempotência fortes e consistentes a todo momento.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;referências&quot;&gt;Referências&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/msfidelis/event-source-distributed-ledger&quot;&gt;Github: Event Source Distributed Ledger&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://martinfowler.com/eaaDev/EventSourcing.html&quot;&gt;Event Sourcing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/event-sourcing.html&quot;&gt;Event sourcing pattern&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@rvf.vazquez/eventsourcing-e-eventstore-proje%C3%A7%C3%B5es-snapshots-97b964a220d&quot;&gt;Eventsourcing e EventStore, Projeções, Snapshots&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Event_store&quot;&gt;Event store&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://israelaece.com/2016/04/28/explorando-o-eventstore-overview/&quot;&gt;Explorando o EventStore – Overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.axoniq.io/axon-framework-reference/4.11/events/infrastructure/&quot;&gt;Event Bus &amp;amp; Event Store&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://leapcell.medium.com/how-to-create-a-event-bus-in-go-d7919b59a584&quot;&gt;How to Create a Event Bus in Go&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/architecture/microservices/multi-container-microservice-net-applications/integration-event-based-microservice-communications&quot;&gt;Implementing event-based communication between microservices (integration events)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://event-driven.io/en/projections_and_read_models_in_event_driven_architecture/&quot;&gt;Guide to Projections and Read Models in Event-Driven Architecture&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 21 Nov 2025 00:00:00 +0000</pubDate>
        <link>https://fidelissauro.dev/event-sourcing/</link>
        <guid isPermaLink="true">https://fidelissauro.dev/event-sourcing/</guid>
        
        
        <category>system-design</category>
        
        <category>engineering</category>
        
        <category>cloud</category>
        
      </item>
    
      <item>
        <title>Blueprint - Experimento de Warmup Progressivo com Istio e Argo Rollouts</title>
        <description>&lt;p&gt;O gerenciamento de tráfego sincrono durante rollouts de aplicações em Kubernetes pode oferecer algum tipo de ruido operacional e refletir na experiência dos clientes, especialmente quando lidamos com aplicações que necessitam de um período de “warm up” antes de atingir sua performance plena.&lt;/p&gt;

&lt;p&gt;Aplicações baseadas em JVM, como Java, Scala e Kotlin, tipicamente enfrentam problemas de performance durante os primeiros momentos de execução. Durante a inicialização, a JVM executa o bytecode no interpretador, consumindo recursos significativos de CPU e resultando em tempos de resposta elevados durante os primeiros momentos do ciclo de vida da aplicação.&lt;/p&gt;

&lt;p&gt;Para amenizar esses casos, podemos utilizar o &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;warmUp&lt;/code&gt; do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Istio Service Mesh&lt;/code&gt; para garantir um período de aquecimento seguro para os pods novos que forem sendo criados durante os rollouts.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;warm-up-configuration-no-istio&quot;&gt;Warm Up Configuration no Istio&lt;/h2&gt;

&lt;p&gt;O Istio implementa uma funcionalidade de &lt;strong&gt;warm-up&lt;/strong&gt; através de configurações no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DestinationRule&lt;/code&gt; que permite controlar a distribuição gradual de tráfego para novos hosts com base em distribuição linear.&lt;/p&gt;

&lt;h3 id=&quot;parâmetros-principais&quot;&gt;Parâmetros Principais&lt;/h3&gt;

&lt;p&gt;A configuração de warm-up no Istio utiliza três parâmetros principais:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;minimumPercent&lt;/code&gt;&lt;/strong&gt;: Define a porcentagem mínima de tráfego que uma nova instância receberá inicialmente&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aggression&lt;/code&gt;&lt;/strong&gt;: Controla a velocidade do aumento de tráfego (padrão: 1.0 para crescimento linear)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;duration&lt;/code&gt;&lt;/strong&gt;: Período de duração do aquecimento. Quanto tempo até a progessão linear irá levar pra chegar em 100%.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;exemplos-coletados&quot;&gt;Exemplos coletados&lt;/h2&gt;

&lt;p&gt;No exemplo utilizamos como apoio para rollout progressivo o Argo Rollouts com estratégia de Canary Releases, para realizar uma estratégia de progressão baseada em tempo que deve ser completada em 5 minutos, com pausas de 60s a cada step.&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;    &lt;span class=&quot;na&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;canary&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;setWeight&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;60s&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;setWeight&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;60s&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;setWeight&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;60s&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;setWeight&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;60&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;60s&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;setWeight&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;60s&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;setWeight&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;experiência-de-rollout-sem-warm-up&quot;&gt;Experiência de Rollout Sem Warm Up&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/no-warm-up.png&quot; alt=&quot;no-warm-up&quot;&gt;&lt;/p&gt;

&lt;p&gt;Sem a configuração de warm-up, novos pods recebem imediatamente uma distribuição proporcional do tráfego total. Para um deployment com 6 replicas, um novo pod receberia instantaneamente 16% do tráfego, potencialmente causando, picos de latência, timeouts durante a inicialização, degradação da experiência e burn rate dos SLO’s.&lt;/p&gt;

&lt;h3 id=&quot;experiência-de-rollout-com-warm-up&quot;&gt;Experiência de Rollout com Warm Up&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/warm-up.png&quot; alt=&quot;warm up&quot;&gt;&lt;/p&gt;

&lt;p&gt;A experiência com Warm Up foi configurado da seguinte forma:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Algoritmo de Balanceamento&lt;/strong&gt;: Foi utilizado o algoritmo de Round Robin do Envoy pelas limitações da aplicabilidade do warm up em demais algoritmos&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Inicialização&lt;/strong&gt;: Novo pod recebe apenas 3% do tráfego que deveria receber perante a distribuição padrão&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Crescimento Gradual&lt;/strong&gt;: Tráfego aumenta linearmente, até atingir seu maximo em 5 minutos. Após o período de warm-up de 5 minutos, o pod recebe distribuição normal&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Agression&lt;/strong&gt;: Iremos trabalhar com crescimento de tráfego linear para os novos pods. Valores maiores que 1.0 aceleram o crescimento de tráfego de forma não-linear, enquanto 1.0 mantém linear.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;networking.istio.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;DestinationRule&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;app-warmup&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;production&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myapp.production.svc.cluster.local&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;trafficPolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;loadBalancer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;simple&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ROUND_ROBIN&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;warmup&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;minimumPercent&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3.0&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;5m&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;aggression&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.0&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;limitações-encontrados&quot;&gt;Limitações Encontrados&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Suporte a Load Balancers&lt;/strong&gt;: Funciona apenas com &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ROUND_ROBIN&lt;/code&gt; e &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LEAST_REQUEST&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Efetividade em Deployments&lt;/strong&gt;: Menos efetivo quando todos os endpoints são novos simultaneamente&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Escala Mínima&lt;/strong&gt;: Mais efetivo quando poucos pods novos são criados por vez&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Thu, 02 Oct 2025 00:00:00 +0000</pubDate>
        <link>https://fidelissauro.dev/istio-warm-up/</link>
        <guid isPermaLink="true">https://fidelissauro.dev/istio-warm-up/</guid>
        
        
        <category>istio</category>
        
        <category>kubernetes</category>
        
        <category>load-balancing</category>
        
        <category>warm-up</category>
        
        <category>rollout</category>
        
        <category>service-mesh</category>
        
      </item>
    
      <item>
        <title>Msc. Field Notes - Shard Router</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;Este artigo faz parte de uma organização de um material bruto excedente da minha tese de mestrado. Tem o objetivo de compilar as referencias tecnicas e experimentações práticas.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Este compilado foca no desenvolvimento e análise de um &lt;strong&gt;roteador baseado em dar suporte em celulas ou shards&lt;/strong&gt; de bu;lheads, componente inicial para implementações arquitetura celular.&lt;/p&gt;

&lt;p&gt;O projeto se baseia na aplicação simples e conceitual de padrões de arquitetura de roteamento &lt;strong&gt;celular&lt;/strong&gt; ou &lt;strong&gt;bulkheads&lt;/strong&gt;, que implementa &lt;strong&gt;roteamento determinístico baseado em hashing consistente&lt;/strong&gt;. O roteador celular atua como um proxy reverso especializado que direciona requisições de clientes para células (shards) específicas, garantindo que requisições de um mesmo cliente sejam sempre processadas pela mesma célula ou shard.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Hashing Consistente&lt;/strong&gt;: Para distribuição uniforme e estável de requisições&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Roteamento Determinístico&lt;/strong&gt;: Garantindo que clientes sejam sempre direcionados à mesma célula&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Isolamento de Falhas&lt;/strong&gt;: Através de bulkheads implementados a nível de roteamento&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Observabilidade Granular&lt;/strong&gt;: Com métricas específicas por célula e algoritmo de hash&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;fundamentação-teórica&quot;&gt;Fundamentação Teórica&lt;/h2&gt;

&lt;h3 id=&quot;sharding-e-particionamento-horizontal&quot;&gt;Sharding e Particionamento Horizontal&lt;/h3&gt;

&lt;p&gt;O sharding, ou particionamento horizontal, é uma técnica consolidada para distribuição de dados e processamento em sistemas distribuídos (Özsu &amp;amp; Valduriez, 2020). Diferentemente do particionamento vertical, que divide dados por colunas ou atributos, o sharding divide o conjunto de dados em partições horizontais baseadas em critérios específicos, como ranges de valores ou funções de hash. Esse conceito é diretamente associado a particionamento de dados fisicamente em bancos de dados, mas não se limita a eles. Iremos seguir aqui pra frente como um critério de segmentação total de infraestrutura, cliente e demais recursos.&lt;/p&gt;

&lt;div class=&quot;mermaid&quot;&gt;
graph TD
    subgraph &quot;Particionamento Vertical&quot;
        PV1[Tabela Original]
        PV2[Colunas A, B] 
        PV3[Colunas C, D]
        PV4[Colunas E, F]
        
        PV1 --&amp;gt; PV2
        PV1 --&amp;gt; PV3
        PV1 --&amp;gt; PV4
    end
&lt;/div&gt;

&lt;div class=&quot;mermaid&quot;&gt;
graph TD    
    subgraph &quot;Sharding&quot;
        PH1[Dataset Completo]
        PH2[Shard A&lt;br&gt;Users 1-1000]
        PH3[Shard B&lt;br&gt;Users 1001-2000]
        PH4[Shard C&lt;br&gt;Users 2001-3000]
        
        PH1 --&amp;gt; PH2
        PH1 --&amp;gt; PH3
        PH1 --&amp;gt; PH4
    end
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;análise-de-blast-radius-e-disponibilidade-sistêmica&quot;&gt;Análise de Blast Radius e Disponibilidade Sistêmica&lt;/h2&gt;

&lt;h3 id=&quot;conceito-de-blast-radius-em-arquiteturas-distribuídas&quot;&gt;Conceito de Blast Radius em Arquiteturas Distribuídas&lt;/h3&gt;

&lt;p&gt;O &lt;strong&gt;blast radius&lt;/strong&gt; (raio de explosão) representa o escopo de impacto de uma falha em sistemas distribuídos. Em arquiteturas celulares, o blast radius é diretamente proporcional ao número de células distribuídas, oferecendo uma relação matemática clara entre disponibilidade e granularidade de distribuição.&lt;/p&gt;

&lt;p&gt;A fórmula fundamental para cálculo de disponibilidade em caso de falha de uma célula é:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;Disponibilidade = ((N - F) / N) × 100%

Onde:
- N = Número total de células/shards
- F = Número de células falhando
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;impacto-da-granularidade-na-disponibilidade&quot;&gt;Impacto da Granularidade na Disponibilidade&lt;/h3&gt;

&lt;p&gt;A análise quantitativa demonstra como o aumento do número de células reduz exponencialmente o blast radius:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Relação Blast Radius vs. Número de Células&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Número de Células&lt;/th&gt;
      &lt;th&gt;Falhas (1 célula)&lt;/th&gt;
      &lt;th&gt;Disponibilidade&lt;/th&gt;
      &lt;th&gt;Blast Radius&lt;/th&gt;
      &lt;th&gt;Clientes Afetados&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;66.7%&lt;/td&gt;
      &lt;td&gt;33.3%&lt;/td&gt;
      &lt;td&gt;1/3 da base&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;80.0%&lt;/td&gt;
      &lt;td&gt;20.0%&lt;/td&gt;
      &lt;td&gt;1/5 da base&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;10&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;90.0%&lt;/td&gt;
      &lt;td&gt;10.0%&lt;/td&gt;
      &lt;td&gt;1/10 da base&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;25&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;96.0%&lt;/td&gt;
      &lt;td&gt;4.0%&lt;/td&gt;
      &lt;td&gt;1/25 da base&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;50&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;98.0%&lt;/td&gt;
      &lt;td&gt;2.0%&lt;/td&gt;
      &lt;td&gt;1/50 da base&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;100&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;99.0%&lt;/td&gt;
      &lt;td&gt;1.0%&lt;/td&gt;
      &lt;td&gt;1/100 da base&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1000&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;99.9%&lt;/td&gt;
      &lt;td&gt;0.1%&lt;/td&gt;
      &lt;td&gt;1/1000 da base&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;trade-offs-operacionais&quot;&gt;Trade-offs Operacionais&lt;/h3&gt;

&lt;p&gt;O aumento da granularidade celular apresenta trade-offs que devem ser considerados:&lt;/p&gt;

&lt;h4 id=&quot;benefícios-da-alta-granularidade&quot;&gt;Benefícios da Alta Granularidade&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Redução de Blast Radius&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;10 células: Falha afeta 10% dos usuários&lt;/li&gt;
  &lt;li&gt;100 células: Falha afeta 1% dos usuários&lt;/li&gt;
  &lt;li&gt;1000 células: Falha afeta 0.1% dos usuários&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Isolamento Melhorado&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Falhas ficam contidas em domínios menores&lt;/li&gt;
  &lt;li&gt;Debugging e troubleshooting mais focado&lt;/li&gt;
  &lt;li&gt;Rollbacks afetam menos usuários&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h4 id=&quot;custos-da-alta-granularidade&quot;&gt;Custos da Alta Granularidade&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Complexidade Operacional vs. Granularidade Celular&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Aspecto&lt;/th&gt;
      &lt;th&gt;Baixa Granularidade&lt;br&gt;(3-10 Células)&lt;/th&gt;
      &lt;th&gt;Média Granularidade&lt;br&gt;(25-50 Células)&lt;/th&gt;
      &lt;th&gt;Alta Granularidade&lt;br&gt;(100+ Células)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Complexidade Geral&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Moderada&lt;/td&gt;
      &lt;td&gt;Moderada&lt;/td&gt;
      &lt;td&gt;Alta&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Monitoramento&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Simples&lt;br&gt;- Poucos endpoints&lt;br&gt;- Dashboards básicos&lt;/td&gt;
      &lt;td&gt;Estruturado&lt;br&gt;- Alertas configurados&lt;br&gt;- Métricas agregadas&lt;/td&gt;
      &lt;td&gt;Sofisticado&lt;br&gt;- Observabilidade avançada&lt;br&gt;- APM necessário&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Automatizado&lt;br&gt;- CI/CD recomendado&lt;br&gt;- Blue/Green deploy&lt;/td&gt;
      &lt;td&gt;Automatizado&lt;br&gt;- CI/CD recomendado&lt;br&gt;- Blue/Green deploy&lt;/td&gt;
      &lt;td&gt;CI/CD Avançado&lt;br&gt;- Canary releases&lt;br&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Recursos Computacionais&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Baixo&lt;br&gt;- 3-10 instâncias&lt;br&gt;- Overhead mínimo&lt;/td&gt;
      &lt;td&gt;Moderado&lt;br&gt;- 25-50 instâncias&lt;br&gt;- Overhead controlado&lt;/td&gt;
      &lt;td&gt;Alto&lt;br&gt;- 100+ instâncias&lt;br&gt;- Overhead significativo&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Custo Operacional&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Baixo&lt;/td&gt;
      &lt;td&gt;Médio&lt;/td&gt;
      &lt;td&gt;Alto&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overhead Operacional Detalhado&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Recursos Computacionais&lt;/strong&gt;: Aumento linear proporcional ao número de células&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Monitoramento e Observabilidade&lt;/strong&gt;: Necessidade de ferramentas sofisticadas (Prometheus, Grafana, Jaeger)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Automação&lt;/strong&gt;: Obrigatória para granularidade alta, opcional para baixa granularidade&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Equipe Especializada&lt;/strong&gt;: Requisitos crescentes de expertise em SRE e DevOps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;modelo-matemático-de-disponibilidade&quot;&gt;Modelo Matemático de Disponibilidade&lt;/h3&gt;

&lt;p&gt;Para múltiplas falhas simultâneas, o modelo estende-se para:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;Disponibilidade = ((N - F) / N) × 100%

Exemplos práticos:
- 100 células, 2 falhas: ((100-2)/100) = 98% disponível
- 100 células, 5 falhas: ((100-5)/100) = 95% disponível
- 1000 células, 10 falhas: ((1000-10)/1000) = 99% disponível
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;implementação-na-prova-de-conceito&quot;&gt;Implementação na Prova de Conceito&lt;/h3&gt;

&lt;p&gt;A PoC desenvolvida permite configuração dinâmica do número de células através de variáveis de ambiente:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# Configuração para baixo blast radius (alta granularidade)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SHARD_01_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://cell-001:8080&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SHARD_02_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://cell-002:8080&quot;&lt;/span&gt;
...
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SHARD_100_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://cell-100:8080&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Resultado: 1% blast radius por falha&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;O roteador automaticamente distribui a carga entre todas as células configuradas, garantindo que a falha de qualquer célula individual afete apenas 1/N da base de usuários.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;racional-prático-de-blast-radius&quot;&gt;Racional prático de blast radius&lt;/h3&gt;

&lt;p&gt;Com base na análise de blast radius, recomenda-se:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Startups/Pequenas Aplicações&lt;/strong&gt;: 5-10 células (blast radius: 10-20%)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Aplicações Médias&lt;/strong&gt;: 25-50 células (blast radius: 2-4%)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Aplicações Críticas&lt;/strong&gt;: 100+ células (blast radius: &amp;lt;1%)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sistemas de Alta Disponibilidade&lt;/strong&gt;: 1000+ células (blast radius: &amp;lt;0.1%)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;implementação-e-aspectos-técnicos&quot;&gt;Implementação e Aspectos Técnicos&lt;/h2&gt;

&lt;p&gt;A implementação da PoC utiliza sharding baseado em chaves de identificação de clientes, conforme evidenciado na estrutura de configuração:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ShardRouterImpl&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;hashRing&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;interfaces&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HashRing&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shardingKey&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShardRouterImpl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetShardingKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Header&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shardingKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Esta abordagem garante que todas as requisições de um determinado cliente sejam consistentemente direcionadas à mesma célula, propriedade fundamental para manutenção de estado e cache locality (DeCandia et al., 2007).&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;hashing-consistente&quot;&gt;Hashing Consistente&lt;/h3&gt;

&lt;p&gt;O hashing consistente, introduzido por Karger et al. (1997), resolve limitações do hashing tradicional em ambientes distribuídos dinâmicos. Enquanto o hashing simples requer redistribuição global de chaves quando nós são adicionados ou removidos, o hashing consistente minimiza a movimentação de dados, redistribuindo apenas uma fração das chaves.&lt;/p&gt;

&lt;div class=&quot;mermaid&quot;&gt;
graph TB
    subgraph &quot;Hashing Tradicional&quot;
        HT1[3 Servidores]
        HT2[Key % 3]
        HT3[Server 0: 33%]
        HT4[Server 1: 33%] 
        HT5[Server 2: 33%]
        
        HT1 --&amp;gt; HT2
        HT2 --&amp;gt; HT3
        HT2 --&amp;gt; HT4
        HT2 --&amp;gt; HT5
        
        HT6[ +1 Servidor]
        HT7[Key % 4]
        HT8[75% das chaves&lt;br&gt;redistribuídas]
        
        HT6 --&amp;gt; HT7
        HT7 --&amp;gt; HT8
    end

    style HT8 fill:#ffcdd2
&lt;/div&gt;

&lt;div class=&quot;mermaid&quot;&gt;
graph TB

    subgraph &quot;Hashing Consistente&quot;
        HC1[Hash Ring]
        HC2[Virtual Replicas]
        HC3[Minimal Redistribution]
        
        HC1 --&amp;gt; HC2
        HC2 --&amp;gt; HC3
        
        HC4[ +1 Servidor]
        HC5[25%&lt;br&gt;das chaves movidas]
        
        HC4 --&amp;gt; HC5
    end
    
    style HC5 fill:#c8e6c9
&lt;/div&gt;

&lt;p&gt;A PoC implementa múltiplos algoritmos de hash, permitindo análise comparativa de desempenho e distribuição:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;MD5&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;HashAlgorithm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;MD5&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SHA1&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;HashAlgorithm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SHA1&quot;&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;SHA256&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;HashAlgorithm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SHA256&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SHA512&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;HashAlgorithm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SHA512&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;MURMUR3&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashAlgorithm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;MURMUR3&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Estudos empíricos realizados com a implementação revelam variações significativas na qualidade da distribuição entre algoritmos. O SHA1 apresentou a menor variância (121.67) e diferença entre melhor e pior shard (2.7%), enquanto algoritmos não-criptográficos como FNV64 demonstraram distribuição inadequada (variância de 156,116.33).&lt;/p&gt;

&lt;div class=&quot;mermaid&quot;&gt;
graph TB
    subgraph &quot;Comparação&quot;
        CAH1[Input: client-id]
        
        subgraph &quot;Algoritmos de Hashing&quot;
            AC1[SHA1&lt;br&gt;✅ Melhor Distribuição&lt;br&gt;Desvio: 11.03]
            AC2[SHA256&lt;br&gt;⚠️ Distribuição Moderada&lt;br&gt;Desvio: 64.60]
            AC3[SHA512&lt;br&gt;✅ Boa Distribuição&lt;br&gt;Desvio: 28.31]
            AC4[MD5&lt;br&gt;⚠️ Distribuição Aceitável&lt;br&gt;Desvio: 42.05]

            ANC1[MURMUR3&lt;br&gt;❌ Distribuição Irregular&lt;br&gt;Desvio: 95.84]
            ANC2[FNV64&lt;br&gt;❌ Distribuição Inadequada&lt;br&gt;Desvio: 395.12]

        end
        
        subgraph &quot;Distribuição nos Shards&quot;
            DS1[Shard A: 32-34%]
            DS2[Shard B: 30-35%] 
            DS3[Shard C: 31-37%]
        end
        
        CAH1 --&amp;gt; AC1
        CAH1 --&amp;gt; AC2
        CAH1 --&amp;gt; AC3
        CAH1 --&amp;gt; AC4
        CAH1 --&amp;gt; ANC1
        CAH1 --&amp;gt; ANC2
        
        AC1 --&amp;gt; DS1
        AC1 --&amp;gt; DS2
        AC1 --&amp;gt; DS3
    end
    
    style AC1 fill:#c8e6c9
    style AC3 fill:#e8f5e8
    style AC2 fill:#fff3e0
    style AC4 fill:#fff3e0
    style ANC1 fill:#ffcdd2
    style ANC2 fill:#ffcdd2
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;bulkheads-e-isolamento-de-falhas&quot;&gt;Bulkheads e Isolamento de Falhas&lt;/h3&gt;

&lt;p&gt;O padrão Bulkhead, inspirado na construção naval, propõe a compartimentalização de sistemas para conter falhas (Nygard, 2018). Na arquitetura celular, cada célula funciona como um bulkhead independente, onde falhas em uma célula não propagam para outras células do sistema.&lt;/p&gt;

&lt;div class=&quot;mermaid&quot;&gt;
graph TB
    subgraph &quot;Arquitetura sem Bulkheads&quot;
        AB1[Load Balancer]
        AB2[Shared Resource Pool]
        AB3[Service A]
        AB4[Service B] 
        AB5[Service C]
        AB6[💥 Falha em cascata]
        
        AB1 --&amp;gt; AB2
        AB2 --&amp;gt; AB3
        AB2 --&amp;gt; AB4
        AB2 --&amp;gt; AB5
        AB3 -.-&amp;gt;|falha propaga| AB4
        AB4 -.-&amp;gt;|falha propaga| AB5
        AB5 --&amp;gt; AB6
    end

    
    style AB6 fill:#ffcdd2
&lt;/div&gt;

&lt;div class=&quot;mermaid&quot;&gt;
graph TB    
    subgraph &quot;Arquitetura com Bulkheads (Celular)&quot;
        BC1[Shard Router]
        
        subgraph &quot;Célula B&quot;
            APPB[App B] --&amp;gt; DBB[Database B]
        end

        subgraph &quot;Célula A - Falha Isolada&quot;
            APPA[App A] --&amp;gt; DBA[Database A] --&amp;gt; FALHA[💥 Falha no Shard]
        end

        subgraph &quot;Célula C&quot;
            APPC[App C] --&amp;gt; DBC[Database C]
        end
        
        BC1 --&amp;gt; APPA
        BC1 --&amp;gt; APPB
        BC1 --&amp;gt; APPC
    end
    
    style FALHA fill:#ffcdd2
&lt;/div&gt;

&lt;p&gt;A implementação demonstra este isolamento através da estrutura de proxy reverso:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ph&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProxyHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ServeHTTP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResponseWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shardKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ph&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;router&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetShardingKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shardURL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ph&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;router&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetShardHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shardKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c&quot;&gt;// Isolamento: falha em um shard não afeta outros&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;proxyReq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StatusBadGateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;observabilidade-e-métricas&quot;&gt;Observabilidade e Métricas&lt;/h3&gt;

&lt;p&gt;A observabilidade é crucial para operação de sistemas distribuídos (Majors et al., 2022). A PoC implementa coleta de métricas usando Prometheus, fornecendo visibilidade sobre:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Distribuição de requisições por shard&lt;/li&gt;
  &lt;li&gt;Taxa de sucesso/falha por célula&lt;/li&gt;
  &lt;li&gt;Latência de processamento&lt;/li&gt;
  &lt;li&gt;Utilização de recursos&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;mermaid&quot;&gt;
graph TB
    subgraph &quot;Sistema Observável&quot;
        SO1[Cellular Router]
        
        subgraph &quot;Células&quot;
            SC1[Célula A]
            SC2[Célula B]
            SC3[Célula C]
        end
        
        subgraph &quot;Coleta de Métricas&quot;
            CM1[Prometheus Metrics]
            CM2[Request Counter]
            CM3[Response Counter]
            CM4[Health Checks]
        end
        
        subgraph &quot;Visualização&quot;
            SV1[Grafana Dashboard]
            SV2[📊 Distribuição por Shard]
            SV3[📈 Taxa de Sucesso/Falha]
            SV4[⏱️ Latência por Célula]
            SV5[🎯 Detecção de Hotspots]
        end
        
        SO1 --&amp;gt; SC1
        SO1 --&amp;gt; SC2
        SO1 --&amp;gt; SC3
        
        SC1 --&amp;gt; CM1
        SC2 --&amp;gt; CM1
        SC3 --&amp;gt; CM1
        
        CM1 --&amp;gt; CM2
        CM1 --&amp;gt; CM3
        CM1 --&amp;gt; CM4
        
        CM1 --&amp;gt; SV1
        SV1 --&amp;gt; SV2
        SV1 --&amp;gt; SV3
        SV1 --&amp;gt; SV4
        SV1 --&amp;gt; SV5
    end
    
    style CM1 fill:#e3f2fd
    style SV1 fill:#e8f5e8
    style SV5 fill:#fff3e0
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PrometheusMetricsRecorder&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;requestsCounter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prometheus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CounterVec&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;responseCounter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prometheus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CounterVec&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrometheusMetricsRecorder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RecordRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shard&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requestsCounter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WithLabelValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Inc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;arquitetura-da-solução&quot;&gt;Arquitetura da Solução&lt;/h2&gt;

&lt;h3 id=&quot;visão-geral-do-sistema&quot;&gt;Visão Geral do Sistema&lt;/h3&gt;

&lt;p&gt;A arquitetura implementada na PoC segue o padrão de proxy reverso com roteamento baseado em hashing consistente. O sistema é composto por três camadas principais:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Camada de Roteamento&lt;/strong&gt;: Responsável por receber requisições e determinar o shard de destino&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Camada de Hash Ring&lt;/strong&gt;: Implementa o algoritmo de hashing consistente&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Camada de Células&lt;/strong&gt;: Conjunto de serviços independentes (shards)&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;mermaid&quot;&gt;
graph TB
    subgraph &quot;Cliente&quot;
        C1[Aplicação Cliente]
        C2[Header: client-id]
    end
    
    subgraph &quot;Camada de Roteamento&quot;
        R1[HTTP Server :8080]
        R2[Proxy Handler]
        R3[Shard Router]
    end
    
    subgraph &quot;Camada Hash Ring&quot;
        H1[Consistent Hash Ring]
        H2[SHA-512 Algorithm]
        H3[Virtual Replicas]
    end
    
    subgraph &quot;Células (Shards)&quot;
        S1[Célula A&lt;br&gt;Domain Shard]
        S2[Célula B&lt;br&gt;Domain Shard] 
        S3[Célula C&lt;br&gt;Domain Shard]
        SN[Célula N&lt;br&gt;Domain Shard]
    end
    
    subgraph &quot;Observabilidade&quot;
        M1[Prometheus Metrics]
        M2[Health Checks]
    end
    
    C1 --&amp;gt; C2
    C2 --&amp;gt; R1
    R1 --&amp;gt; R2
    R2 --&amp;gt; R3
    R3 --&amp;gt; H1
    H1 --&amp;gt; H2
    H2 --&amp;gt; H3
    
    H3 --&amp;gt; S1
    H3 --&amp;gt; S2 
    H3 --&amp;gt; S3
    H3 --&amp;gt; SN
    
    R2 --&amp;gt; M1
    R1 --&amp;gt; M2
    
    style S1 fill:#e1f5fe
    style S2 fill:#e1f5fe
    style S3 fill:#e1f5fe
    style SN fill:#e1f5fe
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;fluxo-de-processamento&quot;&gt;Fluxo de Processamento&lt;/h3&gt;

&lt;p&gt;O fluxo de processamento de uma requisição na arquitetura celular segue as seguintes etapas:&lt;/p&gt;

&lt;div class=&quot;mermaid&quot;&gt;
sequenceDiagram
    participant C as Cliente
    participant P as Proxy Router
    participant H as Hash Ring
    participant S as Shard (Célula)
    participant M as Métricas
    
    C-&amp;gt;&amp;gt;P: HTTP Request + client-id header
    P-&amp;gt;&amp;gt;P: Extrair sharding key
    P-&amp;gt;&amp;gt;H: GetNode(client-id)
    H-&amp;gt;&amp;gt;H: Calcular hash SHA-512
    H-&amp;gt;&amp;gt;H: Localizar no ring
    H--&amp;gt;&amp;gt;P: URL do shard destino
    P-&amp;gt;&amp;gt;M: Registrar requisição
    P-&amp;gt;&amp;gt;S: Proxy request
    S--&amp;gt;&amp;gt;P: Response
    P-&amp;gt;&amp;gt;M: Registrar resposta
    P--&amp;gt;&amp;gt;C: HTTP Response
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;algoritmo-de-distribuição&quot;&gt;Algoritmo de Distribuição&lt;/h3&gt;

&lt;p&gt;O algoritmo de hashing consistente implementado utiliza réplicas virtuais para melhorar a distribuição uniforme das chaves:&lt;/p&gt;

&lt;div class=&quot;mermaid&quot;&gt;
graph LR
    subgraph &quot;Hash Ring&quot;
        direction TB
        R1[Replica Shard-1-0&lt;br&gt;Hash: 0x1A2B]
        R2[Replica Shard-2-0&lt;br&gt;Hash: 0x3C4D]  
        R3[Replica Shard-1-1&lt;br&gt;Hash: 0x5E6F]
        R4[Replica Shard-3-0&lt;br&gt;Hash: 0x7890]
        R5[Replica Shard-2-1&lt;br&gt;Hash: 0x9ABC]
        R6[Replica Shard-3-1&lt;br&gt;Hash: 0xDEF0]
    end
    
    K1[Key: user-123&lt;br&gt;Hash: 0x4567] --&amp;gt; R3
    K2[Key: user-456&lt;br&gt;Hash: 0x8901] --&amp;gt; R6
    K3[Key: user-789&lt;br&gt;Hash: 0x2345] --&amp;gt; R2
    
    style R1 fill:#ffcdd2
    style R3 fill:#ffcdd2
    style R2 fill:#c8e6c9
    style R5 fill:#c8e6c9
    style R4 fill:#e1bee7
    style R6 fill:#e1bee7
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;análise-de-desempenho-dos-algoritmos-de-hash&quot;&gt;Análise de Desempenho dos Algoritmos de Hash&lt;/h2&gt;

&lt;h3 id=&quot;metodologia-de-avaliação&quot;&gt;Metodologia de Avaliação&lt;/h3&gt;

&lt;p&gt;Para validar a eficácia dos diferentes algoritmos de hash na distribuição uniforme de chaves, foram realizados experimentos com &lt;strong&gt;1 milhão de chaves UUID v4&lt;/strong&gt; distribuídas entre 3 shards. As chaves UUID v4 foram escolhidas por sua natureza aleatória e representatividade em cenários reais de produção. Os critérios de avaliação incluíram:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Uniformidade de distribuição&lt;/strong&gt;: Medida pelo desvio padrão da distribuição&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Variância&lt;/strong&gt;: Indicador de dispersão dos valores&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Diferença máxima&lt;/strong&gt;: Distância entre o shard com maior e menor carga&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;resultados-experimentais&quot;&gt;Resultados Experimentais&lt;/h3&gt;

&lt;p&gt;A Tabela 1 apresenta os resultados comparativos dos algoritmos testados:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Análise Comparativa de Algoritmos de Hash&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Algoritmo&lt;/th&gt;
      &lt;th&gt;Desvio Padrão&lt;/th&gt;
      &lt;th&gt;Variância&lt;/th&gt;
      &lt;th&gt;Melhor Shard (%)&lt;/th&gt;
      &lt;th&gt;Pior Shard (%)&lt;/th&gt;
      &lt;th&gt;Diferença&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;SHA1&lt;/td&gt;
      &lt;td&gt;11.03&lt;/td&gt;
      &lt;td&gt;121.67&lt;/td&gt;
      &lt;td&gt;32.0%&lt;/td&gt;
      &lt;td&gt;34.7%&lt;/td&gt;
      &lt;td&gt;2.7%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;SHA512&lt;/td&gt;
      &lt;td&gt;28.31&lt;/td&gt;
      &lt;td&gt;801.67&lt;/td&gt;
      &lt;td&gt;30.5%&lt;/td&gt;
      &lt;td&gt;37.2%&lt;/td&gt;
      &lt;td&gt;6.7%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;SHA256&lt;/td&gt;
      &lt;td&gt;64.60&lt;/td&gt;
      &lt;td&gt;4173.67&lt;/td&gt;
      &lt;td&gt;26.3%&lt;/td&gt;
      &lt;td&gt;41.9%&lt;/td&gt;
      &lt;td&gt;15.6%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MD5&lt;/td&gt;
      &lt;td&gt;42.05&lt;/td&gt;
      &lt;td&gt;1768.33&lt;/td&gt;
      &lt;td&gt;28.2%&lt;/td&gt;
      &lt;td&gt;38.5%&lt;/td&gt;
      &lt;td&gt;10.3%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MURMUR3&lt;/td&gt;
      &lt;td&gt;95.84&lt;/td&gt;
      &lt;td&gt;9185.33&lt;/td&gt;
      &lt;td&gt;23.1%&lt;/td&gt;
      &lt;td&gt;48.2%&lt;/td&gt;
      &lt;td&gt;25.1%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br&gt; &lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;discussão-dos-resultados&quot;&gt;Discussão dos Resultados&lt;/h3&gt;

&lt;p&gt;Os resultados evidenciam que o &lt;strong&gt;SHA1&lt;/strong&gt; apresenta a melhor distribuição uniforme, com menor desvio padrão (11.03) e diferença entre shards (2.7%). Este comportamento contraria expectativas iniciais que favoreciam SHA-512 devido à maior complexidade criptográfica.&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;SHA-512&lt;/strong&gt;, embora apresente distribuição aceitável (desvio padrão: 28.31), demonstra performance inferior ao SHA1 em termos de uniformidade. Contudo, mantém características criptográficas superiores, relevantes para cenários que exigem resistência a ataques de hash.&lt;/p&gt;

&lt;p&gt;Algoritmos não-criptográficos como &lt;strong&gt;MURMUR3&lt;/strong&gt; apresentaram distribuição menos uniforme que esperado, contradizendo literatura que sugere sua superioridade em aplicações de hashing distribuído (Appleby, 2008).&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;propriedades-da-arquitetura-celular&quot;&gt;Propriedades da Arquitetura Celular&lt;/h2&gt;

&lt;h3 id=&quot;determinismo-de-roteamento&quot;&gt;Determinismo de Roteamento&lt;/h3&gt;

&lt;p&gt;Uma propriedade fundamental da arquitetura celular é o determinismo de roteamento. Requisições com a mesma chave de sharding são consistentemente direcionadas à mesma célula, independentemente do momento da requisição ou estado do sistema.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShardRouterImpl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetShardHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hashRing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[%s] Mapping key %s to host: %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
               &lt;span class=&quot;n&quot;&gt;sr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hashRing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetHashAlgorithm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Esta propriedade é essencial para:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Manutenção de cache local por célula&lt;/li&gt;
  &lt;li&gt;Consistência de sessão de usuário&lt;/li&gt;
  &lt;li&gt;Otimização de consultas relacionadas por cliente&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;escalabilidade-horizontal&quot;&gt;Escalabilidade Horizontal&lt;/h3&gt;

&lt;p&gt;A arquitetura permite adição dinâmica de células sem interrupção do serviço. O uso de hashing consistente garante redistribuição mínima de chaves (aproximadamente K/N chaves movidas, onde K é o total de chaves e N o número de nós).&lt;/p&gt;

&lt;h3 id=&quot;tolerância-a-falhas&quot;&gt;Tolerância a Falhas&lt;/h3&gt;

&lt;p&gt;O isolamento entre células proporciona contenção de falhas. A indisponibilidade de uma célula afeta apenas os clientes mapeados para aquela célula específica, mantendo o restante do sistema operacional.&lt;/p&gt;

&lt;h3 id=&quot;observabilidade-granular&quot;&gt;Observabilidade Granular&lt;/h3&gt;

&lt;p&gt;O roteamento determinístico facilita observabilidade granular por célula, permitindo:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Métricas específicas por domínio de clientes&lt;/li&gt;
  &lt;li&gt;Detecção de hotspots de tráfego&lt;/li&gt;
  &lt;li&gt;Análise de padrões de uso por segmento&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;implementação-e-aspectos-técnicos-1&quot;&gt;Implementação e Aspectos Técnicos&lt;/h2&gt;

&lt;h3 id=&quot;padrões-de-projeto-aplicados&quot;&gt;Padrões de Projeto Aplicados&lt;/h3&gt;

&lt;p&gt;A implementação utiliza diversos padrões consolidados:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strategy Pattern&lt;/strong&gt;: Para algoritmos de hash intercambiáveis&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashAlgorithm&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SHA512&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;HashAlgorithm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SHA512&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SHA256&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;HashAlgorithm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SHA256&quot;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Proxy Pattern&lt;/strong&gt;: Para roteamento transparente de requisições&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProxyHandler&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;router&lt;/span&gt;          &lt;span class=&quot;n&quot;&gt;interfaces&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShardRouter&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;metricsRecorder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interfaces&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MetricsRecorder&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Factory Pattern&lt;/strong&gt;: Para criação de componentes configuráveis&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewConsistentHashRing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numReplicas&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interfaces&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HashRing&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ring&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConsistentHashRing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Nodes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;NumReplicas&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numReplicas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ring&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configureHashAlgorithm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ring&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;limitações-e-trabalhos-futuros&quot;&gt;Limitações e Trabalhos Futuros&lt;/h2&gt;

&lt;h3 id=&quot;limitações-identificadas&quot;&gt;Limitações Identificadas&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Rebalanceamento&lt;/strong&gt;: Não há implementação automática de rebalanceamento quando células ficam sobrecarregadas&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Descoberta de Serviços&lt;/strong&gt;: Configuração estática de shards limita elasticidade&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Consistência Cross-Cell&lt;/strong&gt;: Transações que envolvem múltiplas células não são suportadas&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Circuit Breaker&lt;/strong&gt;: Ausência de proteção contra cascata de falhas&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;extensões-propostas&quot;&gt;Extensões Propostas&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Auto-scaling Celular&lt;/strong&gt;: Algoritmos para adição/remoção automática de células baseado em métricas de carga&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Service Mesh Integration&lt;/strong&gt;: Integração com Istio/Linkerd para descoberta de serviços e políticas de tráfego&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Distributed Tracing&lt;/strong&gt;: Implementação de rastreamento distribuído para análise de latência cross-cell&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Consensus Protocols&lt;/strong&gt;: Integração com Raft/PBFT para coordenação entre células&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2 id=&quot;referências&quot;&gt;Referências&lt;/h2&gt;

&lt;p&gt;Appleby, A. (2008). &lt;em&gt;MurmurHash3&lt;/em&gt;. SMHasher. https://github.com/aappleby/smhasher&lt;/p&gt;

&lt;p&gt;DeCandia, G., Hastorun, D., Jampani, M., Kakulapati, G., Lakshman, A., Pilchin, A., … &amp;amp; Vogels, W. (2007). Dynamo: Amazon’s highly available key-value store. &lt;em&gt;ACM SIGOPS operating systems review&lt;/em&gt;, 41(6), 205-220. https://doi.org/10.1145/1323293.1294281&lt;/p&gt;

&lt;p&gt;Karger, D., Lehman, E., Leighton, T., Panigrahy, R., Levine, M., &amp;amp; Lewin, D. (1997). Consistent hashing and random trees: Distributed caching protocols for relieving hot spots on the World Wide Web. &lt;em&gt;Proceedings of the twenty-ninth annual ACM symposium on Theory of computing&lt;/em&gt;, 654-663. https://doi.org/10.1145/258533.258660&lt;/p&gt;

&lt;p&gt;Majors, C., Fong-Jones, L., &amp;amp; Miranda, G. (2022). &lt;em&gt;Observability engineering: Achieving production excellence&lt;/em&gt;. O’Reilly Media.&lt;/p&gt;

&lt;p&gt;Newman, S. (2015). &lt;em&gt;Building microservices: Designing fine-grained systems&lt;/em&gt;. O’Reilly Media.&lt;/p&gt;

&lt;p&gt;Nygard, M. T. (2018). &lt;em&gt;Release it!: Design and deploy production-ready software&lt;/em&gt; (2nd ed.). Pragmatic Bookshelf.&lt;/p&gt;

&lt;p&gt;Özsu, M. T., &amp;amp; Valduriez, P. (2020). &lt;em&gt;Principles of distributed database systems&lt;/em&gt; (4th ed.). Springer. https://doi.org/10.1007/978-3-030-26253-2&lt;/p&gt;

&lt;p&gt;Richardson, C. (2018). &lt;em&gt;Microservices patterns: With examples in Java&lt;/em&gt;. Manning Publications.&lt;/p&gt;

&lt;script src=&quot;https://unpkg.com/mermaid@11.12.0/dist/mermaid.min.js&quot;&gt;&lt;/script&gt;

&lt;script&gt;
  $(document).ready(function () {
    mermaid.initialize({
      startOnLoad:true,
      theme: &quot;default&quot;,
    });
    window.mermaid.init(undefined, document.querySelectorAll(&apos;.language-mermaid&apos;));
  });
&lt;/script&gt;

</description>
        <pubDate>Thu, 25 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://fidelissauro.dev/msc-shard-router/</link>
        <guid isPermaLink="true">https://fidelissauro.dev/msc-shard-router/</guid>
        
        
        <category>msc</category>
        
      </item>
    
      <item>
        <title>System Design - Teorema PACELC</title>
        <description>&lt;p&gt;Esse texto é um complemento ao capítulo anterior sobre &lt;a href=&quot;/teorema-cap/&quot;&gt;&lt;strong&gt;ACID, BASE e Teorema CAP&lt;/strong&gt;&lt;/a&gt;, e apresenta uma &lt;strong&gt;evolução conceitual do modelo teórico do CAP&lt;/strong&gt;, incluindo as críticas que surgiram com a evolução dos sistemas distribuídos e de seus componentes. O &lt;strong&gt;PACELC&lt;/strong&gt; é um conceito mais moderno, que ajuda a compreender &lt;strong&gt;algumas lacunas que o CAP não cobre&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Após uma boa assimilação das classificações &lt;strong&gt;AP, CA, CP&lt;/strong&gt; do CAP, podemos aprofundar o entendimento nos &lt;strong&gt;apêndices trazidos pelo PACELC&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;o-teorema-pacelc&quot;&gt;O Teorema PACELC&lt;/h1&gt;

&lt;p&gt;O Teorema PACELC foi proposto por Daniel Abadi em 2010, na Universidade de Yale, e nos ajuda a entender sistemas distribuídos &lt;strong&gt;para além do que é proposto pelo Teorema CAP&lt;/strong&gt;. O Teorema CAP, como já vimos, estabelece que &lt;strong&gt;um sistema, mediante uma partição de rede, precisa escolher entre consistência e disponibilidade&lt;/strong&gt;. Esse modelo foi — e ainda é — extremamente importante para nortear decisões arquiteturais e de engenharia em diversos contextos, mas deixa algumas lacunas conceituais em sistemas modernos, principalmente quanto ao comportamento do sistema ao considerar a &lt;strong&gt;Partition Tolerance&lt;/strong&gt;. Por exemplo: &lt;strong&gt;o que aconteceria com um sistema quando não houver falhas de rede?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/pacelc-partition-sync.drawio.png&quot; alt=&quot;Partição de Rede&quot;&gt;&lt;/p&gt;

&lt;p&gt;O PACELC amplia esse entendimento no contexto de bancos de dados distribuídos ao levantar uma nova questão: &lt;strong&gt;o que acontece quando não há falhas de rede e não há particionamento entre os nós do sistema?&lt;/strong&gt; Nesses cenários, &lt;strong&gt;é possível operar em diferentes níveis de consistência&lt;/strong&gt; conforme a necessidade. Assim, o modelo nos ajuda a refletir: &lt;strong&gt;o que o sistema deve priorizar quando está funcionando corretamente?&lt;/strong&gt; E, de forma complementar, &lt;strong&gt;o que ele deve priorizar quando ocorre um particionamento entre os nós?&lt;/strong&gt; O teorema nos ajuda a responder esses questionamentos de forma mais detalhada.&lt;/p&gt;

&lt;h1 id=&quot;teorema-pacelc-vs-teorema-cap&quot;&gt;Teorema PACELC vs Teorema CAP&lt;/h1&gt;

&lt;p&gt;Como vimos anteriormente, o Teorema CAP diz que &lt;strong&gt;quando ocorre uma Partição de Rede (P) entre os nós do sistema, é necessário escolher entre Consistência (C) ou Disponibilidade (A)&lt;/strong&gt;. Esse raciocínio é muito útil para a escolha de tecnologias que envolvem esses dois trade-offs, mas ainda deixa em aberto o requisito não funcional de &lt;strong&gt;como o sistema deve operar quando não há partições de rede&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/pacelc.drawio.png&quot; alt=&quot;PACELC&quot;&gt;&lt;/p&gt;

&lt;p&gt;O PACELC funciona como uma &lt;strong&gt;extensão do CAP&lt;/strong&gt;, propondo o seguinte racional: &lt;strong&gt;se houver partição (P), devemos escolher entre Disponibilidade (A) e Consistência (C)&lt;/strong&gt;; &lt;strong&gt;Else (E)&lt;/strong&gt;, ou seja, &lt;strong&gt;se não houver partição&lt;/strong&gt;, escolhemos &lt;strong&gt;entre Latência (L) e Consistência (C)&lt;/strong&gt;. O teorema mostra que, mesmo em condições normais, sem partições, ainda é preciso tomar decisões difíceis ao projetar a arquitetura. &lt;strong&gt;Ou priorizamos a garantia de uma consistência forte, pagando o preço de mais latência&lt;/strong&gt;, ou &lt;strong&gt;abrimos mão de maiores níveis de consistência para reduzir o tempo de resposta e otimizar a performance das operações&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Em momentos de falha, decidimos entre disponibilidade e consistência&lt;/strong&gt;. Fora deles, &lt;strong&gt;optamos entre consistência forte, que pode custar latência&lt;/strong&gt;, e &lt;strong&gt;consistência eventual, que otimiza a performance, mas sem garantias imediatas de consistência&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Esse raciocínio aproxima o modelo da &lt;strong&gt;realidade dos sistemas modernos&lt;/strong&gt;, onde temos redes geograficamente distribuídas, &lt;a href=&quot;/replicacao&quot;&gt;replicação de dados&lt;/a&gt; e &lt;a href=&quot;/sharding&quot;&gt;sharding e particionamento&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Imagine um banco de dados global: se ele quiser garantir que todas as réplicas estejam sempre sincronizadas antes de confirmar uma operação (&lt;strong&gt;consistência forte&lt;/strong&gt;), cada escrita será mais lenta devido à latência de rede. Já se ele aceitar &lt;strong&gt;consistência eventual&lt;/strong&gt;, poderá responder mais rápido, mas correrá o risco de que um usuário no Brasil veja um dado diferente de outro usuário na Espanha por algum tempo.&lt;/p&gt;

&lt;p&gt;Em resumo, &lt;strong&gt;os dois teoremas não são excludentes, mas complementares&lt;/strong&gt;. O PACELC amplia o CAP ao analisar não apenas os cenários de falha, mas também &lt;strong&gt;o comportamento do sistema em situações normais&lt;/strong&gt;, conectando os padrões &lt;strong&gt;CP (Consistency + Partition Tolerance)&lt;/strong&gt; e &lt;strong&gt;AP (Availability + Partition Tolerance)&lt;/strong&gt; com as escolhas de &lt;strong&gt;Latência (L) e Consistência (C)&lt;/strong&gt; fora das partições.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1 id=&quot;aplicações-do-pacelc&quot;&gt;Aplicações do PACELC&lt;/h1&gt;

&lt;p&gt;O Teorema PACELC se tornou uma forma prática de &lt;strong&gt;classificar sistemas distribuídos e suas bases de dados&lt;/strong&gt;. Por exemplo, o &lt;strong&gt;Amazon DynamoDB&lt;/strong&gt; é conhecido como &lt;strong&gt;PA/EL&lt;/strong&gt; — ou seja, &lt;strong&gt;prefere disponibilidade durante partições (PA) e latência baixa em condições normais (EL)&lt;/strong&gt;. Já o &lt;strong&gt;Google Spanner&lt;/strong&gt; é classificado como &lt;strong&gt;PC/EC&lt;/strong&gt;, pois &lt;strong&gt;prefere consistência tanto durante partições quanto no funcionamento cotidiano, aceitando pagar o preço da latência&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Assim como no CAP, em que temos classificações como &lt;strong&gt;AP, CP ou AC&lt;/strong&gt;, no PACELC também é possível categorizar os bancos de dados em diferentes combinações, como &lt;strong&gt;PA/EL, PC/EL, PA/EC e PC/EC&lt;/strong&gt;, dependendo das escolhas de trade-offs.&lt;/p&gt;

&lt;h2 id=&quot;pael-on-partition-availability-else-latency&quot;&gt;PA/EL (On Partition, Availability; Else, Latency)&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/pacelc-pael.drawio.png&quot; alt=&quot;PA/EL&quot;&gt;&lt;/p&gt;

&lt;p&gt;O modelo &lt;strong&gt;PA/EL&lt;/strong&gt; descreve um sistema que, em condições normais (sem partição de rede), &lt;strong&gt;prioriza a latência em vez da consistência&lt;/strong&gt;. Esse tipo de sistema busca garantir &lt;strong&gt;baixa latência nas operações&lt;/strong&gt;, mesmo que isso signifique abrir mão de uma consistência forte. &lt;strong&gt;Else (E):&lt;/strong&gt; quando ocorre uma partição de rede, o sistema &lt;strong&gt;prioriza a disponibilidade (A) em vez da consistência (C)&lt;/strong&gt;. Em outras palavras, reforça o modelo de &lt;strong&gt;consistência eventual&lt;/strong&gt;, no qual todos os nós continuam respondendo às requisições independentemente do rompimento da partição, ainda que as réplicas não estejam totalmente sincronizadas.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/pael-error.drawio.png&quot; alt=&quot;PA/EL - Error&quot;&gt;&lt;/p&gt;

&lt;p&gt;Esses bancos de dados são projetados para oferecer &lt;strong&gt;alta performance nas operações de escrita de forma resiliente&lt;/strong&gt;, mas aceitam que diferentes usuários possam ver versões ligeiramente diferentes dos dados por algum tempo, até que a partição seja resolvida. É o caso de tecnologias como &lt;strong&gt;DynamoDB e Cassandra&lt;/strong&gt;, amplamente utilizadas em cenários de grande escala, onde &lt;strong&gt;performance global e disponibilidade&lt;/strong&gt; são mais importantes que a &lt;strong&gt;consistência absoluta&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;pcel-on-partition-consistency-else-latency&quot;&gt;PC/EL (On Partition, Consistency; Else, Latency)&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/pc-el.drawig.png&quot; alt=&quot;PC/EL&quot;&gt;&lt;/p&gt;

&lt;p&gt;No modelo &lt;strong&gt;PC/EL&lt;/strong&gt;, temos sistemas que, em seu funcionamento normal, &lt;strong&gt;priorizam a latência e o alto throughput ao custo da consistência&lt;/strong&gt;. Nesse cenário, o sistema &lt;strong&gt;reduz o nível de consistência operacional&lt;/strong&gt; para manter &lt;strong&gt;tempos de resposta rápidos e operações de escrita otimizadas&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Else (E):&lt;/strong&gt; em caso de particionamento, o sistema &lt;strong&gt;passa a priorizar a consistência (C)&lt;/strong&gt;. Isso significa que, em uma situação de falha, &lt;strong&gt;o sistema pode ficar indisponível até que o cluster recupere o consenso e volte a operar&lt;/strong&gt;, garantindo a &lt;strong&gt;integridade dos dados&lt;/strong&gt; mesmo ao custo da &lt;strong&gt;disponibilidade temporária&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/pc-el-error.drawio.png&quot; alt=&quot;PC/EL - Error&quot;&gt;&lt;/p&gt;

&lt;p&gt;É uma escolha intermediária, em que os sistemas em questão &lt;strong&gt;não possuem soluções confiáveis de resolução de conflitos em grandes volumes de dados&lt;/strong&gt;, funcionando apenas dentro do fluxo transacional previsto. Por isso, &lt;strong&gt;é preferível tornar o serviço indisponível do que lidar com uma parcela de dados que eventualmente nunca se tornaria consistente&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Esse modelo é interessante quando &lt;strong&gt;a consistência mínima durante falhas é inegociável&lt;/strong&gt;, mas, durante a operação normal, o objetivo é &lt;strong&gt;priorizar alto desempenho nas operações de escrita e leitura&lt;/strong&gt;. O sistema &lt;strong&gt;aceita consistência eventual apenas quando todos os nós estão disponíveis&lt;/strong&gt;, exigindo &lt;strong&gt;processos contínuos de health checks e heartbeats&lt;/strong&gt; entre eles para validar o status antes de realizar operações. Caso contrário, &lt;strong&gt;prefere ficar totalmente inoperante&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;paec-on-partition-availability-else-consistency&quot;&gt;PA/EC (On Partition, Availability; Else, Consistency)&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/pa-ec.drawio.png&quot; alt=&quot;PA/EC&quot;&gt;&lt;/p&gt;

&lt;p&gt;O modelo &lt;strong&gt;PA/EC&lt;/strong&gt; descreve sistemas que, em condições normais de operação, &lt;strong&gt;priorizam a consistência forte&lt;/strong&gt;, garantindo que &lt;strong&gt;todas as réplicas do sistema mantenham sempre a mesma versão do dado&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Else (E):&lt;/strong&gt; em caso de falhas ou particionamentos de rede, o sistema &lt;strong&gt;prioriza a disponibilidade (A)&lt;/strong&gt;, aceitando operações de escrita e leitura mesmo que existam &lt;strong&gt;divergências temporárias&lt;/strong&gt; entre as réplicas.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/pa-ec-error.drawio.png&quot; alt=&quot;PA/EC Error&quot;&gt;&lt;/p&gt;

&lt;p&gt;Normalmente, esses sistemas contam com algoritmos complexos de &lt;a href=&quot;/replicacao/&quot;&gt;&lt;strong&gt;CRDTs&lt;/strong&gt;&lt;/a&gt; (&lt;em&gt;Conflict-Free Replicated Data Types&lt;/em&gt;), que fazem a gestão de conflitos entre diferentes atualizações de dados em nós distribuídos. Esse modelo é menos comum, mas pode aparecer em &lt;strong&gt;contextos híbridos de microserviços&lt;/strong&gt;, nos quais a experiência do usuário não pode parar mesmo com falhas parciais, mas em que a &lt;strong&gt;regra de negócio e a criticidade operacional exigem que, quando a rede está saudável, todos os dados permaneçam rigorosamente sincronizados&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Em resumo, esse modelo &lt;strong&gt;assume a consistência eventual apenas como um fallback da consistência forte em último caso&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;pcec-on-partition-consistency-else-consistency&quot;&gt;PC/EC (On Partition, Consistency; Else, Consistency)&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/PC-EC.drawio.png&quot; alt=&quot;PC/EC&quot;&gt;&lt;/p&gt;

&lt;p&gt;O modelo &lt;strong&gt;PC/EC&lt;/strong&gt; descreve sistemas que são &lt;strong&gt;mais conservadores em relação à consistência dos dados&lt;/strong&gt;. Em operações normais, o sistema também &lt;strong&gt;prioriza a consistência em vez da latência&lt;/strong&gt;, aceitando um &lt;strong&gt;maior custo de tempo de resposta&lt;/strong&gt; em troca da garantia de que &lt;strong&gt;a última versão do dado esteja disponível em todos os nós&lt;/strong&gt;. &lt;strong&gt;Else (E):&lt;/strong&gt; durante uma partição de rede, o sistema &lt;strong&gt;prioriza a consistência (C) em vez da disponibilidade (A)&lt;/strong&gt;, assumindo que &lt;strong&gt;é melhor falhar temporariamente do que operar com consistência eventual&lt;/strong&gt; em algum nível.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/system-design/PE-EC-Partition.drawio.png&quot; alt=&quot;PC/EC&quot;&gt;&lt;/p&gt;

&lt;p&gt;Esse comportamento é típico em sistemas nos quais &lt;strong&gt;a precisão dos dados é a qualidade mais importante&lt;/strong&gt;. É a escolha natural para &lt;strong&gt;sistemas bancários, coordenação de clusters e transações críticas&lt;/strong&gt;, onde &lt;strong&gt;ver dados incorretos por alguns milissegundos pode gerar prejuízos enormes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Podemos encontrar esse modelo em &lt;strong&gt;bancos SQL tradicionais&lt;/strong&gt;, no &lt;strong&gt;etcd&lt;/strong&gt; e também em bancos &lt;strong&gt;transacionais geograficamente distribuídos&lt;/strong&gt;, como o &lt;strong&gt;Google Spanner&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;comparações-do-pacelc&quot;&gt;Comparações do PACELC&lt;/h3&gt;

&lt;p&gt;A seguir, temos uma tabela comparativa de alguns flavors de bancos ditribuídos que estão inerentes a trabalhos com partição, e onde cada uma delas se encontra dentro dos itens do PACELC.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Sistema / Banco de Dados&lt;/th&gt;
      &lt;th&gt;PAC (durante partição)&lt;/th&gt;
      &lt;th&gt;ELC (sem partição)&lt;/th&gt;
      &lt;th&gt;Classificação&lt;/th&gt;
      &lt;th&gt;Observação&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Amazon DynamoDB&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;A&lt;/strong&gt; (disponibilidade)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;L&lt;/strong&gt; (baixa latência, consistência eventual por padrão)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;PA/EL&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Eventual consistency como default, mas suporta “strong reads” opcionais.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Cassandra&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;A&lt;/strong&gt; (disponibilidade)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;L&lt;/strong&gt; (baixa latência, consistência eventual por padrão)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;PA/EL&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Modelo baseado no Dynamo, otimizado para disponibilidade e baixa latência global.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;MongoDB&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;A&lt;/strong&gt; (se configurado com &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w=1&lt;/code&gt;) ou &lt;strong&gt;C&lt;/strong&gt; (com majority write concern)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;L&lt;/strong&gt; (eventual consistency em réplicas secundárias)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;PA/EL&lt;/strong&gt; ou &lt;strong&gt;PC/EL&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Flexível; o trade-off depende do write concern e read concern.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Google Spanner&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (consistência forte global)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (mesmo sem partição, prioriza consistência)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;PC/EC&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Usa TrueTime para garantir consistência serializável global, com custo de latência.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Azure Cosmos DB&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;A&lt;/strong&gt; (disponibilidade)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;L/C&lt;/strong&gt; (configurável: eventual, bounded staleness, session, consistent prefix, strong)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;PA/ELC&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Oferece 5 níveis de consistência configuráveis.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Apache Kafka&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;A&lt;/strong&gt; (disponibilidade)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;L&lt;/strong&gt; (prioriza throughput e baixa latência)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;PA/EL&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Garantias de consistência são fracas; foco em disponibilidade e velocidade.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Etcd&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (consistência forte)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (consistência forte)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;PC/EC&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Voltado para consistência forte, usado em sistemas críticos de coordenação.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;ZooKeeper&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (consistência forte)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (consistência forte)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;PC/EC&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Voltado para consistência forte, usado em sistemas críticos de coordenação.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;CockroachDB&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (prioriza consistência em partições)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (consistência forte via consenso Raft)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;PC/EC&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Inspirado no Spanner, mantém consistência global em troca de latência mais alta.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Redis em Cluster Mode&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;A&lt;/strong&gt; (disponibilidade, pode perder dados em falhas)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;L&lt;/strong&gt; (baixa latência com replicação assíncrona)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;PA/EL&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Focado em velocidade; consistência forte não é garantida em partições ou failover.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Amazon RDS (Multi-AZ)&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (replicação síncrona entre zonas, prioriza consistência)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; (dados consistentes entre réplicas antes de confirmar)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;PC/EC&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;Designado para workloads transacionais, garantindo consistência e durabilidade.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;h3 id=&quot;referências&quot;&gt;Referências&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cs.umd.edu/~abadi/papers/abadi-pacelc.pdf&quot;&gt;Consistency Tradeoffs in Modern Distributed Database System Design&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/PACELC_design_principle&quot;&gt;PACELC design principle&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://emergingcode.substack.com/p/pacelc-a-extensao-do-teorema-cap&quot;&gt;PACELC: A extensão do Teorema CAP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.scylladb.com/glossary/pacelc-theorem/&quot;&gt;PACELC Theorem&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://medium.com/distributed-systems-series/pacelc-theorem-explained-distributed-systems-series-9c509febb8f8&quot;&gt;PACELC Theorem Explained: Distributed Systems Series&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.designgurus.io/blog/system-design-interview-basics-cap-vs-pacelc&quot;&gt;System Design Interview Basics: CAP vs. PACELC&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/operating-systems/pacelc-theorem/&quot;&gt;PACELC Theorem&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ritesh-kapoor.medium.com/pacelc-theorem-and-distributed-databases-301d971deda3&quot;&gt;PACELC Theorem &amp;amp; Distributed Databases&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.alexdebrie.com/posts/dynamodb-eventual-consistency/&quot;&gt;Understanding Eventual Consistency in DynamoDB&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 14 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://fidelissauro.dev/pacelc/</link>
        <guid isPermaLink="true">https://fidelissauro.dev/pacelc/</guid>
        
        
        <category>system-design</category>
        
        <category>engineering</category>
        
        <category>cloud</category>
        
      </item>
    
  </channel>
</rss>
