Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Evitando Race Conditions em aplicações distribu...

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

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.

Avatar for Jordi Henrique Silva

Jordi Henrique Silva

November 15, 2022
Tweet

More Decks by Jordi Henrique Silva

Other Decks in Technology

Transcript

  1. @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(); } }
  2. @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(); } }
  3. @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(); } }
  4. @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(); } }
  5. @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(); } }
  6. @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(); } }
  7. @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(); } }
  8. @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(); } }
  9. @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(); } }
  10. @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(); } }
  11. @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(); } }
  12. @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(); } }
  13. @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(); } }
  14. @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(); } }
  15. @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(); } }
  16. @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(); } }
  17. @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(); } }
  18. João Maria select p.* from poltrona p where id =

    1 select p.* from poltrona p where id = 1
  19. 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
  20. 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:
  21. 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:
  22. 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
  23. 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
  24. 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
  25. 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
  26. @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(); } }
  27. @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(); } }
  28. @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(); } }
  29. @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(); } }
  30. @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){ } }
  31. public interface PoltronaRepository extends JpaRepository<Poltrona,Long> { @Query("select p from Poltrona

    p where p.id = :id") Optional<Poltrona> findByIdWithPessimisticLock(Long id); }
  32. 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); }
  33. @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(); } }
  34. @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(); } }
  35. João Maria select p.* from poltrona p where id =

    1 for update select p.* from poltrona p where id = 1 for update
  36. 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
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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
  42. 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
  43. @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(); } }
  44. @Entity public class Poltrona { @Id @GeneratedValue private Long id;

    private String descricao; private String reservadoPara; private boolean reservado = false; @Version private Long version; }
  45. @Entity public class Poltrona { @Id @GeneratedValue private Long id;

    private String descricao; private String reservadoPara; private boolean reservado = false; @Version private Long version; }
  46. @Entity public class Poltrona { @Id @GeneratedValue private Long id;

    private String descricao; private String reservadoPara; private boolean reservado = false; @Version private Long version; }
  47. @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(); } }
  48. @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(); } }
  49. @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(); } }
  50. update poltrona set version = 1, reservado = true, reservadoPara

    = :user where id = :poltronaId and version = 0
  51. João Maria select p.* from poltrona p where id =

    1 select p.* from poltrona p where id = 1
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. 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
  58. 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
  59. 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
  60. 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
  61. 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
  62. 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
  63. 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
  64. 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
  65. @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(); } }
  66. @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(); } }
  67. @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(); } }
  68. @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(); } }
  69. @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(); } }
  70. @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); } }
  71. @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); } }
  72. @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); } }
  73. @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); } }
  74. @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); } }
  75. Pessimistic Locking Estratégia: Evitar Conflitos Quando utilizar: alto índice de

    conflitos, ambientes altamente concorrentes das transações
  76. Pessimistic Locking Estratégia: Evitar Conflitos Quando utilizar: alto índice de

    conflitos, ambientes altamente concorrentes Vantagem: Aumento da consistência
  77. 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
  78. 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
  79. 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
  80. 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)
  81. 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)
  82. 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)
  83. 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)
  84. 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)
  85. 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)
  86. 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)
  87. 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)
  88. 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)
  89. 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)
  90. 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)