2

У меня есть RestController, который вызывает метод из службы. Метод добавляет пользователя в базу данных PostresSQL, которая имеет максимум 20 соединений.Spring Transactional метод, который возвращает 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; 
    } 
} 

Метод в сервисе прокомментирован Spring транзакционного, после того, как сохраняющийся метод возвращает данные CompletableFuture, внутри него некоторой длительная эксплуатации. Я вызываю метод «/ user» одновременно из нескольких потоков (около 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"; 
    }); 
} 

Если строка кода «voidCompletableFuture.get()», который блокирует текущий поток, называется тогда только 20 одновременного запроса работает и добавления данных в базу данных по количеству максимальных соединений. Существует исключение в других нитей:

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

Если удалить эту строку кода, то каждый запрос работает и добавляет данные в базу данных, как и ожидалось. Я думаю, что это потому, что Transaction не завершилась после завершения метода «public Future addUser (UserInfo userInfo)», если я вызову future.get() после. Возможно, кто-то знает, почему Spring и CompletableFuture работают таким образом или, может быть, есть еще один ответ? Почему блокирование CompletableFuture влияет на конец транзакции другим способом? Почему метод не завершает текущую транзакцию и не освобождает соединение, если существует метод внутри блока.

+0

случайно, это было бы возможно, что ваша транзакция уже запущена, когда вы звоните 'AddUser()', и не совершала перед вызовом '' получить) метод (? –

+0

@DidierL Я просмотрел журнал транзакций, транзакции начинаются в 'addUser()', а затем только несколько из них заканчиваются перед вызовом метода get(). Затем транзакции прекращают завершение (log 'TransactionInterceptor: 485: Завершение транзакции') за время, когда' get() 'блокирует запрос. И тогда число соединений становится более чем максимальным в пуле соединений в dataSource, а затем происходит это исключение. 'Caused by: java.sql.SQLTransientConnectionException: соединение недоступно'. Не знаю, почему соединения прекращаются. –

ответ

1

После добавления spring.jpa.open-in-view=false транзакция начнет останавливаться после метода setUser(), а не в течение всего процесса запроса.

Из документации: spring.jpa.open-in-view=true - Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request.

+1

Похоже, что мой комментарий поставил вас в правильном направлении ;-) TBH, я думал, что open-in-view устарел сегодня, так как это может вызвать несколько таких проблем (включая другие проблемы с производительностью). Однако изменение его в существующем приложении, вероятно, будет иметь побочные эффекты. –

+0

@DidierL спасибо за комментарий) –