Yago Costa Ayala
Posted on May 25, 2024
Dados duplicados, transações duplicadas, usuários com o mesmo CPF...
Todo mundo já passou por isso, e resolver é algo bem tranquilo, mas poucos sabem o termo que as grandes empresas usam. É aí que entra a idempotência, um conceito essencial que pode salvar a sua pele (e a do seu sistema). Vamos explorar o que é idempotência, por que ela é importante, casos reais onde ela faz a diferença, e como você pode implementá-la com um exemplo em Node.js.
O Que é Idempotência?
Imagina que você está fazendo uma compra online. Você clica no botão "comprar" e, por algum motivo, a página demora para carregar. Você clica de novo, e de novo, com medo de que sua compra não tenha sido registrada. De repente, você percebe que comprou três vezes o mesmo item! Se o sistema de compras fosse idempotente, ele garantiria que apenas uma compra fosse processada, não importa quantas vezes você clicasse no botão.
Na computação, uma operação é idempotente se produzir o mesmo resultado, mesmo que seja realizada várias vezes. Ou seja, clicar várias vezes no botão "comprar" deveria resultar em apenas uma compra.
Por Que a Idempotência é Importante?
Tolerância a Falhas: Em qualquer sistema, falhas podem ocorrer. Se uma solicitação falhar, você pode tentar de novo sem medo de duplicar a operação.
Consistência: Em sistemas onde manter dados consistentes é crucial, a idempotência evita a duplicação de operações, garantindo que tudo permaneça em ordem.
Simplicidade na Recuperação de Erros: Com operações idempotentes, a recuperação de erros é mais fácil. Se algo der errado, é só tentar de novo, sabendo que o resultado será o mesmo.
Métodos HTTP e Idempotência
GET: Por definição, as requisições GET devem ser idempotentes. Pedir o mesmo recurso várias vezes não altera o estado do recurso.
PUT: A operação PUT deve ser idempotente. Enviar uma requisição PUT múltiplas vezes deve atualizar um recurso com o mesmo estado.
DELETE: Idealmente, DELETE deve ser idempotente. Deletar um recurso várias vezes não deve resultar em erro, pois o recurso já foi removido.
POST: A operação POST não é idempotente por natureza, pois cada requisição POST pode resultar na criação de um novo recurso. No entanto, a idempotência pode ser implementada em POSTs através de técnicas como chaves de idempotência.
Casos de Uso Reais
No setor financeiro, a idempotência é essencial para evitar cobranças duplicadas. Empresas como PayPal e Stripe implementam idempotência em suas APIs de pagamento. Quando uma solicitação de pagamento é enviada, um identificador único é usado para garantir que, mesmo que a solicitação seja repetida devido a falhas de rede, o cliente não será cobrado duas vezes.
Plataformas de reservas de voos e hotéis, como Expedia e Booking.com, utilizam idempotência para garantir que reservas duplicadas não sejam criadas. Se uma solicitação para reservar um quarto ou um voo for repetida, a plataforma verifica se a reserva já foi feita e evita duplicações.
Em redes sociais como Facebook e Twitter, a idempotência é usada ao criar posts ou enviar mensagens. Se um usuário acidentalmente submeter a mesma postagem várias vezes, o sistema pode reconhecer a duplicação e evitar múltiplos posts idênticos.
Como implementar ?
Aqui está um exemplo simples de como implementar idempotência em uma API RESTful usando Node.js e Express. Teremos dois endpoints: um para gerar uma nova chave de idempotência (UUID) e outro para criar um recurso usando essa chave.
const express = require('express');
const { v4: uuidv4 } = require('uuid');
const app = express();
app.use(express.json());
const idempotencyStore = new Set();
app.get('/generate-key', (req, res) => {
const idempotencyKey = uuidv4();
res.send({ idempotencyKey });
});
app.post('/create-resource', (req, res) => {
const idempotencyKey = req.headers['idempotency-key'];
if (!idempotencyKey) {
return res.status(400).send('Idempotency Key is required');
}
if (idempotencyStore.has(idempotencyKey)) {
return res.status(200).send({ message: 'Request already processed' });
}
idempotencyStore.add(idempotencyKey);
res.status(201).send({ message: 'Resource created successfully' });
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Neste exemplo, um endpoint (/generate-key) gera uma nova chave de idempotência (UUID) e o outro endpoint (/create-resource) utiliza essa chave para garantir que a operação de criação de recursos seja idempotente. Se uma solicitação com a mesma chave de idempotência for recebida novamente, o servidor retorna uma mensagem indicando que a solicitação já foi processada, em vez de criar um novo recurso.
Como podemos melhorar ?
- Se o seu servidor utiliza um load balancer, armazenar chaves de idempotência na memória local pode não ser eficaz, pois cada instância do servidor teria seu próprio armazenamento. Em vez disso, use um banco de dados distribuído como o Redis para garantir que todas as instâncias do servidor possam acessar e compartilhar o mesmo conjunto de chaves.
- Em vez de simplesmente gerar um UUID, podemos torná-lo mais específico concatenando-o com um valor do usuário, como o username ou namespace. Isso ajuda a evitar colisões de chaves e torna a chave mais significativa.
- Para evitar que o armazenamento cresça indefinidamente, é uma boa prática definir um tempo de expiração para as chaves. Configurar o Redis para expirar as chaves após um período de tempo razoável garante que o banco de dados seja limpo automaticamente e não cresça indefinidamente.
Conclusão
A idempotência é um princípio essencial para construir sistemas resilientes e confiáveis. Entender como aplicar esse conceito pode melhorar significativamente a robustez de APIs e sistemas distribuídos. Empresas como PayPal, Stripe, Expedia e Facebook utilizam idempotência para garantir a consistência e a segurança em suas operações. Ao implementar idempotência, garantimos que nossos sistemas possam lidar com falhas e repetições de maneira previsível e segura.
Posted on May 25, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.