2

J'ai RestController qui appelle la méthode du service. La méthode ajoute un utilisateur à la base de données PostresSQL, qui a un maximum de 20 connexions.Spring Méthode transactionnelle qui retourne CompletableFuture

@RestController 
public class Controller { 
    @RequestMapping(value = "/user", method = RequestMethod.POST) 
    public String addUser(@RequestBody UserInfo userInfo) { 
     Future<String> completableFuture = userService.addUser(userInfo); 
     String answer = voidCompletableFuture.get(); 
     return answer; 
    } 
} 

Procédé service est annoté par Spring Transactionnel, après la persistance méthode de données renvoie CompletableFuture, à l'intérieur de celui-ci une opération longue. J'appelle la méthode "/ user" simultanément à partir de plusieurs threads (environ 100).

@Transactional 
public Future<String> addUser(UserInfo userInfo) { 
    userDao.persist(userInfo); 
    return CompletableFuture.supplyAsync(() -> { 
     try { 
      Thread.sleep(10000); 
      return "Result"; 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     return "Error"; 
    }); 
} 

Si la ligne de code « voidCompletableFuture.get() », ce qui bloque fil courant, est appelé alors seulement 20 demande simultanée fonctionne et l'ajout de données à la base de données par le nombre de connexions maximum. Il y a exception dans un autre fils:

Caused by: java.sql.SQLTransientConnectionException: Connection is not available, request timed out after 30000ms. 

Si je supprime cette ligne de code, alors chaque demande fonctionne et ajoute des données à base de données comme prévu. Je pense que c'est parce que la transaction n'est pas terminée après la fin de la méthode "public Future addUser (UserInfo userInfo)" si j'appelle future.get() après. Peut-être que quelqu'un sait pourquoi Spring et CompletableFuture fonctionnent d'une telle manière ou peut-être y a-t-il une autre réponse? Pourquoi le blocage de CompletableFuture affecte-t-il la fin de la transaction dans une autre méthode? Pourquoi la méthode ne termine pas la transaction en cours et ne libère pas la connexion s'il y a un blocage dans la méthode de requête.

+0

Par hasard, serait-il possible que votre transaction est déjà commencé lorsque vous appelez 'addUser()', et n'est pas engagé avant d'appeler la méthode 'get()'? –

+0

@DidierL J'ai regardé le journal des transactions, les transactions commencent dans 'addUser()' et seulement plusieurs d'entre elles se terminent avant d'appeler la méthode 'get()'. Ensuite, les transactions s'arrêtent pour se terminer (log 'TransactionInterceptor: 485: Completing transaction') pour l'instant où 'get()' bloque la requête. Et puis le nombre de connexions devient plus puis maximum dans le pool de connexions dans dataSource et ensuite passe cette exception 'Causée par: java.sql.SQLTransientConnectionException: la connexion n'est pas disponible'. Je ne sais pas pourquoi les connexions s'arrêtent pour être terminées. –

Répondre

1

Après l'ajout spring.jpa.open-in-view=false transaction commencer à arrêter après la méthode setUser() et pas en cours de traitement de la demande.

De la documentation: spring.jpa.open-in-view=true - Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request.

+1

On dirait que mon commentaire vous met dans la bonne direction ;-) TBH, je pensais que l'open-in-view était déprécié de nos jours, car il peut causer plusieurs problèmes comme celui-ci (y compris d'autres problèmes de performances). Cependant, le changer dans une application existante aura probablement des effets secondaires. –

+0

@DidierL merci pour le commentaire) –