Какие подходы использовать при нескольких инстансах приложения с одной базой для обработки записей
Что проверяют
Умеете ли вы сделать так, чтобы одну запись не обработали два инстанса одновременно и чтобы она не потерялась при падении. Следом обычно спрашивают, что будет, если инстанс упал посреди обработки, и как сделать обработку идемпотентной.
Как отвечать
Типичная задача: таблица задач или событий, несколько подов Spring Boot разбирают её параллельно. Рабочие подходы:
SELECT ... FOR UPDATE SKIP LOCKED: каждый инстанс берёт пачку свободных строк и пропускает заблокированные другими. Есть в PostgreSQL, MySQL 8 и Oracle. В JPA это нативный запрос в короткой транзакции;- захват через
UPDATE:UPDATE tasks SET status = 'PROCESSING', locked_until = now() + interval '5 min' WHERE id = ? AND status = 'NEW'. Обновлено 0 строк, значит запись уже забрали.locked_untilработает как аренда: зависшую задачу после истечения срока подберёт другой инстанс; - оптимистичная блокировка: колонка версии и
@Versionв JPA, проигравший получаетOptimisticLockException. Хорошо при редких конфликтах, плохо для очереди, где все бьются за одни строки; - один исполнитель для
@Scheduled: ShedLock с@SchedulerLockхранит блокировку в той же базе, Quartz умеет кластерный режим через JDBC JobStore, в PostgreSQL естьpg_try_advisory_lock; - очередь вместо таблицы: при большом потоке записи публикуют через transactional outbox в Kafka или RabbitMQ, и распределение между потребителями берёт на себя брокер.
Отдельно проговорите гарантии. Обработка получается «хотя бы один раз»: инстанс может выполнить работу и упасть до коммита статуса. Поэтому обработчик должен быть идемпотентным: уникальный ключ операции, проверка статуса, INSERT ... ON CONFLICT DO NOTHING.
Где спотыкаются
Предлагают synchronized или ReentrantLock. Они работают внутри одной JVM и не видят другие инстансы.
Держат блокировку строки на время долгого HTTP-вызова. Транзакции растягиваются, пул соединений заканчивается. Правильнее захватить запись, закоммитить статус и работать уже вне транзакции.
Уходят в микросервисы, шардинг и распределённые транзакции. Для обработки одной таблицы это стрельба из пушки по воробьям: ждут SKIP LOCKED, аренду и идемпотентность.