web-dev-qa-db-fra.com

Ressort Réessayer avec transactionnel

Est-il garanti que Spring Retry fonctionnera avec l'annotation @Transactional de Spring?

Plus précisément, j'essaie d'utiliser @Retryable pour le verrouillage optimiste. Il semble que cela dépendrait de l'ordre des proxys AOP créés. Par exemple, si les appels ressemblent à ceci:

Code appelant -> Réessayer le proxy -> Transaction Proxy -> Code de base de données actuel

Ensuite, cela fonctionnerait correctement, mais si les mandataires étaient structurés de la manière suivante:

Code d'appel -> Transaction Proxy -> Réessayer le proxy -> Code de base de données actuel

Ensuite, la nouvelle tentative ne fonctionnerait pas, car le fait de fermer la transaction est ce qui déclenche l'exception de verrouillage optmistic.

Lors des tests, il a semblé générer le premier cas (nouvelle tentative, puis transaction), mais je ne pouvais pas dire s'il s'agissait d'un comportement garanti ou simplement de la chance.

7
Cobra1117

Trouvé la réponse ici: https://docs.spring.io/spring/docs/5.0.6.BUILD-SNAPSHOT/spring-framework-reference/data-access.html#transaction-declarative-annotations Table 2 indique que l'avis de l'annotation Transactional a un ordre de Ordered.LOWEST_PRECEDENCE, ce qui signifie qu'il est prudent de combiner Retryable avec Transactional tant que vous n'écrivez pas l'ordre de l'avis pour l'une ou l'autre de ces annotations. En d'autres termes, vous pouvez utiliser ce formulaire en toute sécurité:

@Retryable(StaleStateException.class)
@Transactional
public void performDatabaseActions() {
    //Database updates here that may cause an optimistic locking failure 
    //when the transaction closes
}
2
Cobra1117

Si vous souhaitez le tester indépendamment et vous assurer de son comportement, vous pouvez disposer de @Transactional @Service, puis d'un autre service utilisant la transaction n ° 1 et ajoutant simplement de nouvelles tentatives.

Dans ce cas, quel que soit le nombre de tests que vous effectuez, vous vous appuyez sur un comportement non documenté (comment le traitement des annotations est-il exactement commandé). Cela peut changer entre les versions mineures, en fonction de l'ordre dans lequel les beans Spring indépendants sont créés, etc. En bref, vous posez des problèmes lorsque vous mélangez @Transactional et @Retry sur la même méthode.

edit: Il y a une question à réponse similaire https://stackoverflow.com/a/45514794/1849837 avec code

@Retryable(StaleStateException.class)
@Transactional
public void doSomethingWithFoo(Long fooId){
    // read your entity again before changes!
    Foo foo = fooRepository.findOne(fooId);
    foo.setStatus(REJECTED)  // <- sample foo modification
} // commit on method end

Dans ce cas, cela semble aller, car quel que soit l'ordre (réessayer, puis transaction, ou transaction ou réessayer), le comportement observable sera le même. 

1
Bartosz Bilicki

Par défaut, Spring Retry génère des conseils avec le même ordre LOWEST_PRECEDENCE - jetez un coup d'œil à RetryConfiguration . Cependant, il existe un moyen assez simple de remplacer cet ordre:

@Configuration
public class MyRetryConfiguration extends RetryConfiguration {
   @Override
   public int getOrder() {
      return Ordered.HIGHEST_PRECEDENCE;
   }
}

Veillez à omettre l’annotation @EnableRetry pour éviter que RetryConfiguration par défaut ne soit pris en compte.

0
oceansize