3.3 Sistemas distribuídos
Visão geral e motivação
Um sistema distribuído é qualquer sistema cujos componentes rodam em mais de uma máquina e se coordenam por uma rede. No momento em que você cruza uma fronteira de processo por uma rede, herda algumas verdades duras que simplesmente não existem dentro de um único processo. A rede não é confiável, e sua latência varia. As mensagens podem ser perdidas, duplicadas, atrasadas ou reordenadas. Os componentes remotos falham por conta própria. Não existe relógio compartilhado. As clássicas “falácias da computação distribuída” (a rede é confiável, a latência é zero, a largura de banda é infinita, a topologia nunca muda) nomeiam exatamente as premissas que causam quedas. Seu trabalho é projetar para essas realidades desde o início, em vez de redescobri-las durante um incidente.
Para uma grande organização, a distribuição não é opcional. Qualquer sistema que atenda escala nacional ou global, junte vários departamentos ou precise de alta disponibilidade se estenderá por muitas máquinas, centros de dados e muitas vezes regiões. As empresas operam sistemas de transações distribuídas, pipelines de eventos e implantações multirregionais. Os governos operam integrações entre agências em que cada agência é dona dos próprios sistemas e ninguém controla o todo. Aqui a distância entre um design robusto e um frágil aparece como quedas de manchete, pagamentos de benefícios perdidos e consequências regulatórias. As técnicas deste capítulo (raciocínio de consistência, idempotência, novas tentativas com recuo, disjuntores, sagas e observabilidade distribuída) são as suas defesas padrão.
A parte mais difícil dos sistemas distribuídos é que as falhas são parciais e intermitentes. Um programa de uma só máquina ou funciona ou trava. Um sistema distribuído pode estar meio funcionando: algumas requisições têm sucesso, outras esgotam o tempo e outras se perdem em silêncio, tudo ao mesmo tempo. Este capítulo se concentra no raciocínio e nos padrões que permitem a uma grande equipe construir sistemas que se degradam com elegância e continuam compreensíveis sob falha parcial.
Princípios fundamentais
- A rede não é confiável. Projete toda interação remota presumindo que ela pode ser lenta, falhar, duplicar ou reordenar.
- Você não pode ter consistência perfeita e disponibilidade perfeita durante uma partição. Escolha deliberadamente por interação (CAP/PACELC) e lembre que a latência é um custo mesmo quando não há partição.
- Torne as operações idempotentes. Se uma operação pode ser repetida com segurança, a maior parte do tratamento de falhas distribuídas se torna tratável.
- Toda chamada remota precisa de um tempo limite. Esperas sem limite transformam uma dependência lenta numa queda de todo o sistema.
- Prefira a consistência eventual onde o negócio permite, mas torne-a explícita. Usuários e auditores precisam entender quando podem ver dados obsoletos.
- Isole as falhas. Anteparas e disjuntores impedem que um componente que falha se propague para todos.
- Você não consegue depurar o que não consegue ver. Os fluxos distribuídos exigem rastreamento, métricas e logs correlacionados em todos os saltos.
- A “entrega exatamente uma vez” é um mito. O processamento exatamente uma vez é uma conquista de engenharia. Projete para pelo-menos-uma-vez com deduplicação.
Recomendações
Raciocine sobre consistência com CAP e PACELC
O teorema CAP diz que, durante uma partição de rede, um sistema precisa escolher entre consistência (toda leitura vê a última escrita) e disponibilidade (toda requisição recebe uma resposta). O PACELC acrescenta uma segunda troca: Else (quando não há partição) você ainda troca Latência por Consistência. Não carimbe isso como um rótulo do sistema inteiro. Decida por operação. Uma transferência de saldo bancário precisa de consistência forte e se recusará em vez de arriscar um gasto duplo. Um feed social ou um contador de visualizações de produto pode aceitar obsolescência em troca de disponibilidade e velocidade. Escreva qual modelo de consistência cada fluxo de dados usa (forte, causal, ler-suas-escritas ou eventual) para que ninguém presuma uma garantia que o sistema na verdade não oferece.
Construa juntas a idempotência, os tempos limite, as novas tentativas e o recuo
Trate essas quatro técnicas como um pacote. Dê a toda operação remota um tempo limite, para que uma dependência travada não bloqueie uma thread para sempre. Em caso de falha, tente de novo, mas apenas para operações seguras de repetir. Seguras de repetir significa idempotentes: atribua a cada requisição uma chave única e faça o receptor deduplicar, de modo que uma “cobrar cartão” repetida não cobre duas vezes. Espace as novas tentativas com recuo exponencial e jitter, para evitar uma tempestade de novas tentativas sincronizadas que transforma um breve soluço num ataque de negação de serviço autoinfligido. Limite o número de novas tentativas e o orçamento total de tempo, porque tentar para sempre apenas desloca a falha. Sem idempotência, as novas tentativas são perigosas. Sem recuo, são destrutivas.
Acrescente disjuntores e anteparas para parar as cascatas
Um disjuntor observa as chamadas a uma dependência e, depois de um limite de falhas, “abre”: falha rápido durante um período de resfriamento em vez de empilhar mais requisições sobre um serviço em dificuldade, depois “meio-abre” para testar a recuperação. Isso para a cascata em que um serviço a jusante lento esgota as threads de todos os chamadores até o sistema inteiro travar. As anteparas particionam recursos (conjuntos de threads, conjuntos de conexões) para que a saturação numa dependência não consuma a capacidade de que as outras precisam. Combine ambos com a degradação graciosa: quando uma dependência não crítica está indisponível, devolva respostas em cache ou padrão em vez de falhar a requisição inteira.
Gerencie transações distribuídas com sagas, não com commit em duas fases
Em geral você não consegue manter uma única transação ACID (Atomicidade, Consistência, Isolamento, Durabilidade) entre vários serviços ou bancos de dados. O commit em duas fases distribuído é lento, trava recursos e corta a disponibilidade. Use em vez disso o padrão saga. Modele uma transação de negócio como uma sequência de transações locais, cada uma publicando um evento que dispara a seguinte, com uma ação compensatória para cada etapa que a desfaz se uma etapa posterior falhar. As sagas vêm em dois sabores. A coreografia faz os serviços reagirem aos eventos uns dos outros, sem controlador central. A orquestração faz um coordenador central conduzir as etapas, o que é mais fácil de raciocinar e de monitorar. As sagas abraçam a consistência eventual: o sistema passa por estados intermediários e depois converge. Então projete a experiência do usuário e a trilha de auditoria para dar conta dos estados “em andamento” e “compensado”.
Trate o exatamente-uma-vez como pelo-menos-uma-vez mais deduplicação
Os corretores de mensagens não conseguem garantir de verdade a entrega exatamente uma vez diante de falhas. O que eles e você conseguem alcançar é a entrega pelo-menos-uma-vez com processamento idempotente, que produz efeitos exatamente uma vez. Projete os consumidores para tratar mensagens duplicadas com segurança, usando chaves de idempotência ou um log de mensagens processadas. Conheça com precisão as garantias de ordenação e de entrega do seu corretor. Para fluxos contínuos, use grupos de consumidores, partições e gestão de deslocamentos de propósito e torne seguro o reprocessamento, para poder reexecutar um fluxo depois de uma correção de bug sem corromper o estado a jusante.
Instrumente os fluxos distribuídos de ponta a ponta
Adote os três pilares da observabilidade, correlacionados entre fronteiras de serviço. Propague um ID de rastreamento/correlação por todo salto, para poder seguir uma única requisição de usuário por todos os serviços que ela toca (rastreamento distribuído). Emita métricas estruturadas (percentis de latência, taxas de erro, saturação, vazão) por serviço e por dependência. Emita logs estruturados que carreguem o ID de correlação. Use tudo isso para definir objetivos de nível de serviço e para alertar sobre sintomas que os usuários de fato sentem, como a taxa de erro e a latência, e não apenas sobre a saúde de máquinas individuais. Num sistema distribuído, a observabilidade não é uma ferramenta opcional. É a única forma de entender o comportamento sob falha parcial.
Compromissos: prós e contras
| Técnica | Prós | Contras / custo |
|---|---|---|
| Consistência forte | Modelo mental simples, sem leituras obsoletas | Menor disponibilidade durante partições, maior latência, custo de coordenação |
| Consistência eventual | Alta disponibilidade, baixa latência, escalável | Leituras obsoletas, raciocínio complexo, exige resolução de conflitos |
| Novas tentativas com recuo | Atravessam falhas transitórias automaticamente | Amplificam a carga se mal usadas. Exigem idempotência e tetos |
| Disjuntores / anteparas | Previnem falhas em cascata, falham rápido | Complexidade adicional, ajuste de limites, risco de disparo prematuro |
| Saga (versus 2PC) | Escalável, disponível, sem travas distribuídas | Consistência eventual, lógica de compensação, mais difícil de raciocinar |
A grande troca é entre coordenação e independência. Toda garantia que você quer entre máquinas (consistência, ordenação, exatamente-uma-vez) custa latência, disponibilidade ou complexidade. Ela exige que as máquinas concordem, e concordar por uma rede não confiável é caro. A habilidade é comprar apenas as garantias de que o negócio realmente precisa, operação por operação, e projetar todo o resto para a degradação graciosa. Compre consistência demais e seus sistemas ficam lentos e frágeis. Compre de menos e você obtém corrupção silenciosa de dados que surge como uma falha de auditoria meses depois.
Perguntas para discutir com sua equipe
Seus padrões de resiliência são entregues como padrões compartilhados da plataforma, ou cada equipe reinventa tempos limite e novas tentativas? O capítulo trata a idempotência, os tempos limite, as novas tentativas limitadas, os disjuntores e o rastreamento como mais baratos e confiáveis quando construídos uma só vez em bibliotecas compartilhadas e padrões da plataforma. Numa grande organização, deixar cada equipe criá-los à mão garante a inconsistência: alguns caminhos repetem operações não idempotentes, alguns não têm tempo limite, alguns não emitem ID de correlação. Leve evidências auditando uma amostra de serviços e contando quantos definem um tempo limite explícito em toda chamada remota e propagam um ID de rastreamento de ponta a ponta. Se esse número é baixo, a correção é um investimento de plataforma, não um memorando de treinamento. Os padrões comuns também tornam a resiliência testável e auditável, o que os reguladores de finanças e de governo esperam cada vez mais que vocês demonstrem.
Seus tempos limite e orçamentos de novas tentativas se compõem ao longo de toda a cadeia de chamadas, ou uma requisição profunda se repete até virar uma queda? Uma única requisição costuma cruzar muitos saltos, e se cada camada tenta de novo três vezes por conta própria, com o seu próprio tempo limite, a falha mais interna se multiplica e o chamador externo espera muito além de qualquer limite tolerável a um humano. Defina um orçamento total de tempo para a requisição voltada ao usuário e divida-o ao longo da cadeia, para que um serviço interno saiba quão pouco tempo lhe resta e falhe rápido em vez de repetir até uma tempestade. Leve seu grafo de dependências e um rastreamento real, depois some a combinação do pior caso de tempo limite e novas tentativas e compare com o que o usuário de fato esperará. O recuo exponencial com jitter e um teto de tentativas totais impede que um breve soluço vire um ataque de negação de serviço autoinfligido. As cadeias síncronas profundas e tagarelas são o inimigo aqui, então a resposta pode empurrar vocês para fluxos assíncronos ou menos saltos.
Quando foi a última vez que vocês injetaram as falhas que o seu design afirma sobreviver, e o que quebrou que vocês não esperavam? Os padrões de resiliência são hipóteses até você fazer o sistema falhar de propósito: derrubar uma instância, acrescentar latência a uma dependência, perder uma fração das mensagens, reentregar um lote duas vezes. Num sistema distribuído as falhas interessantes são parciais e intermitentes, então um disjuntor ou uma compensação de saga que parece correto no código ainda pode se comportar mal sob um tempo-limite-que-talvez-tenha-se-completado real. Leve os resultados de um dia de simulação ou de uma injeção de falhas de verdade, não um documento de design, e anote quais alertas dispararam, quanto tempo o rastreamento levou para localizar a falha e se se formou alguma tempestade de novas tentativas. Em setores regulamentados, a evidência de que vocês testaram a falha faz parte de demonstrar a resiliência operacional aos auditores. Se vocês nunca fizeram um, o primeiro experimento pertence a um ambiente de teste com raio de impacto pequeno e uma chave de abortar.
Para cada fluxo de dados importante, a equipe responsável consegue nomear o modelo de consistência que ele oferece, e essa escolha corresponde ao que o negócio de fato precisa? O CAP e o PACELC forçam uma escolha deliberada por operação, mas numa grande organização o padrão é a deriva: um fluxo que começou eventualmente consistente para um contador de baixo risco é reaproveitado para algo que agora aprova pagamentos ou concede acesso, e ninguém reexamina a garantia. As considerações concorrentes são reais, já que a consistência forte custa disponibilidade durante uma partição e latência mesmo quando não há nenhuma, enquanto a consistência eventual compra velocidade ao preço de leituras obsoletas e de uma resolução de conflitos que vocês precisam projetar. Leve um catálogo dos seus principais fluxos de dados, cada um rotulado com o seu modelo atual (forte, causal, ler-suas-escritas ou eventual) e a consequência de negócio de uma leitura obsoleta ou perdida, e procure descompassos em que a garantia é mais forte ou mais fraca do que o risco justifica. Em finanças corporativas e em sistemas governamentais de benefícios ou de identidade, uma leitura eventualmente consistente por trás de uma decisão autoritativa é o tipo de defeito silencioso que surge como uma constatação de auditoria ou uma negativa indevida meses depois, então a própria revisão é evidência que os auditores pedirão para ver.
Como as suas transações de negócio entre vários serviços se comportam na metade do caminho, e quem é responsável pelas compensações que as desfazem? Substituir o commit em duas fases por sagas significa que o sistema passa por estados intermediários visíveis, e uma etapa pode ter sucesso enquanto uma etapa posterior falha e dispara uma ação compensatória que a reverte. Para uma equipe grande isso levanta difíceis questões de responsabilidade: a cadeia autorizar-debitar-creditar-lançar costuma cruzar várias equipes, e uma compensação que uma equipe esquece de implementar deixa dinheiro ou registros permanentemente inconsistentes. Pesem a coreografia, em que os serviços reagem aos eventos uns dos outros sem controlador central e o fluxo é difícil de ver, contra a orquestração, em que um coordenador conduz e monitora as etapas ao custo de um componente a operar. Levem o diagrama de estados da sua saga mais importante, a lista de ações compensatórias e seus responsáveis e a evidência de que os estados “em andamento” e “compensado” são tratados tanto na experiência do usuário quanto na trilha de auditoria. No setor bancário e na gestão de casos do setor público, os reguladores esperam que vocês reconstruam exatamente o que aconteceu com uma transação que falhou no meio, então um estado intermediário não modelado é uma lacuna de conformidade, não apenas um bug.
Seus consumidores de mensagens sobrevivem à entrega duplicada e reordenada, e vocês conseguem provar isso antes que o corretor force a questão? A entrega exatamente uma vez é um mito, então a sua garantia real é pelo-menos-uma-vez, e um consumidor que presume que cada mensagem chega uma vez e em ordem processará em dobro no dia em que o corretor reentregar um lote depois de um failover. Entre muitas equipes o risco se acumula, porque um consumidor não idempotente num fluxo compartilhado pode corromper o estado a jusante de que outras equipes dependem, e a falha é invisível até a reexecução ou uma partição reordenar os eventos. A troca é o custo de engenharia de chaves de idempotência, de um log de mensagens processadas e de gestão explícita de deslocamentos e partições, contra o custo da corrupção silenciosa. Levem a lista de consumidores dos seus fluxos críticos, anotem quais deduplicam e quais apenas esperam e levem o resultado de um teste real de reentrega ou de reexecução em vez de uma garantia de que deveria dar certo. Para a troca de dados entre agências governamentais e para os pipelines de eventos corporativos, a capacidade de reexecutar um fluxo com segurança depois de uma correção de bug, sem criar casos ou cobranças duplicados, é ao mesmo tempo uma necessidade operacional e algo que os auditores vão querer ver demonstrado.
Perspectiva por setor
Startup. Com duas ou três partes móveis e sem equipe de plataforma, resista a construir maquinário distribuído que você não consegue manter. Compre resiliência onde ela mora, no SDK que o seu provedor de pagamentos ou de mensageria já oferece, e gaste a sua escassa atenção nos dois padrões que previnem dano irreversível: uma chave de idempotência em toda chamada que move dinheiro ou altera uma conta e um tempo limite com nova tentativa limitada para que uma conexão instável jamais aja em dobro. Mantenha pequeno o número de saltos de rede, porque toda dependência síncrona que você acrescenta é mais uma coisa que pode falhar antes que você tenha alguém de sobreaviso para notar.
Pequena empresa. Provavelmente você não tem especialista em sistemas distribuídos e tem orçamento apertado, então trate isto como uma questão de comprar e não de construir: prefira filas gerenciadas, bancos de dados gerenciados e plataformas que cuidam por você de novas tentativas, ordenação e deduplicação em vez de infraestrutura que você precise operar. Enquadre o seu risco em termos simples, sabendo quais operações prejudicariam um cliente se rodassem duas vezes ou devolvessem dados obsoletos, e ligue os recursos de idempotência e de pelo-menos-uma-vez que os seus fornecedores já oferecem. Evite costurar serviços por um banco de dados compartilhado para simular uma transação, pois isso recria em silêncio o problema distribuído mais difícil sem nenhuma das ferramentas para geri-lo.
Grande empresa. O problema central é a consistência entre muitas equipes, então entregue idempotência, tempos limite, novas tentativas limitadas, disjuntores e rastreamento correlacionado como padrões compartilhados da plataforma em vez de deixar cada grupo criá-los à mão. Padronize como os modelos de consistência e as garantias de entrega são declarados por fluxo, conduza injeção de falhas e dias de simulação numa cadência regular e faça os orçamentos totais de tempo se comporem ao longo de cadeias de chamadas profundas para que nenhum serviço possa repetir a plataforma até uma queda. Gerencie a resiliência como uma capacidade medida, com objetivos de nível de serviço sobre sintomas visíveis ao usuário, porque na sua escala um único tempo limite ausente pode se propagar até uma queda de manchete.
Governo. Os sistemas entre agências significam que ninguém é dono do todo, então projete para fronteiras que você não controla: filas duráveis com entrega pelo-menos-uma-vez, deduplicação sobre um ID de mensagem estável e IDs de correlação que fluam através das linhas das agências para dar aos auditores um rastreamento de ponta a ponta. As regras de contratação e de transparência empurram você a documentar o modelo de consistência e a garantia de entrega de cada integração e a manter as decisões autoritativas (identidade, elegibilidade, benefício) em leituras fortemente consistentes e não em endpoints em cache. Trate a evidência de falha testada e o histórico reconstruível das transações como entregáveis, já que a resiliência operacional e a prestação de contas ao público são obrigações contratuais e estatutárias, não requintes internos.
Exemplos
Startup. Uma pequena fintech tem apenas duas partes móveis que conversam pela rede: seu aplicativo e um provedor de pagamentos de terceiros. Mesmo nesse tamanho, faz toda requisição de cobrança carregar uma chave de idempotência e envolve a chamada numa nova tentativa com recuo, de modo que uma resposta perdida numa conexão instável jamais cobre um cliente em dobro. Pular isso parece barato no primeiro dia, mas a primeira cobrança duplicada que atinge um usuário real custa um incêndio de suporte, um reembolso e um arranhão na confiança que a jovem empresa não pode se permitir.
Grande empresa. Uma plataforma global de transporte por aplicativo processa os pagamentos das corridas por uma saga: autorizar cartão, debitar passageiro, creditar motorista, registrar o lançamento contábil, cada um uma transação local com um estorno compensatório. Cada etapa carrega uma chave de idempotência, de modo que as novas tentativas depois de um tempo limite de rede nunca cobram em dobro. As chamadas ao serviço de pontuação de fraude ficam atrás de um disjuntor. Quando ele se degrada no pico, o disjuntor abre e as corridas recorrem a uma pontuação conservadora em vez de bloquear todas as corridas. Quando um cliente contesta uma corrida, o rastreamento distribuído permite que os engenheiros a sigam por uma dúzia de serviços em segundos.
Governo. Um serviço nacional de identidade é usado por muitas agências para verificação. Ele oferece uma leitura fortemente consistente para consultas autoritativas de status (você não deve aprovar um benefício contra dados de identidade obsoletos), mais um endpoint eventualmente consistente e em cache para consultas de alto volume e não críticas. A troca de dados entre agências corre por uma fila durável com entrega pelo-menos-uma-vez, e o consumidor de cada agência deduplica por um ID de mensagem, de modo que um registro reentregue não cria um caso duplicado. Os IDs de correlação fluem através das fronteiras das agências, dando aos auditores um rastreamento de ponta a ponta de como os dados de um cidadão se moveram entre departamentos.
Justificativa de negócio: motivações, ROI e TCO
A disciplina de sistemas distribuídos é comprada barato e sua ausência é paga de forma catastrófica. O custo de adoção é o tempo de engenharia para construir idempotência, tempos limite, novas tentativas, disjuntores e rastreamento em bibliotecas compartilhadas e padrões da plataforma. É um investimento modesto, em grande parte único, que depois beneficia todas as equipes. O custo de não adotá-la se mede em grandes quedas: um único tempo limite ausente que se propaga até uma queda total da plataforma, um caminho de pagamento não idempotente que cobra em dobro milhares de clientes ou uma transação distribuída sem saga que deixa os dados permanentemente inconsistentes. Cada um é um incidente de manchete com custo direto de receita, de remediação e de reputação e, em setores regulamentados, multas.
Enquadre o caso para a liderança em torno de disponibilidade e raio de impacto. Os padrões de resiliência reduzem diretamente tanto a frequência quanto a duração dos incidentes graves, as métricas que os executivos já acompanham como tempo de atividade e tempo médio de recuperação. A observabilidade distribuída é a maior alavanca isolada sobre o MTTR: as equipes com rastreamento correlacionado resolvem incidentes entre serviços numa fração do tempo. Como essas capacidades são melhor entregues como padrões compartilhados da plataforma, o custo marginal por equipe é baixo e o retorno em toda a organização se acumula. O argumento do TCO é simples: construir a resiliência desde o início custa uma fração do que custa adaptá-la depois da queda que força a questão.
Antipadrões e armadilhas
- Sem tempos limite. Uma única dependência travada esgota todas as threads e derruba o sistema inteiro.
- Repetir operações não idempotentes. Efeitos colaterais duplicados: cobranças em dobro, registros duplicados, e-mails dobrados.
- Tempestades de novas tentativas. Novas tentativas sincronizadas sem recuo e jitter que amplificam um pequeno soluço até uma queda.
- Presumir a entrega exatamente uma vez. Construir consumidores que quebram diante das mensagens duplicadas que o corretor acabará entregando.
- Transações distribuídas por banco de dados compartilhado. Acoplar serviços por um só banco para simular o ACID, recriando um monólito distribuído.
- Ignorar a falha parcial. Código que presume que uma chamada remota ou tem sucesso total ou falha total, sem tratamento para “esgotou o tempo mas talvez tenha se completado”.
- Sem IDs de correlação. Depurar um incidente entre serviços dando grep em logs sem relação em dez máquinas.
- Cadeias de chamadas síncronas tagarelas. Grafos de dependências síncronas profundos em que qualquer salto lento trava a requisição inteira.
Modelo de maturidade
- Nível 1: Iniciar. As chamadas remotas são tratadas como chamadas locais. Os tempos limite são ausentes ou ingênuos, as novas tentativas são inexistentes ou imprudentes e as falhas se propagam pelo sistema. Não há visão compartilhada de consistência nem de entrega, e depurar um incidente entre serviços significa vasculhar logs máquina a máquina depois do fato.
- Nível 2: Desenvolver. Algumas equipes acrescentam tempos limite, novas tentativas básicas e um pouco de idempotência, mas as práticas são inconsistentes de serviço para serviço. Os logs são centralizados mas não correlacionados, então rastrear uma requisição entre saltos é manual. As transações distribuídas são esperadas funcionar em vez de modeladas, e as garantias de consistência vivem nas cabeças dos engenheiros individuais.
- Nível 3: Padronizar. Idempotência, novas tentativas limitadas, recuo com jitter, disjuntores e anteparas são padrão em toda a organização por meio de bibliotecas compartilhadas. As sagas com ações compensatórias tratam as transações entre vários serviços, o rastreamento distribuído com IDs de correlação está implantado e todo fluxo de dados importante documenta seu modelo de consistência e sua garantia de entrega. As regras são escritas e impostas em toda a organização em vez de deixadas a cada equipe.
- Nível 4: Gerenciar. A resiliência é medida em relação a linhas de base, não apenas presente. Você acompanha a taxa de erros, os percentis de latência, a saturação e a vazão por serviço e dependência, observa as razões de novas tentativas e as taxas de abertura de disjuntores e define objetivos de nível de serviço sobre sintomas visíveis ao usuário. Os orçamentos totais de tempo são verificados quanto a se compor ao longo das cadeias de chamadas, o tempo médio de recuperação de incidentes entre serviços é uma métrica monitorada e os resultados de injeção de falhas e de dias de simulação alimentam os números que servem de portão para cada mudança.
- Nível 5: Orquestrar. A resiliência é o padrão da plataforma continuamente melhorado, integrado em toda a organização e adaptativo às condições. A injeção de falhas roda rotineiramente em produção com raios de impacto pequenos, os sistemas se degradam com elegância por design e as escolhas de consistência e de entrega são revisitadas conforme a carga e o risco de negócio mudam. As métricas do Nível 4 conduzem respostas automatizadas e uma evolução arquitetural constante, de modo que o parque distribuído fica mais robusto a cada incidente em vez de apenas sobreviver a ele.
Ideias para discussão
- Quais das suas operações críticas são genuinamente idempotentes hoje, e quais, em silêncio, não são?
- Para cada fluxo de dados importante, a sua equipe consegue declarar de memória o modelo de consistência e a garantia de entrega?
- Onde um disjuntor teria evitado a sua última queda em cascata?
- Quanto tempo leva hoje para rastrear uma única requisição que falha por todos os serviços que ela toca?
- Quais das suas “transações distribuídas” dependem na verdade de sorte, e quais são verdadeiras sagas com compensações?
- Se o seu corretor de mensagens reentregasse toda mensagem duas vezes por uma hora, o que quebraria?
Principais conclusões
- Presuma que a rede não é confiável e que as falhas são parciais. Projete toda interação remota para lentidão, perda, duplicação e reordenação.
- Decida consistência versus disponibilidade por operação usando CAP/PACELC. Documente o modelo que cada fluxo oferece.
- Idempotência, tempos limite, novas tentativas limitadas e recuo com jitter são um pacote. Nunca adote novas tentativas sem as outras três.
- Disjuntores e anteparas contêm falhas. Sagas com compensações substituem transações distribuídas inviáveis.
- Trate a entrega como pelo-menos-uma-vez e torne idempotente o processamento para obter efeitos exatamente uma vez.
- Rastreamento, métricas e logs correlacionados são a única forma de entender e operar fluxos distribuídos.
Referências e leitura complementar
- Martin Kleppmann, Designing Data-Intensive Applications
- Andrew Tanenbaum and Maarten van Steen, Distributed Systems: Principles and Paradigms
- Michael Nygard, Release It!: Design and Deploy Production-Ready Software
- Sam Newman, Building Microservices
- Chris Richardson, Microservices Patterns (sagas, transactional messaging)
- Eric Brewer, “CAP Twelve Years Later” and Daniel Abadi on PACELC
- Leslie Lamport, “Time, Clocks, and the Ordering of Events in a Distributed System”
- Cindy Sridharan, Distributed Systems Observability
- Nassim Nicholas Taleb’s notion of antifragility (as applied by resilience-engineering literature)