🎬 O Guia basico sobre Optimistic vs Pessimistic Lock no Spring Boot 🍿
O Desafio: O Último Ingresso
Imagine que temos uma tabela SeatDetails com o campo isBooked (status do assento). Se 100 pessoas tentarem marcar o assento A1 ao mesmo tempo, sem controle, várias vão ler "disponível" e tentar atualizar ao mesmo tempo. Resultado: sobreposição de dados, ticket vendido duas vezes e uma baita dor de cabeça. Aí entram os Locking Strategies para garantir que a venda seja atômica e consistente.
⚡ 1. Optimistic Locking (O "Achismo" Otimista)
O otimista não segura a porta. Ele deixa todo mundo tentar, mas na hora de finalizar, ele confere se alguém passou na frente.
Como funciona? Adicionamos um campo version na entidade com a anotação @Version. Quando alguém atualiza o assento, o JPA incrementa a versão.
@Entity
public class SeatDetails {
@Id
private Long id;
private boolean isBooked;
@Version
private Long version; // O segredo está aqui!
// getters e setters
}
Na Prática:
- Usuário A e B leem o assento (versão = 1).
- A atualiza e commita (versão vira 2).
- B tenta atualizar baseado na versão 1.
- Resultado: O Hibernate lança um
OptimisticLockException. B perdeu, mas não bagunçou o banco.
Quando usar? Em cenários de baixa concorrência (edição de perfil de usuário, por exemplo).
🛡️ 2. Pessimistic Locking (O "Chato" Pragmático)
O pessimista trava a porta na cara. Quando alguém entra para editar, ele tranca a fileira do cinema até terminar.
Como funciona? Usamos o @Lock do Spring Data JPA com o modo PESSIMISTIC_WRITE. Ele manda o banco de dados executar um SELECT ... FOR UPDATE, travando a linha.
public interface SeatRepo extends JpaRepository<SeatDetails, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT s FROM SeatDetails s WHERE s.id = :id")
Optional<SeatDetails> findByIdWithPessimisticLock(@Param("id") Long id);
}
Na Prática:
- Usuário A chama o método com
@Transactional. - O banco trava a linha do assento A1.
- Usuário B tenta ler o mesmo assento e fica na fila de espera (bloqueado) até A terminar.
- O
@Transactionalgarante que, ao final do processo, o lock seja liberado (commit ou rollback).
Quando usar? Em cenários de alta concorrência e disputa feroz (leilões, Black Friday, estoque final).
🚀 3. O Novo Ator no Pedaço: RedLock com Redis (Quando o Banco Não Dá Conta do Gole)
E quando sua aplicação já cresceu tanto que tem várias instâncias rodando (microserviços em containers, serverless) e você precisa coordenar a reserva do assento entre diferentes serviços, não só entre threads de uma única JVM? É aí que o banco de dados sozinho já não resolve mais.
O Pessimistic Lock funciona lindamente dentro da mesma instância do banco, mas se o assento A1 for acessado por APIs diferentes que usam bancos diferentes? Ou se seu serviço for stateless (tipo um Lambda) e não segurar uma transação de banco aberta por muito tempo? Você precisa de um Distributed Lock, e aí entra o RedLock.
Pense no RedLock como o segurança trancando não apenas uma porta, mas todas as saídas de emergência do cinema. Ele impede que qualquer instância do seu sistema, rodando em qualquer lugar, mexa no recurso ao mesmo tempo.
Mas por que não usar um simples SETNX do Redis? Porque se o nó master do Redis cair e você fizer failover para um Slave, o lock pode se perder. É como se o segurança trocasse de turno no meio da briga e esquecesse quem estava com a chave.
O RedLock resolve isso usando vários master independentes. Para conseguir a chave, o cliente precisa convencer a maioria deles (mais da metade) simultaneamente. Só assim ele garante que o lock é verdadeiro e que não vai sumir.
Na prática, você teria 5 masters Redis completamente separados (em máquinas diferentes). Para trancar o assento, o cliente:
- Marca o horário que começou a tentar.
- Tenta trancar o mesmo recurso em todos os 5 masters (com um tempo de validade curto, tipo 30 segundos).
- Se conseguir trancar na maioria (pelo menos 3), o lock é dele. E o tempo que ele realmente pode usar o lock é o tempo total inicial menos o tempo que ele gastou tentando.
- Se falhar, ele libera os locks que conseguiu em qualquer nó e tenta de novo.
O Redisson (a biblioteca mais usada) faz quase tudo isso pra você. O código fica lindo:
<!-- Adicione no seu pom.xml -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.1</version>
</dependency>
// No seu serviço...
@Service
public class ReservaService {
@Autowired
private RedissonClient redissonClient;
public void reservarAssento(String assentoId) {
RLock lock = redissonClient.getLock("lock:assento:" + assentoId);
if (lock.tryLock()) {
try {
// Só UM cliente (ou instância) por vez entra aqui!
// Verifica se o assento ainda está livre
// Reserva o assento
// Salva a reserva no banco
} finally {
lock.unlock(); // Não esquece de liberar!
}
} else {
throw new RuntimeException("Assento ocupado, tente novamente!");
}
}
}
Mas atenção! O RedLock não é bala de prata. O famoso Martin Kleppmann (autor de Designing Data-Intensive Applications, de onde eu tirei parte desse texto kkkk 🤫) tem uma baita crítica a ele: o RedLock assume que o relógio de todas as máquinas está perfeitamente sincronizado. No mundo real, com clock drift (relógio atrasando ou adiantando) ou pausas longas de Garbage Collector, um lock pode expirar antes da hora, dando a impressão que o recurso está livre quando na verdade ainda está sendo usado. Se você precisa de correção absoluta (ex: transferência bancária), o RedLock pode ser arriscado.
🤔 Quando Usar RedLock?
| Cenário | Recomendação |
|---|---|
| Seu sistema é uma única aplicação Spring | Fique com Optimistic/Pessimistic Lock. |
| Você precisa de PERFORMANCE e tolera falhas raras (ex: um job duplicado num cache) | Use Redis simples com SETNX. |
| Você precisa de CORREÇÃO ABSOLUTA (ex: débito em conta bancária) | Esqueça RedLock. Use ZooKeeper, etcd ou um banco relacional com transações fortes. |
| Cenário híbrido: vários microserviços e precisão moderada | RedLock pode ser um bom caminho, mas entenda os riscos. |
📊 Tabela Rápida: Qual devo usar?
| Característica | Optimistic Lock | Pessimistic Lock | RedLock (Redis) |
|---|---|---|---|
| Estratégia | "Tenta aí, se falhar, tenta de novo." | "Tranca tudo, depois você mexe." | "Tranca em múltiplos lugares antes de agir." |
| Bloqueio | Nenhum bloqueio enquanto lê. | Bloqueio físico no banco (SELECT FOR UPDATE). |
Usa SET NX em vários masters. |
| Performance | Alta performance em leitura. | Performance menor (espera nas filas). | Rápida, mas requer ida em vários nós. |
| Melhor para | Leituras freq., escritas esporádicas. | Escritas freq. e muitos conflitos. | Ambientes Distribuídos (vários serviços). |
| Risco | Muitas exceções se a disputa for alta. | Deadlocks ou lentidão. | Clock drift e falhas de sincronização. |
| Código | Anotação @Version na entidade. |
Anotação @Lock no Repository. |
Biblioteca Redisson ou cliente específico. |
💡 Dica de Implementação (para não errar no código)
Optimistic: Se você receber uma OptimisticLockException, é legal tratar isso na camada de serviço. Dá pra tentar uma retentativa automática (reler os dados e tentar salvar de novo) ou avisar o usuário: "Ops, alguém alterou esse dado. Recarregue a tela."
Pessimistic: Lembre-se sempre de rodar dentro de um método @Transactional. Se você só fizer o findByIdWithPessimisticLock e não tiver o @Transactional, o lock morre sozinho.
🎯 Conclusão (Na Pipoca)
O Optimistic Locking é perfeito para quando a briga é rara. É leve, rápido e não entope o banco.
O Pessimistic Locking é aquele segurança que trava a catraca em shows lotados: trava o banco, mas garante que ninguém vai invadir o assento.
E o RedLock com Redis é o segurança que coordena vários portões de diferentes cinemas da rede: ideal quando seus serviços estão todos espalhados, mas tem suas controvérsias e não é a bala de prata para precisão extrema.
No final das contas, não existe bala de prata. A escolha depende do seu negócio: quantas pessoas vão disputar esse assento ao mesmo tempo? Se for um filme do Pelé em dia de estreia, melhor preparar o lock e o Refri. 😉
Gostou? Tem um sistema de reservas aí na sua empresa? Já usou RedLock? Conta aí nos comentários qual dessas estratégias você mais usa e se já sofreu com algum lock. 🚀
Comments
No comments yet. Start the discussion.