Race Condition: três formas de resolver um dos problemas mais comuns em sistemas concorrentes
← Voltar para o blog
Desenvolvimento
25 views5 min de leitura

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.