O conceito de idempotência e a aplicação na prática

Jean Meira
Livelo

--

Ninguém gosta de ser cobrado duas vezes indevidamente em uma operação financeira. Existem técnicas que podem e devem ser utilizadas na construção de uma solução que evitam com que esse e outros problemas parecidos aconteçam.

O conceito de idempotência, apesar de ser uma palavra difícil, é relativamente simples: uma operação (ou função) deve produzir o mesmo resultado que a primeira aplicação quando executada múltiplas vezes, sem que ocorram efeitos colaterais adicionais.

Apesar de simples, se o conceito não for endereçado corretamente, efeitos colaterais catastróficos podem surgir em APIs ou sistemas distribuídos diante de eventuais problemas corriqueiros. Este artigo visa aprofundar sobre o assunto e oferecer algumas formas de evitar esse tipo de problema em aplicações.

Um exemplo de cenário síncrono

Uma situação comum, que muitas pessoas já devem ter se deparado, ocorre durante a tentativa de pagamento duplicado em maquininhas de cartão de crédito.

Confira o exemplo:

Maria está na praia e compra um sorvete para seu filho João. O sorvete custa R$5,00, e ela passa no cartão de crédito. A transação é processada normalmente.

Logo em seguida, sua filha Jéssica lhe pede um segundo sorvete. Maria tenta comprar, também por R$5,00, mas a maquininha de cartão não aceita, pois a transação pode estar sendo feita de maneira equivocada pela pessoa responsável pela operação.

Essa negativa da maquininha é uma implementação que busca garantir a idempotência da transação. Para evitar que, acidentalmente, o cliente pague R$10,00 por apenas um sorvete de R$5,00, o sistema bloqueia intencionalmente a operação.

Normalmente, é possível contornar a negativa sistêmica alterando o valor para R$4,99 ou R$5,01, alterando a forma de pagamento, ou aguardando um período para repetir a operação, pois assim o sistema interpreta que trata-se de uma nova transação independente.

Essa estratégia é bem comum em APIs, e pode ser aplicada em outros cenários, como para evitar que um produto seja cadastrado duas vezes seguidas em um sistema por falha no front-end, permitindo um duplo clique do operador.

Um exemplo de cenário assíncrono

A preocupação em relação a idempotência de transações é ainda mais comum na comunicação entre sistemas distribuídos. A comunicação entre aplicações precisa ser cuidadosamente planejada para evitar efeitos colaterais em re-tentativas sistêmicas.

Como exemplo, é possível utilizar a própria Livelo, onde um participante pode resgatar um produto utilizando pontos e dinheiro. É necessário garantir que uma série de eventos sejam executados uma única vez durante a execução de todo o fluxo, evitando assim, tanto um potencial prejuízo financeiro quanto uma experiência ruim para os participantes, a Livelo e os parceiros.

Entre as preocupações consideradas, podemos destacar que:

  • Os pontos utilizados para a transação sejam debitados uma única vez;
  • A cobrança em dinheiro, seja via Pix ou cartão de crédito, seja feita uma única vez;
  • Apenas um e-mail transacional seja enviado comunicando a realização da compra;
  • O parceiro entregue apenas o que foi negociado com o participante, sem duplicidade de produtos.

E a mesma preocupação é válida para a reversão da transação. Caso o parceiro tenha algum problema durante a execução da entrega, e seja necessário realizar o estorno dos pontos e dinheiro do participante, é preciso evitar que as operações sejam executadas múltiplas vezes acidentalmente.

Falácias da computação distribuída

Essa preocupação acaba sendo maior em sistemas distribuídos pois o cenário é extremamente mais volátil do que em uma estrutura monolítica. Desenhar uma solução considerando apenas o “caminho feliz” pode gerar consequências indesejáveis para a empresa.

Entre as falácias conhecidas em programação distribuída estão:

  • A rede é confiável
  • Latência é zero
  • A banda é infinita
  • A topologia não muda

A partir dessas premissas, é necessário que erros sejam tolerados e tratados de acordo. Uma estratégia comum para lidar com erros de forma elegante é executar a maior parte da operação de forma assíncrona, não impactando diretamente a experiência do usuário final com a exibição de erros ou lentidão na resposta de requisições.

Durante as operações realizadas assincronamente, podem haver falhas de comunicação ou erros sistêmicos que permitam re-tentativas. Existem diversas estratégias para re-tentativa de operações, como a realização de tentativas exponenciais, que podem ajudar os sistemas com lentidão a se recuperarem, não desperdiçando requisições que provavelmente falhariam.

É necessário garantir re-tentativas seguras

Independente da estratégia escolhida, um fato é que apesar de um erro ter ocorrido, é possível que a operação tenha sido completamente ou parcialmente executada na outra ponta. A construção de software deve levar esses fatores em consideração, desde as etapas de design até desenvolvimento, testes e monitoria.

Um exemplo de evento inesperado pode ser um serviço externo demorar além do esperado para responder à requisição da aplicação. Se o serviço demorar 40 segundos para responder sobre uma transação, e a aplicação esperar 30 segundos mantendo a conexão aberta, é possível que a comunicação tenha sido completada com sucesso apesar do erro retornado (timeout), e o serviço requisitante não fique sabendo disso.

Para evitar uma operação duplicada, a integração precisa evitar isso, seja consultando o status da transação antes da re-tentativa, ou possuindo uma garantia que, a partir de um identificador único de transação, o serviço parceiro garanta a não duplicidade.

Essa preocupação pode gerar maior esforço de design e desenvolvimento, mas ela é necessária para evitar que ocorram múltiplas cobranças indevidas, ou até mesmo múltiplas entregas do mesmo pedido.

Conclusão

Ao construir produtos digitais, especialmente em arquiteturas distribuídas, todos os possíveis cenários devem ser levados em consideração. Eventos não planejados podem acontecer. O dispositivo do usuário pode ter performance inferior ao dispositivo usado durante o desenvolvimento, a rede pode sofrer instabilidades, data centers podem sofrer intermitências, atualizações sistêmicas podem impactar o comportamento de aplicações entre muitas outras possibilidades.

Porém, dedicando o esforço necessário para realizar uma análise detalhada e evitando fechar os olhos para eventuais cenários atípicos, a maior parte dos problemas pode ser mitigada de alguma forma. Esse cuidado na construção tende a trazer retorno por meio de um comportamento previsível e controlado de um sistema, evitando catástrofes que podem custar muito dinheiro para a organização, além do esforço posterior para adequação.

Referências e sugestões de leitura

--

--

Jean Meira
Livelo
Writer for

Especialista em Engenharia de Sistemas na Livelo