
Race Condition: três formas de resolver um dos problemas mais comuns em sistemas concorrentes
Esse artigo explica 3 formas de resolver o famoso race condition. Espero que goste da leitura.
Publicado em 13 de julho de 2026 às 11:17
Recentemente, um integrante do meu time me procurou com um problema que, mais cedo ou mais tarde, praticamente todo desenvolvedor enfrenta: uma race condition.
À primeira vista, o código parecia correto. Em ambiente de testes tudo funcionava perfeitamente. Mas, quando duas requisições aconteciam praticamente no mesmo instante, o sistema acabava permitindo que ambas alterassem o mesmo recurso.
Esse tipo de situação acontece porque existe uma pequena janela entre ler um estado e atualizar esse estado. Nesse intervalo, outro processo pode fazer exatamente a mesma coisa.
Quanto maior a concorrência, maior a probabilidade de isso acontecer.
A partir dessa conversa, expliquei três estratégias clássicas utilizadas para resolver esse problema. Resolvi compartilhar aqui porque esse conhecimento aparece constantemente em sistemas reais e também em entrevistas de engenharia de software.
O problema
Imagine o último ingresso disponível para um show.
Ana e Bruno clicam em "Comprar" praticamente ao mesmo tempo.
Os dois leem que ainda existe um ingresso disponível.
Os dois decidem comprar.
Sem nenhum mecanismo de sincronização, ambos conseguem concluir a operação.
Resultado: o sistema vendeu um ingresso duas vezes.
Esse é exatamente o tipo de problema que chamamos de race condition.
Primeira abordagem: Pessimistic Locking
O pensamento aqui é simples:
"Eu acredito que conflitos vão acontecer."
Por isso, antes de ler o registro, o sistema bloqueia aquele recurso utilizando um lock exclusivo.
Enquanto uma transação estiver trabalhando, todas as demais aguardam.
É uma solução extremamente segura e muito utilizada quando a consistência é mais importante do que o desempenho.
Por outro lado, existe um custo.
Se milhares de usuários tentarem acessar o mesmo recurso, inevitavelmente será criada uma fila.
Em cenários de alta contenção, isso pode reduzir significativamente o throughput da aplicação.
Segunda abordagem: Optimistic Concurrency Control (OCC)
Essa estratégia parte da ideia oposta.
"Conflitos são raros."
Ninguém bloqueia nada.
Todos podem ler normalmente.
Na hora de atualizar, a aplicação verifica se o estado continua exatamente igual ao que havia sido lido anteriormente.
Caso outro processo tenha alterado aquele registro antes, o UPDATE simplesmente não acontece.
A aplicação detecta o conflito e decide como agir: realizar um retry, informar que o recurso foi alterado ou solicitar uma nova tentativa.
Essa abordagem costuma apresentar excelente desempenho quando conflitos realmente são pouco frequentes.
O problema aparece quando muitos usuários disputam o mesmo recurso.
Nessas situações, vários retries acabam sendo necessários e parte do trabalho realizado é desperdiçada.
Terceira abordagem: Reservations
Na minha opinião, essa costuma ser a melhor solução para fluxos voltados ao usuário.
Em vez de disputar o recurso apenas no momento do pagamento, o sistema cria uma reserva temporária.
Ao selecionar um assento de cinema, um ingresso ou um quarto de hotel, aquele recurso fica reservado durante alguns minutos.
Enquanto isso, outros usuários enxergam o recurso como indisponível.
Se o pagamento for concluído, a reserva é confirmada.
Caso contrário, ela expira automaticamente e volta a ficar disponível.
Perceba que isso reduz drasticamente a janela de concorrência.
A disputa deixa de acontecer durante vários minutos de checkout e passa a acontecer apenas no instante da criação da reserva, normalmente alguns milissegundos.
Além de diminuir conflitos, essa abordagem melhora bastante a experiência do usuário.
Como decidir?
Uma regra prática que gosto de utilizar é a seguinte:
Se todos os dados estão em um único banco de dados, normalmente não há motivo para complicar com soluções distribuídas. Locks ou OCC costumam resolver muito bem.
Se conflitos são frequentes, prefira Pessimistic Locking.
Se conflitos são raros e existem muitas leituras, Optimistic Concurrency Control geralmente oferece melhor desempenho.
Se o cenário envolve usuários comprando, reservando ou realizando checkout, considere utilizar Reservations.
Conclusão
Race condition não é um bug de linguagem, framework ou banco de dados.
É um problema inerente à concorrência.
Quanto maior a escala de um sistema, maior a importância de entender como controlar acesso simultâneo aos mesmos recursos.
Conhecer Pessimistic Locking, Optimistic Concurrency Control e Reservations não significa decorar padrões.
Significa entender os trade-offs envolvidos para escolher a solução certa para cada cenário.
Na engenharia de software, quase nunca existe uma resposta universal.
Existe apenas a decisão mais adequada para o contexto em que o sistema está inserido.