Como resolver problemas de integridade de dados com webhooks

Dados são o coração de toda aplicação moderna e, independentemente da arquitetura da sua aplicação (monolito ou microsserviços), sempre existe uma camada de dados. A camada de dados é a última camada de qualquer arquitetura de aplicação e, portanto, a base sobre a qual toda aplicação de webhook orientada a dados é construída.

A importância dessa camada para o sistema inteiro a torna muito delicada. Qualquer situação que faça os dados ficarem incompletos, calculados de forma errada ou até duplicados pode comprometer a integridade do sistema.

Webhooks são usados porque você quer que uma ação aconteça na aplicação de destino (o consumidor do webhook). Muitas vezes essa ação faz com que dados sejam gravados ou manipulados no banco de dados. Neste artigo, vamos ver de que formas o tratamento inadequado de webhooks pode levar a inconsistências de dados e o que você deve fazer para evitar isso.

O que é integridade de dados?

Integridade de dados é a exatidão e a consistência dos dados armazenados em um banco de dados ou em qualquer outra estrutura de dados ao longo de todo o seu ciclo de vida. A integridade de dados é fundamental para o uso de qualquer aplicação que armazena, processa ou recupera dados.

Um webhook precisa completar o seu impacto sobre um sistema mantendo a integridade dos dados intacta. O fato de os webhooks serem disparados automaticamente torna ainda mais importante garantir que o processamento deixe os dados da aplicação em um estado consistente e preciso. Caso contrário, múltiplos webhooks disparados contra um endpoint podem comprometer os dados a ponto de ser praticamente impossível voltar a um estado consistente e preciso.

Causas de problemas de integridade de dados com webhooks

Webhooks que falham

Uma das formas mais fáceis de chegar a um estado de inconsistência de dados é quando um webhook falha. Imagine que você tem um serviço de pagamentos e um serviço de emissão de ingressos. Quando um usuário faz um pagamento, um registro de pagamento é salvo e um webhook é disparado para o serviço de ingressos, que deve salvar o registro do ingresso e enviar por e-mail os detalhes ao cliente.

Vamos supor que o pagamento seja concluído e o webhook seja disparado, mas falhe no seu endpoint de ingressos e retorne um status code de erro. A sua aplicação está automaticamente em um estado inconsistente, porque existe um registro de pagamento sem o registro de ingresso correspondente. Se isso acontecer várias vezes, de repente você vai notar um volume considerável de reclamações de clientes que não receberam os ingressos depois de pagar. Isso definitivamente não é bom para o seu negócio.

Tratamento inadequado de erros

Outra forma de comprometer a integridade dos dados da sua aplicação ao receber webhooks é tratando erros de forma inadequada, por exemplo retornando o status code errado para o erro que ocorreu.

Quando webhooks falham, alguns provedores fazem retry ao receber um status code de erro (4xx, 5xx) na resposta. Uma API mal projetada é aquela que retorna um status code 2xx indicando sucesso quando algo na verdade falhou. Imagine que o seu webhook faz uma gravação de dados no banco. Por algum motivo a operação de escrita falha e você captura o erro, mas em vez de retornar um status code 500 na resposta, retorna 200. Isso faz o provedor de webhooks ou o sistema de retry assumir que deu tudo certo e passar para o próximo webhook em vez de reenviar o que falhou.

A operação de escrita incompleta deixa os seus dados em um estado inconsistente, já que os dados que deveriam ter sido gravados pela operação do webhook agora estão faltando.

Lógica de transações de banco com bugs

Transações são um conjunto de operações de banco de dados que precisam todas ter sucesso ou todas falhar para manter os dados consistentes. Quando você tem quatro operações que precisam acontecer em uma transação atômica, mesmo que três tenham sucesso e apenas uma falhe, a operação inteira precisa sofrer rollback. Isso é muito comum em aplicações financeiras.

A forma mais fácil de cair em um estado de dados impreciso é não usar transação nenhuma e executar as escritas individualmente. Outra forma sutil de as inconsistências aparecerem é quando você precisa executar três operações de escrita, mas apenas duas delas são relacionadas, então você coloca as duas em uma transação e deixa a terceira de fora. Mesmo que as duas escritas dentro da transação estejam protegidas, a que ficou de fora não está e pode falhar sem a chance de sofrer rollback.

Duplicação de webhooks

Dos problemas discutidos até aqui, a duplicação de webhooks não acontece do lado do consumidor. Por algum motivo, provedores de webhooks podem disparar uma requisição de webhook mais de uma vez. Isso pode fazer com que uma escrita no banco seja executada mais de uma vez, o que pode levar à duplicação de dados ou a atualizações duplicadas. Duplicar dados pode não ser tão grave quando você está contando curtidas em um post de rede social, mas é perigoso quando você está creditando ou debitando a conta financeira de um usuário.

Prevenindo problemas de dados

Testes minuciosos

A forma mais recomendada de prevenir problemas de integridade de dados é por meio de testes adequados antes que os seus webhooks vão para produção. Garanta que você testa os seus endpoints de webhook contra falhas e problemas de escritas atômicas. Você encontra um detalhamento completo de como testar a confiabilidade dos seus webhooks aqui.

Operações atômicas no banco de dados

Você também precisa garantir que todas as escritas no banco sejam consistentes. Uma boa regra prática ao lidar com operações de manipulação de dados em um endpoint de webhook é mantê-las todas em uma única transação, de modo que, se qualquer uma das escritas falhar, você consiga fazer rollback de tudo e devolver os dados a um estado consistente.

Idempotência

Outra estratégia preventiva é construir os seus webhooks para serem idempotentes e testá-los contra problemas de duplicação de dados causados pelo recebimento do mesmo webhook mais de uma vez. Para saber mais sobre como tornar os seus webhooks idempotentes, veja este artigo.

Recuperando-se de problemas de integridade de dados

Antes de implementar estratégias de recuperação, garanta que você já cobriu o máximo possível de estratégias preventivas por meio de testes e depuração adequados.

Sistema de retry

Para se recuperar das inevitáveis falhas de webhooks, você precisa de um sistema de retry. Um sistema de retry detecta uma falha na URL do webhook ao receber um status code de erro na resposta e reenvia o webhook quando ele falha. Para construir um sistema de retry, você precisa de um componente que faça o buffer das requisições vindas do provedor de webhooks, para conseguir persistir a informação entre as tentativas.

Uma fila de mensagens é recomendada para fazer o buffer dos seus webhooks e reenviá-los quando necessário. Você pode construir uma do zero usando bibliotecas open source como RabbitMQ ou Apache Kafka e escrever a lógica de retry para trabalhar com elas. Se você precisa montar rapidamente uma fila de mensagens com sistema de retry e não quer se preocupar com o custo de manutenção, use o Hookdeck para começar imediatamente.

Backups de dados

Outra medida corretiva que você pode adotar é manter backups do banco de dados quase em tempo real. Isso pode ajudar a restaurar o banco para um estado consistente quando os seus dados forem comprometidos.

Na AWS, por exemplo, vários serviços de armazenamento como DynamoDB, Relational Database Service (RDS) e Elastic Block Store (EBS) oferecem serviços de backup que você pode configurar dentro do próprio serviço. Você também pode usar o AWS Backup, que oferece um console centralizado para automatizar e fazer backups entre os seus serviços AWS.

Sistemas de logging e monitoramento

Ter um bom sistema de logging e monitoramento também é muito importante para encontrar e corrigir erros nos seus webhooks e nos seus dados, especialmente em produção. Você consegue detectar facilmente o que deu errado com os dados, onde aconteceu, e tomar decisões bem informadas sobre como corrigir.

Ainda usando a AWS como exemplo, você pode usar o serviço CloudWatch para monitorar os serviços de computação onde as suas aplicações rodam e também monitorar operações de escrita no banco de dados e em outros sistemas de armazenamento. Isso ajuda a dar visibilidade sobre as operações dos seus webhooks e a rastrear falhas no sistema.

Conclusão

Inconsistências de dados podem ser muito prejudiciais ao sucesso da sua aplicação, já que dados comprometidos podem se transformar em desconfiança entre os usuários. Os webhooks têm um papel na manipulação de dados nas suas aplicações ao disparar a ação que faz os dados mudarem, por exemplo a atualização do saldo da conta financeira de um usuário após uma compra. Esse processo deve ser tratado com cuidado para garantir que os seus dados continuem consistentes e precisos.

Para mais informações sobre como garantir que os seus webhooks estão prontos para produção, veja os nossos artigos "Colocando webhooks em produção" e "Construindo resiliência em webhooks para mitigar problemas de desempenho."

Bom código!