quarta-feira, 25 de outubro de 2017

Armadilhas do REQUIRED_NEW transaction no EJB e Spring

Pesquisando sobre um erro de duplicação de dados e problemas com transações no EJB encontrei essa explicação muito interessante. O atributo REQUIRED_NEW tanto no EJB quanto no Spring podem ter resultados inesperados e levar a dados corrompidos e inconsistentes. Isso pode ocorrer devido o fato de ele sempre iniciar uma nova transação quando o método é chamado, mesmo já existindo uma transação ou não.

Veja o Exemplo:

@Transactional(propagation=Propagation.REQUIRES_NEW)
public long insertTrade(TradeData trade) throws Exception
{
          ...
}

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void updateAcct(TradeData trade) throws Exception
{
   ...
}

Nesse caso os 2 métodos são públicos e estão anotados com REQUIRED_NEW. Quando chamados de forma separada, não há problema. Porém, se um for usado os dois métodos dentro de uma mesma unidade de trabalho transacionada, como por exemplo um outro método, isso pode causar inconsistências. Por exemplo, digamos que dentro do método insertTrade() você chame o método updadeAcct(), se ocorrer um rolled back depois do chamada do updateAcct(), ele já vai ter efetuado o commit no banco de dados, sendo desfeito apenas o que pertence ao insertTrade().

@Transactional(propagation=Propagation.REQUIRES_NEW)
public long insertTrade(TradeData trade) throws Exception {
       em.persist(trade);
       updateAcct(trade);
      //exception occurs here! Trade rolled back mas updateAcct(trade) não!
      ...
}



Isso acontece porque uma nova transação é iniciada no método updateAcct() criando uma árvore de transações, e houve um commit quando ele terminou sua execução, independentemente do que ocorra em outra transação da árvore. Devido a esse comportamento, o atributo de transação REQUIRES_NEW deve ser usado somente se a ação do banco de dados no método que está sendo invocado precisa ser salva no banco de dados independentemente do resultado da transação do topo da árvore (como uma funcionalidade de auditoria por exemplo).

O ponto principal é sempre usar o atributo MANDATORY ou REQUIRED vez de REQUIRES_NEW menos que você tenha um motivo para usá-lo.

         Esses são problemas difíceis de serem detectados no momento do desenvolvimento e de testes, pois geralmente ocorrem quando o processamento da aplicação e o acesso ao banco de dados estão muito altos.

          Aconselho uma passada por esse outro post que deixo o link logo abaixo também, pois traz um outro problema que pode acarretar em erros semelhantes de inconsistência de dados na aplicação.


Nenhum comentário:

Postar um comentário