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

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

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

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)