Реализация Двухфазного коммита (2PC) на Spring Boot
Всем привет. Делаю реализацию two phase commit(2PC)
есть 2 фазы - prepare, commit и есть фаза на случай отката
коммуникация - REST API (/prepare, /commit, /rollback endpoints)
по канонам 2pc в фазе /prepare мне нужно заблочить строку в базе.
В текущей реализации сделал в каждом микросервисе в учавствующих таблицах boolean поле locked и в первой фазе ставлю значение true а во второй фазе false.
Я знаю что моя реализация очень простая и не совсем правильная.
Мой вопрос: как же все так и правильно в первой фазе блочить строку в таблицах?
Уже пробывал:
select ... for update - но он не пойдет поскольку блокирует только на уровне одной транзакции а мне нужно блокировать до второй фазы
думал о dbms_lock (блокировка на уровне базы) но не совсем уверен что это правильное решение.
Мой стэк: Java, Spring Data, MySql
Как же все таки правильно делать блокировку строки в первой фазе чтобы она держалась до завержение всего коммита (вторая фаза) в распределенной транзакции.
P/S реализацию SAGA знаю (очень интересно 2PC реализация)
Спасибо большое.
Ответы (1 шт):
В текущей реализации сделал в каждом микросервисе в учавствующих таблицах boolean поле locked и в первой фазе ставлю значение true а во второй фазе false.
В принципе, стратегия добавить флаг locked — правильная. В обработчике /prepare надо проверять, что блокировки ещё нет, прежде чем устанавливать флаг. Делается это единым UPDATE запросом. После убедитесь, что обновить запись действительно удалось, и сообщите клиенту об этом факте, позволяя перейти на второй этап. Явные блокировки уровня БД вам не потребуются.
PreparedStatement ps = connection.prepareStatement(
"UPDATE ... SET locked = TRUE WHERE id = ? AND NOT locked");
ps.setInt(1, 42);
int rowsAffected = ps.executeUpdate();
if (rowsAffected > 0) {
System.out.printf("Успешная блокировка. Можно вносить изменения.");
} else {
System.out.printf("Заблокировать запись не удалось. Ждите.");
}