O Padrão de Resiliência de Aplicações "Circuit Breaker"
Prefácio Não há muito tempo atrás, quando a fiação elétrica estava sendo construída nas residências, muitas pessoas foram vítimas de choques elétricos. Os azarados ligavam muitos aparelhos em seu circuito e cada aparelho consumia uma certa quantidade de corrente elétrica. Quando a corrente é resistida, ela produz calor proporcional ao quadrado da corrente vezes a resistência (I²R). Como eles não tinham fiação doméstica supercondutora, esse acoplamento oculto entre aparelhos eletrônicos fazia com que os fios nas paredes ficassem quentes, às vezes quentes o suficiente para pegar fogo. Puft. Não há mais casa. A incipiente indústria de energia encontrou uma solução parcial para o problema do aquecimento resistivo, na forma de fusíveis. Todo o propósito de um fusível elétrico é queimar antes da casa. É um componente projetado para falhar primeiro, controlando assim um modo de falha geral. Este dispositivo brilhante funcionou bem, exceto por dois problemas. Primeiro, um fusível é um item descartável de uso único; portanto, é possível que eles se egotem. Em segundo lugar, os fusíveis residenciais (nos Estados Unidos) tinham aproximadamente o mesmo diâmetro das moedas de cobre. Juntas, essas duas condições levaram muitas pessoas a realizar experimentos com fusíveis caseiros de alta corrente e baixa resistência (ou seja, um disco de cobre de 3/4"). Puft. Não há mais casa. Os fusíveis residenciais seguiram o caminho do telefone a disco. Agora, os disjuntores protegem os caçadores de engenhocas ansiosos para queimarem suas casas. O princípio é o mesmo: detecta o uso excessivo, falha primeiro, e abre o circuito. Mais abstratamente, o disjuntor existe para permitir que um subsistema (um circuito elétrico) falhe (consumo excessivo de corrente, possivelmente devido a um curto-circuito) sem destruir todo o sistema (a casa). Além disso, uma vez que o perigo tenha passado, o disjuntor pode ser reiniciado para restaurar a função completa do sistema. (NYGARD, 2007) NYGARD, Michael T. Circuit Breaker. In: RELEASE It! Design and Deploy Production-Ready Software. 1. ed. Raleigh, North Carolina Dallas, Texas, EUA: The Pragmatic Bookshelf, 2007. cap. 5.2, p. 115. ISBN 978-0-9787392-1-8. É comum que sistemas de software façam chamadas remotas para aplicações que estejam sendo executadas em diferentes processos, ou até mesmo, em diferentes máquinas atráves da rede. Uma das principais diferenças entre chamadas locais e chamadas remotas é que as remotas podem apresentar falhas de infraestrutura ou travar, sem retornar uma resposta até que um tempo limite seja atingido. Ou pior, se houver muitas chamadas a um serviço que não responde, pode ocorrer o esgotamento de recursos críticos, levando a falhas em cascata em vários sistemas. Em seu excelente livro Release It, Michael Nygard popularizou o padrão Circuit Breaker - "Disjuntor" - para evitar esse tipo de cascata catastrófica. (MARTIN, 2014) CircuitBreaker. [S. l.], 6 mar. 2014. Acesso em: 2 out. 2022. Introdução Um disjuntor protege um circuito elétrico para evitar que sobrecargas de corrente passem por este dispositivo de proteção e cause danos aos dispositivos, aparelhos e fiação (ou, até mesmo, pessoas) ligados ao circuito protegido. O disjuntor funciona como uma chave automática que, quando armado, fecha o circuito, permitindo a passagem de corrente elétrica através dele. Enquanto o circuito estiver fechado, a corrente elétrica passa pelo disjuntor, que "monitora" a intensidade de corrente que passa por ele. Se essa corrente elétrica se eleva além do limite de operação segura deste disjuntor, ele se desarma automaticamente, abrindo o circuito para impedir a passagem da corrente, protegendo todo o circuito protegido por ele. Assim como em um circuito elétrico, falhas de funcionamento em softwares também trazem riscos: operacionais, físicos, ao patrimônio, pessoais e, até mesmo, em alguns casos, risco de morte. A falha em uma operação de software pode resultar em operações parcialmente executadas, ou impedir a realização de transações por completo, a depender da implementação e dos requisitos funcionais do software. Quando uma operação falha, vários fatores podem ter influenciado esse resultado negativo, a falha pode ser consequência de uma instabilidade momentânea, indisponibilidade de um dos recursos da cadeia de dependências do sistema, ou, até mesmo, uma falha permanente que depende de intervenção para sua correção. Se uma operação falha, significa que algo deixou de ser feito, e provavelmente receberá uma retentativa em algum momento próximo, seja de forma automática ou por uma nova solicitação do usuário. O problema é que além do funcionamento da operação estar comprometido, cada nova chamada à operação consome recursos, sem garantias de que uma retentativa será suficiente para a conclusão exitosa da operação. Além disso, a causa raíz que motiva a falha pode não estar diretamente relacionada ao serviço da operação, mas em uma camada mais profun

Prefácio
Não há muito tempo atrás, quando a fiação elétrica estava sendo construída nas residências, muitas pessoas foram vítimas de choques elétricos. Os azarados ligavam muitos aparelhos em seu circuito e cada aparelho consumia uma certa quantidade de corrente elétrica. Quando a corrente é resistida, ela produz calor proporcional ao quadrado da corrente vezes a resistência (I²R). Como eles não tinham fiação doméstica supercondutora, esse acoplamento oculto entre aparelhos eletrônicos fazia com que os fios nas paredes ficassem quentes, às vezes quentes o suficiente para pegar fogo. Puft. Não há mais casa.
A incipiente indústria de energia encontrou uma solução parcial para o problema do aquecimento resistivo, na forma de fusíveis. Todo o propósito de um fusível elétrico é queimar antes da casa. É um componente projetado para falhar primeiro, controlando assim um modo de falha geral. Este dispositivo brilhante funcionou bem, exceto por dois problemas. Primeiro, um fusível é um item descartável de uso único; portanto, é possível que eles se egotem. Em segundo lugar, os fusíveis residenciais (nos Estados Unidos) tinham aproximadamente o mesmo diâmetro das moedas de cobre. Juntas, essas duas condições levaram muitas pessoas a realizar experimentos com fusíveis caseiros de alta corrente e baixa resistência (ou seja, um disco de cobre de 3/4"). Puft. Não há mais casa.
Os fusíveis residenciais seguiram o caminho do telefone a disco. Agora, os disjuntores protegem os caçadores de engenhocas ansiosos para queimarem suas casas. O princípio é o mesmo: detecta o uso excessivo, falha primeiro, e abre o circuito. Mais abstratamente, o disjuntor existe para permitir que um subsistema (um circuito elétrico) falhe (consumo excessivo de corrente, possivelmente devido a um curto-circuito) sem destruir todo o sistema (a casa). Além disso, uma vez que o perigo tenha passado, o disjuntor pode ser reiniciado para restaurar a função completa do sistema. (NYGARD, 2007)
NYGARD, Michael T. Circuit Breaker. In: RELEASE It! Design and Deploy Production-Ready Software. 1. ed. Raleigh, North Carolina Dallas, Texas, EUA: The Pragmatic Bookshelf, 2007. cap. 5.2, p. 115. ISBN 978-0-9787392-1-8.
É comum que sistemas de software façam chamadas remotas para aplicações que estejam sendo executadas em diferentes processos, ou até mesmo, em diferentes máquinas atráves da rede. Uma das principais diferenças entre chamadas locais e chamadas remotas é que as remotas podem apresentar falhas de infraestrutura ou travar, sem retornar uma resposta até que um tempo limite seja atingido. Ou pior, se houver muitas chamadas a um serviço que não responde, pode ocorrer o esgotamento de recursos críticos, levando a falhas em cascata em vários sistemas. Em seu excelente livro Release It, Michael Nygard popularizou o padrão Circuit Breaker - "Disjuntor" - para evitar esse tipo de cascata catastrófica. (MARTIN, 2014)
CircuitBreaker. [S. l.], 6 mar. 2014. Acesso em: 2 out. 2022.
Introdução
Um disjuntor protege um circuito elétrico para evitar que sobrecargas de corrente passem por este dispositivo de proteção e cause danos aos dispositivos, aparelhos e fiação (ou, até mesmo, pessoas) ligados ao circuito protegido. O disjuntor funciona como uma chave automática que, quando armado, fecha o circuito, permitindo a passagem de corrente elétrica através dele. Enquanto o circuito estiver fechado, a corrente elétrica passa pelo disjuntor, que "monitora" a intensidade de corrente que passa por ele. Se essa corrente elétrica se eleva além do limite de operação segura deste disjuntor, ele se desarma automaticamente, abrindo o circuito para impedir a passagem da corrente, protegendo todo o circuito protegido por ele.
Assim como em um circuito elétrico, falhas de funcionamento em softwares também trazem riscos: operacionais, físicos, ao patrimônio, pessoais e, até mesmo, em alguns casos, risco de morte. A falha em uma operação de software pode resultar em operações parcialmente executadas, ou impedir a realização de transações por completo, a depender da implementação e dos requisitos funcionais do software. Quando uma operação falha, vários fatores podem ter influenciado esse resultado negativo, a falha pode ser consequência de uma instabilidade momentânea, indisponibilidade de um dos recursos da cadeia de dependências do sistema, ou, até mesmo, uma falha permanente que depende de intervenção para sua correção.
Se uma operação falha, significa que algo deixou de ser feito, e provavelmente receberá uma retentativa em algum momento próximo, seja de forma automática ou por uma nova solicitação do usuário. O problema é que além do funcionamento da operação estar comprometido, cada nova chamada à operação consome recursos, sem garantias de que uma retentativa será suficiente para a conclusão exitosa da operação. Além disso, a causa raíz que motiva a falha pode não estar diretamente relacionada ao serviço da operação, mas em uma camada mais profunda da cadeia de dependências do sistema.
Por exemplo: Em uma aplicação hipotética, uma operação depende da chamada de um serviço de API Web, que dependende de um serviço RPC que realiza a orquestração da operação entre alguns outros serviços; Um desses serviços realiza uma operação de persistência em um banco de dados. Se em algum momento, o espaço de armazenamento desse banco de dados se esgotar, o banco de dados não poderá mais guardar novos registros. Neste caso, as operações que necessitam guardar dados nesse banco, resultarão em uma falha, mas até que a falha ocorra, alguns recursos são alocados para a tentiva de executar a operação. Porém, até que haja a intervenção necessária no espaço de armazenamento do banco de dados, a falha continuará ocorrendo continuamente.
No exemplo anterior, pode-se observar que as operações que tentarem persistir novos registros no banco de dados continuarão consumindo recursos e concorrendo com as demais operações do sistema e enquanto o problema do espaço de armazenamento do banco de dados não for resolvido, é tendência que outras operações em falha também se acumulem. Como medida paliativa, seria interessante que essas operações em falha deixassem de ocorrer, até que a causa raíz fosse resolvida para que os recursos dessas operações deixem de ser alocados desnecessariamente. Para isso existe o Padrão "Circuit Breaker" (ou "Disjuntor").
Circuit Breaker
Como uma analogia aos disjuntores elétricos, o Circuit Breaker é um componente que intermedia as chamadas às operações que precisam ser protegidas contra falhas em um sistema. Este componente monitora as chamadas às operações protegidas e ao detectar falhas que podem comprometer o sistema, interrompe o fluxo de chamadas de forma controlada. Essa prática é conhecida como fail-fast (falha rápida).
"Isso é diferente de retentativas, pois os circuit breakers existem para evitar operações, ao invés de reexecutá-las." (NYGARD, 2007)
Figura 1 - Fluxo de uma chamada protegida por um Circuit Breaker
O modelo geral do padrão Circuit Breaker funciona a partir dessas 11 regras:
- O circuit breaker encapsula a chamada para a operação a ser protegida;
- As chamadas protegidas para a operação, são realizadas através do circuit breaker;
- O estado inicial do circuit breaker mantém o circuito "fechado", encaminhando as chamadas;
- Enquanto as chamadas são realizadas, o circuit breaker monitora e registra o resultado da operação;
- Enquanto as falhas ocorrem dentro do limite de operação aceitável do circuit breaker, as chamadas continuam sendo encaminhadas;
- Quando uma falha ultrapassa o limite operacional aceitável do circuit breaker, o circuito é "aberto" e não encaminhará a próximas chamadas;
- As chamadas não encaminhadas pelo cicuit breaker resultarão em falhas automaticamente;
- Quando o circuito é aberto, inicia-se um temporizador que determina por quanto tempo o circuito deve permanecer neste estado;
- Quando o temporizador se esgota, o circuito entra no estado "semiaberto", atípico dos disjuntores;
- O estado semiaberto indica que a próxima chamada recebida pelo circuit breaker deve ser encaminhada;
- Estando no estado semiaberto, se a chamada for bem sucedida, o circuito volta ao estado fechado, senão, volta ao estado aberto.
Os três estados: fechado, aberto e semiaberto
Um disjuntor elétrico possui apenas dois estados de operação: armado e desarmado. Enquanto o disjuntor está armado, significa que o circuito está fechado, permitindo a passagem de corrente entre seus terminais para alimentar o cicuito protegido. Caso o disjuntor detecte alguma sobrecarga de corrente no circuito protegido, isto é, que ultrapasse o limite seguro de operação definido para este disjuntor, o mesmo se desarma automaticamente, impedindo a passagem de corrente e mantendo o circuito aberto.
O componente Circuit Breaker se comporta de forma parecida a um disjuntor elétrico. Enquanto este recebe e encaminha as chamadas às operações protegidas normalmente, ou com falhas dentro de um limite aceitável, ele permite a continuidade da operação, mantendo-se em estado fechado. Se em algum momento, a quantidade de falhas supera o limite de falhas aceitáveis, o Cicuit Breaker deixa de encaminhar as próximas chamadas e opera em estado aberto, falhando imediatamente pelo próprio Circuit Breaker.
Apenas os estados "fechado" e "aberto" seriam suficientes para implementar um Circuit Breaker que opera de forma análoga aos disjuntores elétricos, porém, os disjuntores necessitam de intervenção externa para redefinir o estado do circuito para ser rearmado novamente, em caso de desarme. Já para softwares, essa abordagem causaria um impacto negativo na disponibilidade do sistema. Sendo assim, o interessante é que o Circuit Breaker seja capaz de redefinir seu estado automaticamente quando as coisas estiverem bem novamente, sem a necessidade de intervenção externa. Isso pode ser feito através de um temporizador que, após um intervalo de tempo definido, redefine o estado do Circuit Breaker para "semiaberto". Assim, o Circuit Breaker pode tentar encaminhar a próxima chamada à operação protegida, e dependendo do resultado dessa chamada, o circuito pode ser fechado novamente ou permanecer aberto até que o temporizador se esgote novamente. Sendo assim, diferente dos disjuntores elétricos, o componente Circuit Breaker possui um terceiro estado para o circuito: semiaberto.
Quando o Circuit Breaker detecta um volume inaceitável de falhas e abre o circuito, deve-se definir também um critério para reestabelecer o circuito automaticamente. O comportamento geral para essa transição, é definir um limite de tempo em que o Circuit Breaker permaneça em estado "aberto", ea pós esse limite de tempo, o estado do circuito é alterado automaticamente para "semiaberto". Quando o circuito está semiaberto, significa que a próxima chamada recebida será encaminhada e seu resultado definirá a transição para o próximo estado do circuito: se a chamada for bem sucedida, o circuito será fechado; se falhar, o circuito será aberto novamente.
Figura 2 - Transições de estados de um Circuit Breaker
Os componentes básicos
Figura 3 - Componentes básicos de um Circuit Breaker
O algoritmo geral
Figura 4 - Algoritmo de uma chamada a uma operação protegida por um Circuit Breaker
Figura 5 - Algoritmo do temporizador do estado aberto do Circuit Breaker
Figura 6 - Algoritmo de controle de operações do Circuit Breaker baseando na quantidade de falhas consecutivas