Обработка исчерпания пула соединений и предотвращение взаимоблокировки в Hibernate/C3P0
Мои старые приложения следуют цепочке для запроса моей базы данных: Spring Tx -> Hibernate -> C3P0
, Теперь мне нужно реализовать новые функции на основе существующей архитектуры.
Я обычно вхожу в транзакционный контекст, используя либо @Transactional
аннотации или вручную вызывая PlatformTransactionManager
,
Иногда для выполнения асинхронных операций и операций с большими данными я открываю сеанс без сохранения состояния, используя SessionFactory
API. У нас никогда не было никаких дополнительных проблем, так как наш пул потоков хорошо контролируется
Впервые мое требование состоит в том, чтобы выполнять несколько операций с БД параллельно, чтобы повысить производительность. У меня есть сомнения по этому поводу, потому что я очень осторожен с многопоточными операциями.
Для каждого объекта в базе данных я могу выполнить операцию согласования в отдельном потоке. Но каждая операция согласования использует пару соединений для каждого из двух потоков, которые она порождает. Таким образом, в основном есть 4 соединения для каждого потока.
Класс многопоточности учит студентов, что во избежание тупиковой ситуации (проблема с едой философов) ресурсы должны быть получены транзакционным способом: как только вы приобрели вилку, если вы не можете приобрести вторую за разумное время, отпустите первую и попробуйте снова,
Мой вопрос прост. Учитывая SessionFactory
API, как я могу написать код, который не будет бесконечно ждать 4 соединений от c3p0, если пул полон? Я имею в виду мне нужно 4 StatelessSession
s только если есть место для 4, иначе я могу подождать и повторить попытку.
Насколько я знаю, SessionFactory
API блокирует и не позволяет установить сторожевой таймер
1 ответ
В настоящее время идея заключается в использовании простого старого обходного пути.
В целом, в мире Java, если API не предлагает сторожевой метод для получения ресурса, вы можете делегировать его Future
, который предлагает get(int timeout, TimeUnit timeUnit)
API для ограничения выполнения.
Шаг 1: сторожевой таймер для получения ресурса
Итак, в основном, как я могу получить сеанс без сохранения состояния в рамках тайм-аута?
private Future<StatelessSession> getStatelessSession(SessionFactory sessionFactory)
{
return asyncTaskExecutor.submit(new Callable<StatelessSession>()
{
@Override
public StatelessSession call() throws Exception
{
return sessionFactory.openStatelessSession();
}
});
}
try {
StatelessSession session = getStatelessSession(sessionFactory).get(3000,TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
// go to step 2
}
Шаг 2: ешь так, как поступил бы философ
Как я уже сказал, моя проблема в основном похожа на ресторанного философа, за исключением того, что у нас есть не просто две вилки, а ложки и ножи, которые можно взять в общей сложности 4 штуки.
StatelessSession session1, session2, session3, session4;
for (int i=0; i<MAX_ATTEMPTS;i++) {
try {
session1 = tryGetSessionOrBoooooom();
} catch(TimeoutException ex) {
continue;
}
try {
session2 = tryGetSessionOrBoooooom();
} catch(TimeoutException ex) {
session1.close();
continue;
}
try {
session3 = tryGetSessionOrBoooooom();
} catch(TimeoutException ex) {
session2.close();
session1.close();
continue;
}
try {
session4 = tryGetSessionOrBoooooom();
} catch(TimeoutException ex) {
session3.close();
session2.close();
session1.close();
continue;
}
}
Что произойдет, если вы выйдете за пределы MAX_ATTEMPTS
зависит от вас, разработчик!