Share
Evitando Race Conditions em aplicações distribuídas com Spring Boot
Softwares são ferramentas que automatizam rotinas, regras e procedimentos de um modelo de negócio. Então já é de se esperar que alguns nichos possuam maiores desafios do que outros. Existe um domínio de sistemas que traz contigo grande probabilidade de ocorrer problemas devido à alta concorrência por uso de um recurso de software, e estou me referindo aos sistemas que envolvem realizar reservas.
Sistemas que realizam Reservas, estão sujeitos a sofrer com problemas de condições de corrida (Race Conditions), pois por natureza a solução em software é criada baseado no padrão: leitura-processamento-escrita. E soluções baseadas neste padrão abrem pequenas janelas de tempo onde é possível que outro processo execute a reserva do recurso, e se seu sistema não estiver preparado ocorrerá o que chamamos de atualização perdida ou Lost Update, permitindo que mais de um processo reserve o mesmo recurso. Dado a isso é importante que o desenvolvedor possua heurísticas para identificar Race Conditions e proteger seu sistema, já que a maioria dos WebServices e REST APIs nascem concorrentes por natureza.
A ideia desta talk é trazer de maneira muito didática uma visão de como uma pessoa desenvolvedora independente do estágio de carreira consegue identificar problemas de Race Conditions, anomalias em banco de dados relacionais e como implementar estratégias de controle de concorrência extraindo o máximo da aplicação e do Banco de dados.
Jordi Henrique Silva
Transcript
178 slides
-
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(Long id, String usuario) { //aqui vem a logica de reserva Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(Long id, String usuario) { //aqui vem a logica de reserva Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(Long id, String usuario) { //aqui vem a logica de reserva Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(Long id, String usuario) { //aqui vem a logica de reserva Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping public ResponseEntity<?> reservar(Long id, String usuario) { //aqui vem a logica de reserva Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(Long id, String usuario) { //aqui vem a logica de reserva Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { //aqui vem a logica de reserva Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { //aqui vem a logica de reserva Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { //aqui vem a logica de reserva Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { //aqui vem a logica de reserva Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
E se mais de um usuário tentar reservar a mesma
poltrona simultaneamente? -
João Maria select p.* from poltrona p where id =
1 select p.* from poltrona p where id = 1 -
Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: João
Maria select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 -
Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: João
Maria select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: -
Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: João
Maria update poltrona set reservado = true, reservadoPara = ‘joao’ where id = 1 select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: -
Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: João
Maria update poltrona set reservado = true, reservadoPara = ‘joao’ where id = 1 select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: update poltrona set reservado = true, reservadoPara = ‘maria’ where id = 1 -
1 linha atualizada Poltrona -------------- id: 1, descriçao: A3 reservado:
false reservadoPara: Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: joao João Maria update poltrona set reservado = true, reservadoPara = ‘joao’ where id = 1 select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: update poltrona set reservado = true, reservadoPara = ‘maria’ where id = 1 -
1 linha atualizada Poltrona -------------- id: 1, descriçao: A3 reservado:
false reservadoPara: Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: joao João Maria Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: maria update poltrona set reservado = true, reservadoPara = ‘joao’ where id = 1 select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: update poltrona set reservado = true, reservadoPara = ‘maria’ where id = 1 1 linha atualizada -
1 linha atualizada Poltrona -------------- id: 1, descriçao: A3 reservado:
false reservadoPara: Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: joao João Maria Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: maria update poltrona set reservado = true, reservadoPara = ‘joao’ where id = 1 select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: update poltrona set reservado = true, reservadoPara = ‘maria’ where id = 1 1 linha atualizada -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { //read Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); //process if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); //write repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { //read Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); //process if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); //write repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { //read Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); //process if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); //write repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam String usuario) { //read Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); //process if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); //write repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public syncronized ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario){ } } -
POST /poltronas/1/reservas HTTP 200 HTTP 422, “Poltrona indisponível” Thread 1
Thread 2 POST /poltronas/1/reservas -
public interface PoltronaRepository extends JpaRepository<Poltrona,Long> { @Query("select p from Poltrona
p where p.id = :id") Optional<Poltrona> findByIdWithPessimisticLock(Long id); } -
public interface PoltronaRepository extends JpaRepository<Poltrona,Long> { @Lock(LockModeType.PESSIMISTIC_WRITE) @Query("select p from
Poltrona p where p.id = :id") Optional<Poltrona> findByIdWithPessimisticLock(Long id); } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario){ Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario){ Poltrona poltrona = repository.findByIdWithPessimisticLocking(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
João Maria select p.* from poltrona p where id =
1 for update -
João Maria select p.* from poltrona p where id =
1 for update select p.* from poltrona p where id = 1 for update -
Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: João
Maria select p.* from poltrona p where id = 1 for update select p.* from poltrona p where id = 1 for update -
Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: João
Maria select p.* from poltrona p where id = 1 for update select p.* from poltrona p where id = 1 for update -
Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: João
Maria select p.* from poltrona p where id = 1 for update select p.* from poltrona p where id = 1 for update -
Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: João
Maria update poltrona set reservado = true, reservadoPara = ‘joao’ where id = 1 select p.* from poltrona p where id = 1 for update select p.* from poltrona p where id = 1 for update -
Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: João
Maria COMMIT update poltrona set reservado = true, reservadoPara = ‘joao’ where id = 1 select p.* from poltrona p where id = 1 for update select p.* from poltrona p where id = 1 for update -
1 linha atualizada Poltrona -------------- id: 1, descriçao: A3 reservado:
false reservadoPara: Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: joao João Maria COMMIT update poltrona set reservado = true, reservadoPara = ‘joao’ where id = 1 select p.* from poltrona p where id = 1 for update select p.* from poltrona p where id = 1 for update -
1 linha atualizada Poltrona -------------- id: 1, descriçao: A3 reservado:
false reservadoPara: Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: joao João Maria Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: joao COMMIT update poltrona set reservado = true, reservadoPara = ‘joao’ where id = 1 select p.* from poltrona p where id = 1 for update select p.* from poltrona p where id = 1 for update -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario){ Poltrona poltrona = repository.findByIdWithPessimisticLocking(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
Se houver alto volume de requisições causando alta espera por
acesso da linha? -
@Entity public class Poltrona { @Id @GeneratedValue private Long id;
private String descricao; private String reservadoPara; private boolean reservado = false; @Version private Long version; } -
@Entity public class Poltrona { @Id @GeneratedValue private Long id;
private String descricao; private String reservadoPara; private boolean reservado = false; @Version private Long version; } -
@Entity public class Poltrona { @Id @GeneratedValue private Long id;
private String descricao; private String reservadoPara; private boolean reservado = false; @Version private Long version; } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario){ Poltrona poltrona = repository.findByIdWithPessimistcLock(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario){ Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario){ Poltrona poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if(poltrona.isReservada()){ throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); repository.save(poltrona); return ResponseEntity.ok().build(); } } -
update poltrona set reservado = true, reservadoPara = :user where
id = :poltronaId -
update poltrona set version = 1, reservado = true, reservadoPara
= :user where id = :poltronaId and version = 0 -
João Maria select p.* from poltrona p where id =
1 select p.* from poltrona p where id = 1 -
Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version:
0 João Maria select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 -
Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version:
0 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version: 0 João Maria select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 -
Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version:
0 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version: 0 update poltrona set version = 1 reservado = true, reservadoPara = ‘joao’ where id = 1 and version = 0 João Maria select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 -
1 linha atualizada Poltrona -------------- id: 1, descriçao: A3 reservado:
false reservadoPara: version: 0 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version: 0 Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: joao version: 1 update poltrona set version = 1 reservado = true, reservadoPara = ‘joao’ where id = 1 and version = 0 João Maria select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 -
1 linha atualizada Poltrona -------------- id: 1, descriçao: A3 reservado:
false reservadoPara: version: 0 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version: 0 Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: joao version: 1 update poltrona set version = 1 reservado = true, reservadoPara = ‘joao’ where id = 1 and version = 0 João Maria select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 -
1 linha atualizada Poltrona -------------- id: 1, descriçao: A3 reservado:
false reservadoPara: version: 0 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version: 0 Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: joao version: 1 update poltrona set version = 1 reservado = true, reservadoPara = ‘maria’ where id = 1 and version = 0 update poltrona set version = 1 reservado = true, reservadoPara = ‘joao’ where id = 1 and version = 0 João Maria select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 -
1 linha atualizada Poltrona -------------- id: 1, descriçao: A3 reservado:
false reservadoPara: version: 0 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version: 0 Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: joao version: 1 update poltrona set version = 1, reservado = true, reservadoPara = ‘maria’ where id = 1 and version = 0 update poltrona set version = 1, reservado = true, reservadoPara = ‘joao’ where id = 1 and version = 0 João Maria OptimisticLockException select p.* from poltrona p where id = 1 select p.* from poltrona p where id = 1 -
João POST /poltronas/1/reservas select p.* from poltrona p where id
= 1 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version: 0 -
João POST /poltronas/1/reservas select p.* from poltrona p where id
= 1 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version: 0 update poltrona set version = 1, reservado = true, reservadoPara = ‘maria’ where id = 1 and version = 0 -
João POST /poltronas/1/reservas select p.* from poltrona p where id
= 1 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version: 0 update poltrona set version = 1, reservado = true, reservadoPara = ‘maria’ where id = 1 and version = 0 Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: maria version: 1 -
João POST /poltronas/1/reservas select p.* from poltrona p where id
= 1 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version: 0 update poltrona set version = 1, reservado = true, reservadoPara = ‘maria’ where id = 1 and version = 0 Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: maria version: 1 update poltrona set version = 1, reservado = true, reservadoPara = ‘joao’ where id = 1 and version = 0 -
João POST /poltronas/1/reservas select p.* from poltrona p where id
= 1 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version: 0 update poltrona set version = 1, reservado = true, reservadoPara = ‘maria’ where id = 1 and version = 0 Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: maria version: 1 update poltrona set version = 1, reservado = true, reservadoPara = ‘joao’ where id = 1 and version = 0 OptimisticLockException -
João POST /poltronas/1/reservas select p.* from poltrona p where id
= 1 Poltrona -------------- id: 1, descriçao: A3 reservado: false reservadoPara: version: 0 update poltrona set version = 1, reservado = true, reservadoPara = ‘maria’ where id = 1 and version = 0 Poltrona -------------- id: 1, descriçao: A3 reservado: true reservadoPara: maria version: 1 update poltrona set version = 1, reservado = true, reservadoPara = ‘joao’ where id = 1 and version = 0 OptimisticLockException HTTP 500 -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario) { PoltronaOptimisticLocking poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); try { repository.save(poltrona); } catch (ObjectOptimisticLockingFailureException ex) { throw new ResponseStatusException(CONFLICT, "Impossivel realizar a reserva, tente novamente", ex); } return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario) { PoltronaOptimisticLocking poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); try { repository.save(poltrona); } catch (ObjectOptimisticLockingFailureException ex) { throw new ResponseStatusException(CONFLICT, "Impossivel realizar a reserva, tente novamente", ex); } return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario) { PoltronaOptimisticLocking poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); try { repository.save(poltrona); } catch (ObjectOptimisticLockingFailureException ex) { throw new ResponseStatusException(CONFLICT, "Impossivel realizar a reserva, tente novamente", ex); } return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario) { PoltronaOptimisticLocking poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); try { repository.save(poltrona); } catch (ObjectOptimisticLockingFailureException ex) { throw new ResponseStatusException(CONFLICT, "Impossivel realizar a reserva, tente novamente", ex); } return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario) { PoltronaOptimisticLocking poltrona = repository.findById(id) .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Poltrona não existente.")); if (poltrona.isReservada()) { throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Esta poltrona já esta reservada"); } poltrona.setReservado(true); poltrona.setReservadoPara(usuario); try { repository.save(poltrona); } catch (ObjectOptimisticLockingFailureException ex) { throw new ResponseStatusException(CONFLICT, "Impossivel realizar a reserva, tente novamente", ex); } return ResponseEntity.ok().build(); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario) {...} @ExceptionHandler(ObjectOptimisticLockingFailureException.class) public ResponseEntity<?> optimisticLockingHandler(ObjectOptimisticLockingFailureException ex) { Map<String, String> response = Map.of("mensagem", "Impossivel realizar a reserva, tente novamente"); return ResponseEntity.status(CONFLICT).body(response); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario) {...} @ExceptionHandler(ObjectOptimisticLockingFailureException.class) public ResponseEntity<?> optimisticLockingHandler(ObjectOptimisticLockingFailureException ex) { Map<String, String> response = Map.of("mensagem", "Impossivel realizar a reserva, tente novamente"); return ResponseEntity.status(CONFLICT).body(response); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario) {...} @ExceptionHandler(ObjectOptimisticLockingFailureException.class) public ResponseEntity<?> optimisticLockingHandler(ObjectOptimisticLockingFailureException ex) { Map<String, String> response = Map.of("mensagem", "Impossivel realizar a reserva, tente novamente"); return ResponseEntity.status(CONFLICT).body(response); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario) {...} @ExceptionHandler(ObjectOptimisticLockingFailureException.class) public ResponseEntity<?> optimisticLockingHandler(ObjectOptimisticLockingFailureException ex) { Map<String, String> response = Map.of("mensagem", "Impossivel realizar a reserva, tente novamente"); return ResponseEntity.status(CONFLICT).body(response); } } -
@RestController public class ReservaPoltronaController { @Autowired private PoltronaRepository repository; @Transactional
@PostMapping("api/v1/poltronas/{id}/reservas") public ResponseEntity<?> reservar(@PathVariable Long id, @RequestParam @NotBlank String usuario) {...} @ExceptionHandler(ObjectOptimisticLockingFailureException.class) public ResponseEntity<?> optimisticLockingHandler(ObjectOptimisticLockingFailureException ex) { Map<String, String> response = Map.of("mensagem", "Impossivel realizar a reserva, tente novamente"); return ResponseEntity.status(CONFLICT).body(response); } } -
Pessimistic Locking Estratégia: Evitar Conflitos Quando utilizar: alto índice de
conflitos, ambientes altamente concorrentes das transações -
Pessimistic Locking Estratégia: Evitar Conflitos Quando utilizar: alto índice de
conflitos, ambientes altamente concorrentes Vantagem: Aumento da consistência -
Pessimistic Locking Estratégia: Evitar Conflitos Quando utilizar: alto índice de
conflitos, ambientes altamente concorrentes Vantagem: Aumento da consistência Desvantagem: Pode gerar alto nível de contenção levando a queda de produtividade das transações -
Optimistic Locking Estratégia: Detectar Conflitos Quando utilizar: baixo índice de
conflitos, ambientes razoavelmente concorrentes -
Optimistic Locking Estratégia: Detectar Conflitos Quando utilizar: baixo índice de
conflitos, ambientes razoavelmente concorrentes Vantagem: Menor tempo de bloqueio, menor contenção de transações -
Optimistic Locking Estratégia: Detectar Conflitos Quando utilizar: baixo índice de
conflitos, ambientes razoavelmente concorrentes Vantagem: Menor tempo de bloqueio, menor contenção de transações Desvantagem: em ambientes concorrentes pode gerar alto custo computacional, dado ao grande numero de rollbacks em transações -
Pessimistic Locking Optimistic Locking Evitar Conflitos Detectar Conflitos Alto índice
de concorrência Baixo/médio índice de concorrência Método mais consistente Menor tempo de bloqueio Em cenários com índice de concorrência exagerados pode levar a um grande tempo de bloqueio Aumento na quantidade transações revertidas (rollbacks), exige retentivas (retries) -
Pessimistic Locking Optimistic Locking Evitar Conflitos Detectar Conflitos Alto índice
de concorrência Baixo/médio índice de concorrência Método mais consistente Menor tempo de bloqueio Em cenários com índice de concorrência exagerados pode levar a um grande tempo de bloqueio Aumento na quantidade transações revertidas (rollbacks), exige retentivas (retries) -
Pessimistic Locking Optimistic Locking Evitar Conflitos Detectar Conflitos Alto índice
de concorrência Baixo/médio índice de concorrência Método mais consistente Menor tempo de bloqueio Em cenários com índice de concorrência exagerados pode levar a um grande tempo de bloqueio Aumento na quantidade transações revertidas (rollbacks), exige retentivas (retries) -
Pessimistic Locking Optimistic Locking Evitar Conflitos Detectar Conflitos Alto índice
de concorrência Baixo/médio índice de concorrência Método mais consistente Menor tempo de bloqueio Em cenários com índice de concorrência exagerados pode levar a um grande tempo de bloqueio Aumento na quantidade transações revertidas (rollbacks), exige retentivas (retries) -
Pessimistic Locking Optimistic Locking Evitar Conflitos Detectar Conflitos Alto índice
de concorrência Baixo/médio índice de concorrência Método mais consistente Menor tempo de bloqueio Em cenários com índice de concorrência exagerados pode levar a um grande tempo de bloqueio Aumento na quantidade transações revertidas (rollbacks), exige retentivas (retries) -
Pessimistic Locking Optimistic Locking Evitar Conflitos Detectar Conflitos Alto índice
de concorrência Baixo/médio índice de concorrência Método mais consistente Menor tempo de bloqueio Em cenários com índice de concorrência exagerados pode levar a um grande tempo de bloqueio Aumento na quantidade transações revertidas (rollbacks), exige retentivas (retries) -
Pessimistic Locking Optimistic Locking Evitar Conflitos Detectar Conflitos Alto índice
de concorrência Baixo/médio índice de concorrência Método mais consistente Menor tempo de bloqueio Em cenários com índice de concorrência exagerados pode levar a um grande tempo de bloqueio Aumento na quantidade transações revertidas (rollbacks), exige retentivas (retries) -
Pessimistic Locking Optimistic Locking Evitar Conflitos Detectar Conflitos Alto índice
de concorrência Baixo/médio índice de concorrência Método mais consistente Menor tempo de bloqueio Em cenários com índice de concorrência exagerados pode levar a um grande tempo de bloqueio Aumento na quantidade transações revertidas (rollbacks), exige retentivas (retries) -
Pessimistic Locking Optimistic Locking Evitar Conflitos Detectar Conflitos Alto índice
de concorrência Baixo/médio índice de concorrência Método mais consistente Menor tempo de bloqueio Em cenários com índice de concorrência exagerados pode levar a um grande tempo de bloqueio Aumento na quantidade transações revertidas (rollbacks), exige retentivas (retries) -
Pessimistic Locking Optimistic Locking Evitar Conflitos Detectar Conflitos Alto índice
de concorrência Baixo/médio índice de concorrência Método mais consistente Menor tempo de bloqueio Em cenários com índice de concorrência exagerados pode levar a um grande tempo de bloqueio Aumento na quantidade transações revertidas (rollbacks), exige retentivas (retries) -
Pessimistic Locking Optimistic Locking Evitar Conflitos Detectar Conflitos Alto índice
de concorrência Baixo/médio índice de concorrência Método mais consistente Menor tempo de bloqueio Em cenários com índice de concorrência exagerados pode levar a um grande tempo de bloqueio Aumento na quantidade transações revertidas (rollbacks), exige retentivas (retries)