Реализация Двухфазного коммита (2PC) на Spring Boot

Всем привет. Делаю реализацию two phase commit(2PC)

есть 2 фазы - prepare, commit и есть фаза на случай отката

коммуникация - REST API (/prepare, /commit, /rollback endpoints)

по канонам 2pc в фазе /prepare мне нужно заблочить строку в базе.

В текущей реализации сделал в каждом микросервисе в учавствующих таблицах boolean поле locked и в первой фазе ставлю значение true а во второй фазе false.

Я знаю что моя реализация очень простая и не совсем правильная.

Мой вопрос: как же все так и правильно в первой фазе блочить строку в таблицах?

Уже пробывал:

  1. select ... for update - но он не пойдет поскольку блокирует только на уровне одной транзакции а мне нужно блокировать до второй фазы

  2. думал о dbms_lock (блокировка на уровне базы) но не совсем уверен что это правильное решение.

Мой стэк: Java, Spring Data, MySql

Как же все таки правильно делать блокировку строки в первой фазе чтобы она держалась до завержение всего коммита (вторая фаза) в распределенной транзакции.

P/S реализацию SAGA знаю (очень интересно 2PC реализация)

Спасибо большое.


Ответы (1 шт):

Автор решения: mymedia

В текущей реализации сделал в каждом микросервисе в учавствующих таблицах 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("Заблокировать запись не удалось. Ждите.");
}
→ Ссылка