Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
August 11, 2022 05:42 pm GMT

Criando Sistemas de Reservas consistentes com Optimistic Locking, Spring Boot e JPA/Hibernate

No artigo anterior foi utilizado a estratgia de Pessimistic Locking para construo de uma API REST para reserva de mesas de bar. Como visto no artigo existem situaes onde empregar bloqueios fsicos pode ser custoso visando que se diversas linhas estejam bloqueadas haver uma diminuio de produtividade nas transaes de banco de dados.

Caso voc se encontre em uma destas situaes, existem outras estratgias para lidar com Race Conditions, como o caso do Optimistic Locking ou Bloqueio Otimista em portugus.

Optimistic Locking uma estratgia de deteco de conflitos, ao contrrio do Pessimistic Locking que seu foco evitar conflitos, o bloqueio otimista deixa os conflitos acontecerem e ao identific-los reverte as transaes detentoras dos mesmos.

Conhecendo o Optimistic Locking

A ideia principal do bloqueio otimista reduzir o perodo de tempo em que as transaes detm bloqueios fsicos afim prover melhorias no desempenho da aplicao. Antes ao utilizar o bloqueio pessimista uma transao obtia um bloqueio garantindo exclusividade para alterar o recurso. J no bloqueio otimista diversas transaes podem tentar alterar, porm, somente uma ira conseguir e as demais sero abortadas.

Para identificar os conflitos a estrategia utiliza o estado do recurso como um critrio de versionamento, permitindo que apenas as transaes que contenham o recurso com verso equivalente a presente no banco de dados sejam confirmadas (COMMIT).

Optimistic Locking com JPA/Hibernate

JPA/Hibernate oferece suporte para estratgia de Optimistic Locking, para habilita-lo devemos inserir um campo na entidade para representar a verso. Este campo dever ser anotado com @Version para ser gerenciado pela JPA/Hibernate. O atributo de verso deve ser mapeado para java.sql.Timestamp ou qualquer tipo que represente nmeros inteiros, por exemplo, Long, long, int, Integer, short e Short.

@Entitypublic class Mesa {    @Id    @GeneratedValue    private Long id;    @Version    private Long version;    //Demais campos, construtores e metodos omitidos  }

OBS: recomendado que o versionamento de entidades seja atravs de atributos inteiros dado que a verso devera sempre crescer monotonicamente. O perigo de utilizar um atributos de data e hora que por causa de uma sincronizao do Network Time Protocol as datas podem retroceder.

Observe o comportamento ao persistir uma entidade.

@Transactional public void cadastrar(){     Mesa mesa = new Mesa(4);     entityManager.persist(mesa);}

O SQL gerado pela JPA/Hibernate.

INSERT INTO mesa (id, quantidadeDeLugares, disponivelParaReserva, version)VALUES (1, 4, 1, 0);

Dado que o mecanismo de bloqueio otimista est habilitado para uma entidade para cada operao de atualizao disparado uma checagem para verificar se a verso da entidade em memria corresponde a verso da entidade no banco de dados, caso corresponda atualizao confirmada e a verso da entidade incrementada. Caso contrario a transao sera revertida.

Observe abaixo um exemplo de atualizao bem sucedida.

@Transactional public void atualizarQuantidadeLugares(Long mesaId, int novaQuantidadeLugares){     Mesa mesa = entityManager.find(Mesa.class, mesaId);     mesa.atualizarQuantidadeDeLugares(novaQuantidadeLugares);}

O Hibernate gera a seguinte sada:

SELECT *   FROM mesa m WHERE m.id = :idUPDATE mesa   SET quantidadeDeLugares=:qtdLugeres, version = 1 WHERE id=:id AND version = 0;

Caso o retorno da operao UPDATE seja a contagem de 1 linha atualizada a operao SQL UPDATE sera commitada. Caso seja 0 sera lanada uma Exceo do tipo OptimisticLockException e automaticamente a transao sera revertida.

Para entender melhor, imagine que duas transaes desejem atualizar informaes da mesma entidade de maneira simultnea, ambas as transaes iram obter a mesma verso de entidade. A primeira transao (Tx1) a terminar, ir persistir suas alteraes, e incrementar a verso da entidade. A segunda transao (Tx2) no tem cincia que a verso da entidade foi incrementada, e dispara sua atualizao, porm, a verso de presente em memria precede a verso presente no banco, portanto a segunda transao abortada.

Evitando uma perda de atualizao com Optimistic Locking

Construindo uma API REST para reserva de mesas com Optimistic Locking

Para se beneficiar do mecanismo de Optimistic Locking em nossa API de reserva, devemos implementar algumas mudanas em comparao a implementao anterior. Antes onde era adquirido um bloqueio pessimista atravs da consulta findByIdWithPessimisticLock( ) agora no mais necessrio dado que uma checagem de verso sera realizada pela JPA/Hibernate. Ento podemos substituir a consulta pelo mtodo default findById( ).

Um possvel implementao para lgica descrita acima :

@PostMapping("/mesas/{id}/reservas")@Transactionalpublic ResponseEntity<?> reservar(        @PathVariable(value = "id") Long mesaId,        @RequestBody ReservaMesaRequest request,        UriComponentsBuilder uriBuilder) {    Mesa mesa = mesaRepository.findById(mesaId)            .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "Mesa no cadastrada"));    Usuario usuario = usuarioRepository.findById(request.getUsuarioId())            .orElseThrow(() -> new ResponseStatusException(UNPROCESSABLE_ENTITY, "Usuario no cadastrado"));    LocalDateTime dataDaReserva = request.getDataReserva();    if (reservaRepository.existsMesaByIdAndReservadoParaIs(mesaId, dataDaReserva)) {        throw new ResponseStatusException(UNPROCESSABLE_ENTITY, "Horario indisponivel para reserva");    }    Reserva reserva = mesa.reservar(usuario, dataDaReserva);    reservaRepository.save(reserva);    URI location = uriBuilder.path("/mesas/{id}/reservas/{reservaId}")            .buildAndExpand(mesa.getId(), reserva.getId())            .toUri();    return ResponseEntity.created(location).build();}

Sabemos que se uma operao de atualizao UPDATE falhar no mecanismo de checagem de verso uma ObjectOptimisticLockingFailureExceptionser lanada, e se o sistema no estive preparado um erro inesperado sera retornado ao cliente, ento importante que a exceo seja tratada e uma mensagem amigvel e coerente ao caso seja dada ao cliente.

Segundo a RFC 7231 quando uma solicitao resulta em erro dado ao estado atual do recurso no servidor de destino o indicado que a resposta a solicitao contenha o HTTP Status 409 Conflict.

Exemplo de Exception Handler com Spring:

@RestControllerAdvicepublic class DeafultExceptionHandler {        @ExceptionHandler(ObjectOptimisticLockingFailureException.class)    public ResponseEntity<?> optmisticLock(ObjectOptimisticLockingFailureException ex) {        String msg = "Aconteceu um conflito em sua atualizao, tente novamente.";        return status(409)                .body(                        Map.of("mensagem",msg)                );    }}

Concluso

O mecanismo de bloqueio otimista da JPA/Hibernate trabalha com a estratgia de deteco de conflitos. Isto que significa que o tempo em que o registro fica bloqueado reduzido. Antes um bloqueio era adquirido no momento da leitura e era liberado ao fim da transao. Agora diversas transaes simultneas podem tentar atualizar o registro, mas apenas uma conseguir.

Estrategia de bloqueio otimista pode trazer uma melhoria em performance para seu sistema como um todo na maioria dos casos, porm, no se engane, caso o ndice de conflitos seja alto o Optimistic Locking pode ser mais custoso que um bloqueio pessimista, dado que ser necessrio um alto esforo do banco de dados para reverter transaes. Outro fator que corrobora uma possvel perda de performance que bloqueios otimistas bem implementados requerem retries o que pode aumentar o tempo de resposta da sua aplicao.

Enfim, se seu sistema possui um ndice de concorrncia baixo ou moderado o uso de Optimistic Locking uma tima opo, pois oferece ganhos de consistncia sem sacrificar a performance.

Referncias


Original Link: https://dev.to/jordihofc/criando-sistemas-de-reservas-consistentes-com-optimistic-locking-spring-boot-e-jpahibernate-2h8b

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To