Retry - это решение. Но вы не реализовали его должным образом.
- РЕДАКТИРОВАТЬ, как было предложено @Mark Rotteveel -
Вам нужно явно вызвать .abort()
на вашем соединении, а затем вы можете повторить попытку. Вероятно, вам удастся сохранить свои объекты PreparedStatement / Statement, но если у вас все еще возникнут проблемы, подумайте о том, чтобы закрыть и воссоздать их.
- КОНЕЦ РЕДАКТИРОВАНИЯ ---
Вторая проблема отсутствует экспоненциального отката.
Компьютеры надежны. Очень надежный. Лучше, чем швейцарские часы.
Если два потока выполняют задание, и в рамках этого задания они блокируют друг друга, и оба увидят это, прервут свои транзакции и начнут заново, тогда ...
вероятно, точно то же самое повторится . И опять. И опять. И опять. Компьютеры могут быть такими надежными в неудачном сценарии ios.
Решение - случайная экспоненциальная отсрочка. Один из способов гарантировать, что два потока не будут продолжать делать что-то одинаково, в одном и том же порядке и с одинаковым временем, - это буквально начать подбрасывать монеты, чтобы принудительно сделать его менее стабильным. Это звучит глупо, но без этой концепции inte rnet не существовал бы (Ethe rnet работает именно так: все системы в сети ethe rnet немедленно отправляют данные, а затем проверяют наличие всплесков в строке, которая указывает на несколько стороны отправили все одновременно, и в результате получился нечитаемый беспорядок. Если они обнаруживают это, они случайным образом ждут с экспоненциальной отсрочкой , а затем отправляют его снова. Это, казалось бы, безумное решение выбило из колеи Token Ring сетей).
«Экспоненциальная» часть означает: по мере того, как повторяются попытки, увеличивайте задержки (и по-прежнему произвольно).
Ваша последняя ошибка заключается в том, что вы всегда пытаетесь повторить попытку, а не только тогда, когда это разумно.
Вот пример экспоненциального рандомизированного отката, который устраняет все ваши проблемы, кроме той части, где вам нужно заново создать объекты (Prepared) Statement и закрыть старые; ваш фрагмент не проясняет, где это происходит.
} (catch SQLException e) { // catch SQLEx, not Ex
String ps = e.getSQLState();
if (ps != null && ps.length() == 5 && ps.startsWith("40")) {
// For postgres, this means retry. It's DB specific!
retryCount++;
if (retryCount > 50) throw e;
try {
Thread.sleep((retryCount * 2) + rnd.nextInt(8 * retryCount);
continue; // continue the retry loop.
} catch (InterruptedException e2) {
// Interrupted; stop retrying and just throw the exception.
throw e;
}
}
// it wasn't retry; just throw it.
throw e;
}
Или сделайте себе огромную услугу, бросьте всю эту работу и используйте библиотеку. JDB C спроектирован так, чтобы быть невероятно раздражающим, непоследовательным и уродливым для «конечных пользователей» - это потому, что целевая аудитория JDB C - не вы. Это поставщики БД. Это клей самого низкого уровня, который только можно вообразить, со всевозможными странными хитростями, чтобы все поставщики БД могли раскрыть свои любимые функции.
Например, JDBI отлично подходит и очень хорошо поддерживает повторные попытки с лямбдами.