Compartilhar via


Práticas recomendas para tratamento de falhas transitórias

Todos os aplicativos que se comunicam com serviços e recursos remotos devem detectar e se recuperar de falhas transitórias. Esse requisito é especialmente verdadeiro para aplicativos executados na nuvem. Devido à natureza do ambiente de nuvem e conectividade pela Internet, é provável que seu aplicativo encontre falhas transitórias com mais frequência. As falhas transitórias incluem a perda momentânea de conectividade de rede com componentes e serviços, a indisponibilidade temporária de um serviço e os tempos limite que ocorrem quando um serviço está ocupado. Essas falhas normalmente se resolvem sem intervenção, portanto, a ação provavelmente terá êxito se o aplicativo tentar novamente após um atraso adequado.

O tratamento transitório de falhas é uma técnica de resiliência fundamental dentro do pilar de confiabilidade do Azure Well-Architected Framework. Se você detectar e se recuperar de falhas transitórias no nível do aplicativo, você reduz a chance de falhas em cascata que possam disparar procedimentos mais amplos de recuperação de incidentes ou recuperação de desastre (DR). O tratamento de falhas transitório eficaz ajuda sua carga de trabalho a tolerar interrupções rotineiras e manter a disponibilidade sem escalonamento para procedimentos de recuperação no nível da infraestrutura.

Por que ocorrem falhas transitórias na nuvem?

Falhas transitórias podem ocorrer em qualquer ambiente, em qualquer plataforma ou sistema operacional e em qualquer tipo de aplicativo. Para soluções que são executadas na infraestrutura local, o hardware redundante normalmente mantém o desempenho e a disponibilidade do aplicativo e seus componentes. Componentes e recursos também estão localizados próximos uns dos outros. Essa abordagem torna a falha menos provável, mas falhas transitórias ainda podem ocorrer. Eventos inesperados, como alimentação externa ou problemas de rede, ou outros cenários de desastre, podem causar interrupções. Hardware redundante também pode ser caro e geralmente é subutilizado.

Os ambientes de nuvem podem fornecer maior disponibilidade geral porque distribuem cargas de trabalho em muitos servidores e usam redundância, failover automático e alocação dinâmica de recursos. Mas a natureza dos ambientes de nuvem torna as falhas transitórias mais prováveis por várias razões:

  • Muitos recursos em um ambiente de nuvem são compartilhados e o acesso a esses recursos está sujeito à limitação para proteger os recursos. Alguns serviços recusam conexões quando a carga atinge um nível específico ou a taxa de transferência máxima. Essa abordagem permite que o serviço processe solicitações existentes e mantenha o desempenho para todos os usuários. A limitação ajuda a manter a qualidade do serviço para vizinhos e outros usuários que utilizam o recurso compartilhado.

  • Os ambientes de nuvem usam um grande número de unidades de hardware de commodities. Eles fornecem desempenho distribuindo dinamicamente a carga entre várias unidades de computação e componentes de infraestrutura. Eles fornecem confiabilidade reciclando ou substituindo unidades com falha automaticamente. Devido a essa natureza dinâmica, falhas transitórias e falhas de conexão temporárias podem ocorrer ocasionalmente.

  • Mais componentes de hardware, incluindo infraestrutura de rede, como roteadores e balanceadores de carga, geralmente existem entre o aplicativo e os recursos e serviços que ele usa. Essa infraestrutura pode ocasionalmente introduzir latência de conexão extra e falhas transitórias de conexão.

  • As condições de rede entre o cliente e o servidor geralmente variam, especialmente quando a comunicação cruza a Internet. Mesmo em locais internos, cargas de tráfego pesado podem reduzir a eficiência da comunicação e causar falhas de conexão intermitentes.

Desafios

Falhas transitórias podem afetar significativamente a disponibilidade percebida de um aplicativo, mesmo se você testá-lo completamente em condições esperadas. Para garantir que os aplicativos hospedados na nuvem operem de forma confiável, eles devem enfrentar os seguintes desafios:

  • O aplicativo deve ser capaz de detectar falhas quando elas ocorrem e determinar se as falhas são transitórias, duradouras ou terminais. Recursos diferentes normalmente retornam respostas diferentes quando ocorre uma falha. Essas respostas também podem variar dependendo do contexto da operação. Por exemplo, a resposta para um erro quando o aplicativo lê do armazenamento pode ser diferente da resposta para um erro ao gravar do armazenamento.

    Muitos recursos e serviços têm contratos de falha transitória bem documentados. Quando essas informações não estão disponíveis, torna-se muito mais difícil determinar a natureza da falha e se ela provavelmente será transitória.

  • O aplicativo deve ser capaz de repetir a operação se determinar que a falha provavelmente será transitória. Ele também precisa acompanhar o número de vezes que reexecuta a operação.

  • O aplicativo deve usar uma estratégia de repetição que atenda aos seus requisitos. A estratégia especifica quantas vezes o aplicativo deve tentar novamente, o atraso entre as tentativas e as ações a serem executadas após uma tentativa com falha. O número de tentativas e o atraso entre cada uma delas geralmente são difíceis de determinar. A estratégia depende do tipo de recurso e das condições operacionais atuais do recurso e do aplicativo.

Diretrizes gerais

As diretrizes a seguir podem ajudá-lo a criar mecanismos de tratamento de falhas transitórios adequados para seus aplicativos.

Verificar se existe um mecanismo de repetição interno

Muitos serviços fornecem um SDK ou uma biblioteca de clientes que contém um mecanismo transitório de tratamento de falhas. A política de repetição usada normalmente é adaptada à natureza e aos requisitos do serviço de destino. Como alternativa, as interfaces REST para serviços podem retornar informações que podem ajudá-lo a determinar se uma repetição é necessária e quanto tempo aguardar antes da próxima tentativa.

Use o mecanismo de repetição interno quando uma opção interna estiver disponível, a menos que você tenha requisitos específicos e bem compreendidos que tornem um comportamento de repetição diferente mais adequado para seu cenário.

Cada serviço do Azure lida com falhas transitórias de forma diferente. Alguns serviços fornecem políticas de repetição no nível do SDK que incluem algoritmos de back-off configuráveis. Outros serviços fornecem recursos de plataforma, como investigações de integridade e tempos limite de visibilidade que complementam a lógica de repetição no nível do aplicativo. Verifique o guia de confiabilidade de cada serviço do Azure que você usa. Esses guias incluem uma seção dedicada que fornece recomendações específicas do serviço para configuração de tentativas, ajuste de tempo limite e monitoramento de integridade.

Verificar se tentar novamente se adequa à operação

Repita as tarefas somente quando as falhas são transitórias, o que a natureza do erro normalmente indica e quando a operação pode ter êxito quando tentada novamente. Para serviços baseados em HTTP, o código de status 429 (muitas solicitações) e erros de servidor 5xx são candidatos de repetição típicos. A maioria dos erros de cliente 4xx, como 400, 401, 403 e 404, indica problemas que uma repetição não resolverá. Não repita as tarefas que tentam uma operação que não pode ser bem-sucedida, como uma atualização de banco de dados para um item que não existe ou uma solicitação para um serviço ou recurso que encontrou um erro fatal.

Em geral, implemente novas tentativas somente quando você puder determinar seu efeito completo e quando entender e puder validar as condições. Caso contrário, permita que o código de chamada implemente novas tentativas. Erros retornados de recursos e serviços fora do controle podem evoluir ao longo do tempo e talvez seja necessário revisitar sua lógica transitória de detecção de falhas.

Ao criar serviços ou componentes, implemente códigos de erro e mensagens que ajudam os clientes a determinar se eles devem tentar novamente as operações com falha. Por exemplo, retorne um isTransient valor para indicar se o cliente deve repetir a operação e sugerir um atraso adequado antes da próxima tentativa de repetição. Se você criar um serviço Web, retorne erros personalizados definidos por seus contratos de serviço. Os clientes genéricos podem não ser capazes de ler esses erros, mas eles são úteis quando você cria clientes personalizados.

Determinar uma contagem e intervalo de repetição apropriados

Otimize a contagem de repetições e o intervalo para o tipo de caso de uso. Se você não repetir tempo suficiente, o aplicativo não poderá concluir a operação e falhará. Se você repetir muitas vezes ou não esperar o suficiente entre tentativas, o aplicativo poderá conter recursos como threads, conexões e memória por longos períodos, o que afeta negativamente a integridade do aplicativo. Para obter mais informações, confira Padrão de repetição.

Adapte valores para o intervalo de tempo e o número de tentativas de repetição para o tipo de operação. Por exemplo, se a operação faz parte de uma interação do usuário, o intervalo deve ser curto e você deve tentar apenas algumas tentativas. Use essa abordagem para evitar fazer com que os usuários aguardem por uma resposta, que contém conexões abertas e pode reduzir a disponibilidade para outros usuários. Se a operação fizer parte de um fluxo de trabalho crítico ou de execução longa, em que cancelar e reiniciar o processo for caro ou demorado, você poderá esperar mais tempo entre as tentativas e tentar novamente mais vezes.

Determinar os intervalos corretos entre novas tentativas é a parte mais difícil de criar uma estratégia bem-sucedida. As estratégias típicas usam os seguintes tipos de intervalo de repetição:

  • Retirada exponencial: O aplicativo aguarda um pouco antes da primeira repetição e, em seguida, aumenta exponencialmente o tempo entre cada repetição subsequente. Por exemplo, ele pode repetir a operação após dois segundos, quatro segundos, oito segundos e até um número definido de tentativas ou uma duração total. Adicione um "jitter", que é um pequeno atraso aleatório, a cada intervalo de novas tentativas para impedir que vários clientes sincronizem suas tentativas e criem picos de carga no serviço de destino.

  • Intervalos incrementais: O aplicativo aguarda um pouco antes da primeira repetição e, em seguida, aumenta incrementalmente o tempo entre cada repetição subsequente. Por exemplo, ele pode repetir a operação após 3 segundos, 7 segundos e 11 segundos.

  • Intervalos regulares: O aplicativo aguarda o mesmo período de tempo entre cada tentativa. Por exemplo, ele pode repetir a operação a cada três segundos.

  • Repetição imediata: Falhas transitórias que eventos como uma colisão de pacote de rede ou um pico em um componente de hardware causam normalmente são breves. Nesses cenários, tentar novamente a operação imediatamente pode ajudar porque ela poderá ter êxito se a falha for limpa no tempo necessário para o aplicativo montar e enviar a próxima solicitação. Não tente mais de uma repetição imediata. Se a repetição imediata falhar, alterne para estratégias alternativas, como recuo exponencial ou ações de fallback.

  • Randomização: Qualquer uma das estratégias de repetição listadas anteriormente pode incluir a randomização para impedir que várias instâncias do cliente enviem tentativas de repetição subsequentes ao mesmo tempo. Por exemplo, uma instância pode repetir a operação após 3 segundos, 11 segundos ou 28 segundos, enquanto outra instância pode repetir a operação após 4 segundos, 12 segundos ou 26 segundos. A randomização é uma técnica útil que você pode combinar com outras estratégias.

Use uma estratégia de recuo exponencial com atrito para operações em segundo plano e use estratégias de nova tentativa imediatas ou de intervalo regular para operações interativas. Em ambos os casos, escolha o atraso e a contagem de repetição para que a latência máxima para todas as tentativas de repetição atenda ao requisito de latência de ponta a ponta.

Uma combinação de fatores contribui para o tempo limite máximo geral para uma operação repetida. Considere os seguintes fatores:

  • O tempo que uma conexão com falha leva para produzir uma resposta. Um valor de tempo limite no cliente normalmente define o período de espera.

  • O atraso entre tentativas de repetição.

  • O número máximo de tentativas.

O total desses tempos pode resultar em longos tempos gerais de operação, especialmente quando você usa uma estratégia de atraso exponencial em que o intervalo entre novas tentativas cresce rapidamente após cada falha. Se um processo precisar atender a um SLO (objetivo de nível de serviço) específico, o tempo de operação geral, incluindo todos os tempos limite e atrasos, deverá estar dentro dos limites definidos no SLO.

Considere o timeout das operações ao escolher intervalos de repetição para evitar iniciar uma tentativa subsequente imediatamente, especialmente quando a duração do timeout é semelhante ao intervalo de repetição. Determine se você precisa manter o período total possível, que é o tempo limite mais os intervalos de repetição, sob um limite de tempo total específico. Se uma operação tiver um tempo limite excepcionalmente curto ou longo, o tempo limite poderá influenciar quanto tempo esperar e com que frequência tentar novamente a operação.

Defina tempos limite em cada chamada de saída antes de implementar a lógica de repetição. Tempos limite, tentativas e estratégias de recuo funcionam em conjunto. Uma estratégia de repetição é tão eficaz quanto os tempos limite que regem cada tentativa individual. Tempos de espera muito longos podem fazer com que threads e conexões se acumulem durante interrupções. Tempos limite muito curtos causam falhas prematuras em operações que, de outra forma, teriam êxito.

Não implemente estratégias de repetição excessivamente agressivas. Essas estratégias usam intervalos muito curtos ou repetições que ocorrem com muita frequência. Eles podem afetar negativamente o recurso ou o serviço de destino. Eles também podem impedir que o recurso ou o serviço se recupere, de modo que o recurso ou serviço continue bloqueando ou recusando solicitações. Esse cenário cria um ciclo no qual o aplicativo envia mais solicitações para o recurso ou serviço, o que reduz ainda mais sua capacidade de recuperação.

Use o tipo de exceção e todos os dados que ele contém, ou os códigos de erro e mensagens que o serviço retorna, para otimizar o número de repetições e o intervalo entre eles. Algumas exceções ou códigos de erro, como HTTP 503 (Serviço Indisponível), podem indicar que o serviço falhou e não responderá a novas tentativas. Quando uma resposta incluir um cabeçalho Retry-After, siga-o e aguarde pelo menos pelo tempo especificado antes de tentar novamente. Esse sinal fornecido pelo servidor reflete a linha do tempo de recuperação do serviço e tem precedência sobre o cálculo de recuo do lado do cliente.

Use uma abordagem de fila de mensagens mortas para que as informações da solicitação de entrada não sejam perdidas depois que você usar todas as tentativas de repetição. Essa técnica adia o trabalho com falha para processamento posterior em vez de descartá-lo.

Evitar antipadrões

Na maioria dos casos, evite implementações que incluam camadas duplicadas de código de repetição. Evite designs que usam mecanismos de repetição em cascata ou que apliquem novas tentativas em cada estágio de uma operação que envolva uma hierarquia de solicitações, a menos que você tenha requisitos específicos. Nesses casos excepcionais, use políticas que limitem o número de repetições e períodos de atraso e certifique-se de entender as consequências.

Por exemplo, considere um componente que faz uma solicitação para outro, que acessa o serviço de destino. Uma repetição com uma contagem de três em ambas as chamadas soma nove tentativas de repetição no total em relação ao serviço.

Muitos serviços e recursos implementam um mecanismo de repetição interno. Desative ou modifique esses mecanismos se precisar implementar novas tentativas em um nível mais alto. Para obter mais informações sobre os riscos de novas tentativas descoordenadas, consulte Retry Storm antipattern.

Nunca implemente um mecanismo de repetição sem fim. Essa abordagem normalmente impede que o recurso ou o serviço se recupere de situações de sobrecarga e faz com que a limitação e as conexões recusadas continuem por mais tempo. Use um número finito de tentativas ou implemente um padrão como Circuit Breaker para permitir que o serviço se recupere.

Implemente um orçamento de repetição para limitar o número total de repetições em todas as solicitações em um processo ou serviço, além dos limites para cada solicitação individual. Por exemplo, você pode permitir que um processo faça no máximo 60 repetições por minuto em relação a uma dependência especificada. Se você esgotar o orçamento, recuse a solicitação em vez de repetir a tentativa.

Somente os limites de repetição por solicitação não podem impedir um cenário em que muitas solicitações simultâneas tentem novamente algumas vezes e sobrecarregem coletivamente um serviço downstream em dificuldades. Um orçamento para tentativas repetidas limita a carga agregada de tentativas e pode fazer a diferença entre um problema de capacidade localizada e uma falha em cascata.

Nunca tente novamente imediatamente mais de uma vez.

Evite usar um intervalo de repetição regular ao acessar serviços e recursos no Azure, especialmente quando você tiver um alto número de tentativas de repetição. A melhor abordagem nesse cenário é uma estratégia exponencial de retirada que usa uma funcionalidade de quebra de circuito.

Impedir que várias instâncias do mesmo cliente ou várias instâncias de clientes diferentes enviem novas tentativas simultaneamente. Se esse cenário for provável, introduza a randomização nos intervalos de repetição.

Teste sua estratégia de repetição e implementação

Teste sua estratégia de repetição em uma ampla gama de condições, especialmente quando o aplicativo e seus recursos ou serviços de destino operam sob carga extrema. Para verificar o comportamento durante o teste, você pode executar as seguintes ações:

  • Inclua falhas transitórias em suas práticas de engenharia de caos e injeção de falhas introduzindo-as propositalmente em seus ambientes de produção e não produção. Por exemplo, envie solicitações sem suporte ou adicione código que detecte solicitações de teste e responda com diferentes tipos de erros.

  • Crie uma versão simulada do recurso ou serviço que retorna um intervalo de erros que o serviço real pode retornar. Certifique-se de que ele abrange todos os tipos de erro que sua estratégia de repetição detecta.

  • Para serviços personalizados que você cria e implanta, force a ocorrência de erros transitórios tirando o serviço temporariamente do ar ou sobrecarregando-o. Não tente sobrecarregar nenhum recurso compartilhado ou serviços compartilhados no Azure.

  • Considere usar um serviço de injeção de falha para executar experimentos controlados em seus recursos do Azure. Por exemplo, o Azure Chaos Studio dá suporte a falhas diretas de serviço, como adicionar latência de rede ou reinicializar um cluster de cache e falhas baseadas em agente, como aplicar pressão de memória ou encerrar um processo em uma VM (máquina virtual). Você pode integrar experimentos de injeção de falhas em seus pipelines de CI/CD (integração contínua e entrega contínua) para validar continuamente a resiliência como parte do processo de implantação.

  • Para APIs baseadas em HTTP, considere usar uma biblioteca em seus testes automatizados para alterar o resultado de solicitações HTTP, adicionando tempos extras de ida e volta ou alterando a resposta, como o código de status HTTP, cabeçalhos, corpo ou outros fatores. Essa abordagem ajuda você a testar deterministicamente um subconjunto das condições de falha para falhas transitórias e outros tipos de falhas.

  • Execute testes simultâneos e de fator de alta carga para garantir que o mecanismo de repetição e a estratégia funcionem corretamente nessas condições. Esses testes também ajudam a confirmar que as tentativas de repetição não afetam as operações do cliente ou causam contaminação cruzada entre solicitações.

Gerenciar configurações de política de repetição

Uma política de nova tentativa é uma combinação de todos os elementos de sua estratégia de nova tentativa. Ele define o mecanismo de detecção que determina os seguintes fatores:

  • Se é provável que uma falha seja transitória
  • O tipo de intervalo a ser usado, como retirada regular, exponencial ou randomização
  • Os valores de intervalo reais
  • O número de vezes que tentar novamente

Implemente novas tentativas em muitos lugares, inclusive em aplicativos básicos e em cada camada de aplicativos mais complexos. Em vez de codificar elementos de política em vários locais, use um ponto central para armazenar todas as políticas. Por exemplo, armazene valores como o intervalo e a contagem de repetições em arquivos de configuração de aplicativo, leia-os em runtime e crie programaticamente as políticas de repetição. Essa abordagem simplifica o gerenciamento de configurações e a modificação e ajuste fino de valores para responder a requisitos e cenários em mudança. Projete o sistema para armazenar os valores em vez de reler um arquivo de configuração para cada solicitação e use padrões adequados se a configuração não fornecer os valores.

Armazene os valores usados para criar as políticas de repetição em runtime no sistema de configuração do aplicativo para que você possa alterá-las sem precisar reiniciar o aplicativo.

Aproveite as estratégias de repetição internas ou padrão disponíveis nas APIs de cliente que você usa, mas somente quando elas se adequarem ao seu cenário. Essas estratégias normalmente são genéricas. Eles podem ser adequados em alguns cenários, mas em outros cenários eles não fornecem toda a gama de opções para atender aos seus requisitos específicos. Para determinar os valores mais adequados, teste para entender como as configurações afetam seu aplicativo. Para opções de configuração e padrões de repetição específicos do serviço, verifique o guia de confiabilidade de cada serviço do Azure em sua arquitetura.

Registrar e rastrear falhas transitórias e não transitórias

Sua estratégia de repetição deve incluir a manipulação de exceções e outra instrumentação que registra tentativas de repetição. Uma falha transitória ocasional e uma repetição são esperadas e não indicam um problema. Mas um número regular ou crescente de repetições geralmente indica um problema que pode causar uma falha ou reduzir o desempenho e a disponibilidade do aplicativo.

Registre falhas transitórias como entradas de aviso e não como entradas de erro para que os sistemas de monitoramento não as detectem como erros de aplicativo que podem disparar alertas falsos.

Armazene um valor em suas entradas de log que indica se a limitação no serviço ou outros tipos de falhas, como falhas de conexão, causam novas tentativas. Essa abordagem ajuda você a diferenciar as causas durante a análise de dados. Um aumento nos erros de limitação geralmente indica uma falha de design do aplicativo ou a necessidade de migrar para um serviço premium que fornece hardware dedicado.

Medir e registrar os tempos decorridos gerais para operações que incluem um mecanismo de repetição. Essa métrica indica com precisão o efeito geral que as falhas transitórias têm nos tempos de resposta do usuário, na latência do processo e na eficiência dos casos de uso do aplicativo. Registre o número de repetições que ocorrem para que você possa entender os fatores que contribuem para o tempo de resposta.

Implemente um sistema de telemetria e monitoramento que gera alertas quando as seguintes métricas aumentam:

  • O número e a taxa de falhas
  • O número médio de repetições
  • O tempo geral decorrido antes que as operações tenham êxito

Gerenciar operações que falham continuamente

Crie um plano para lidar com operações que continuam falhando em todas as tentativas. Essas situações são inevitáveis.

  • Uma estratégia de repetição define o número máximo de vezes que um aplicativo deve repetir uma operação. Isso não impede que o aplicativo repita a operação para começar com o mesmo número de tentativas. Por exemplo, se um serviço de processamento de pedidos falhar com um erro fatal que o tire de serviço permanentemente, a estratégia de repetição poderá detectar um tempo limite de conexão e tratá-lo como uma falha transitória. O código repete a operação o número especificado de vezes e, em seguida, para. Quando outro cliente faz um pedido, o aplicativo tenta a operação novamente, tenta novamente e falha.

  • Para evitar tentativas repetidas de operações que falham continuamente, implemente o Padrão Circuit Breaker. Quando você usa esse padrão, se o número de falhas dentro de uma janela de tempo especificada exceder um limite, as solicitações retornarão ao chamador imediatamente como erros e o aplicativo não tentar acessar o recurso ou serviço com falha.

  • O aplicativo pode testar periodicamente o serviço, de forma intermitente e com intervalos longos entre solicitações, para detectar quando ele fica disponível. O intervalo depende de fatores como a criticidade da operação e a natureza do serviço. Pode variar de alguns minutos a várias horas. Quando o teste for bem-sucedido, o aplicativo poderá retomar as operações normais e passar solicitações para o serviço recém-recuperado.

  • Enquanto isso, você pode ser capaz de voltar para outra instância do serviço em um datacenter ou aplicativo diferente. Você também pode usar um serviço semelhante que forneça funcionalidade compatível, mas mais simples ou faça algumas operações alternativas com base na esperança de que o serviço esteja disponível em breve. Por exemplo, você pode armazenar solicitações para o serviço em uma fila ou em um banco de dados e tentar novamente mais tarde. Ou você pode redirecionar o usuário para uma instância alternativa do aplicativo, degradar o desempenho do aplicativo, mas ainda fornecer funcionalidade aceitável ou apenas retornar uma mensagem ao usuário para indicar que o aplicativo não está disponível no momento.

Outras considerações

Ao determinar os valores para o número de repetições e os intervalos de repetição de uma política, considere se a operação no serviço ou no recurso faz parte de uma operação de execução prolongada ou de várias etapas. Pode ser difícil ou caro compensar todas as etapas operacionais que já foram bem-sucedidas quando uma delas falha. Nesse caso, um intervalo longo e um grande número de tentativas podem ser aceitáveis, desde que a estratégia não bloqueie outras operações mantendo ou bloqueando recursos escassos.

Considere se repetir a mesma operação pode causar inconsistências de dados. Se um aplicativo repetir partes de um processo de várias etapas e as operações não forem idempotentes, poderão ocorrer inconsistências. Por exemplo, se uma operação que incrementa um valor for repetida, ela produzirá um resultado incorreto. Uma operação repetida que envia uma mensagem para uma fila também pode causar problemas para um consumidor que não consegue detectar mensagens duplicadas. Para evitar esses cenários, projete cada etapa como uma operação idempotente. Para obter mais informações, confira Padrões de idempotência.

Seja intencional sobre o escopo das operações que o aplicativo vai tentar novamente. Por exemplo, talvez seja mais fácil implementar a lógica de repetição em um nível que inclua várias operações e repita todas elas se houver falha. Mas essa abordagem pode levar a problemas de idempotência ou operações de reversão desnecessárias.

Se você escolher um escopo de repetição que inclua várias operações, contabilize a latência total de todas elas ao determinar intervalos de repetição, quando monitorar o tempo decorrido da operação e antes de gerar alertas para falhas.

Considere como sua estratégia de novas tentativas afeta os vizinhos e outros locatários em um aplicativo compartilhado e quando você utiliza recursos e serviços compartilhados. Políticas de repetição agressivas podem aumentar o número de falhas transitórias que ocorrem para outros usuários e para aplicativos que compartilham os recursos e serviços. As políticas de repetição implementadas por outros usuários também podem afetar seu aplicativo. Para aplicativos comercialmente críticos, use serviços premium que não são compartilhados. Essa abordagem permite controlar a carga e a consequente limitação de recursos e serviços, o que pode justificar o custo extra.

Próxima etapa