Como impedir que duas pessoas comprem o último bilhete?
Share

In this article
- 01Por que uma leitura seguida de escrita na contagem de bilhetes causa overselling?
- 02Por que usar um bloqueio pessimista na linha para os últimos bilhetes?
- 03Como uma reserva no Redis com TTL impede que checkouts abandonados bloqueiem o inventário?
- 04O que acontece quando o webhook de pagamento chega duas vezes?
- 05Isso funciona sob carga?
Decremente a contagem de bilhetes dentro de uma curta transação de base de dados que mantém um bloqueio pessimista na linha, e reserve cada bilhete no Redis com um tempo de vida (TTL). A escrita do segundo comprador espera alguns milissegundos atrás da primeira, e então vê que está esgotado. Checkouts abandonados liberam automaticamente sua reserva quando o TTL expira.
Por que uma leitura seguida de escrita na contagem de bilhetes causa overselling?
Duas requisições podem ler a mesma contagem restante de um bilhete, ambas passam na verificação e ambas registram uma venda. A contagem estava correta quando lida e desatualizada quando escrita, então um bilhete é vendido duas vezes. Qualquer leitura seguida de escrita em inventário compartilhado sofre dessa condição de corrida, a menos que a escrita seja atômica ou bloqueada.
Uma simples tentativa otimista corrige o problema de correção, mas começa a causar muitos retries quando os últimos bilhetes de um evento popular são disputados por um pico de checkouts: cada cliente comprador que perde precisa tentar o checkout inteiro novamente, não apenas a escrita.
Por que usar um bloqueio pessimista na linha para os últimos bilhetes?
Um bloqueio pessimista faz o segundo escritor esperar brevemente em vez de falhar e tentar novamente às cegas. Para uma linha muito concorrida que muitos compradores querem ao mesmo tempo, alguns milissegundos de espera são mais baratos do que retries do checkout inteiro. O bloqueio é mantido apenas durante a curta transação que decrementa a contagem de bilhetes.
Como uma reserva no Redis com TTL impede que checkouts abandonados bloqueiem o inventário?
Quando um comprador inicia o checkout, reserve o bilhete no Redis com um TTL. Se ele pagar, a reserva se converte em venda. Se ele desistir, a chave expira e o bilhete retorna automaticamente ao inventário. Ninguém pode segurar um bilhete como refém abrindo uma aba de checkout e saindo.
O que acontece quando o webhook de pagamento chega duas vezes?
Nada além do normal. O manipulador do webhook é idempotente, baseado no ID do pedido, e sua única função é publicar um evento de pagamento confirmado no RabbitMQ. Uma entrega repetida encontra o pedido já processado, então um bilhete nunca é emitido duas vezes. Um consumidor downstream gera o bilhete e envia a confirmação.
O mesmo padrão é explicado passo a passo na nota sobre webhooks idempotentes.
Isso funciona sob carga?
Nos testes no FindYourVibe, o checkout permaneceu correto com mais de 300 compradores concorrentes em um único lançamento de evento, sem vender bilhetes em excesso. As escolhas de design que garantiram isso foram o bloqueio na linha para os últimos bilhetes, a reserva no Redis com TTL e o webhook idempotente alimentando uma fila.
Bloqueio pessimista na linha para os últimos bilhetes de um evento
Reserva no Redis com TTL, evitando overselling
RabbitMQ desacopla a emissão do bilhete da resposta do webhook
Webhook de pagamento idempotente baseado no ID do pedido
Publicado originalmente em sakshamjain.codes/blog/stop-two-people-buying-the-last-ticket. Precisa disso implementado? Veja meus serviços em NexoRush ou envie uma mensagem.