Как реализовать логику перевыбора подрядчика при непринятии заказа в течение 15 минут
Что проверяют
Понимаете ли вы, что отложенное действие должно переживать рестарт сервиса и работать на нескольких инстансах, и видите ли гонку между «подрядчик принял» и «время вышло». Следом спрашивают, что будет, если подрядчик нажал «принять» через 15 минут и одну секунду.
Как отвечать
Начните с модели: у заказа есть текущее предложение (offer) с полями contractor_id, offered_at, expires_at, status. Срок хранится в базе как Instant в UTC, а не живёт только в памяти.
- обнаружение просрочки: планировщик каждые несколько секунд выбирает
status = 'OFFERED' AND expires_at <= now()сFOR UPDATE SKIP LOCKED, чтобы инстансы не брали одни и те же строки. Нужен индекс поexpires_atдля активных предложений; - отложенное сообщение как альтернатива: RabbitMQ с TTL и dead-letter exchange или плагином отложенных сообщений, Quartz с JDBC JobStore в кластерном режиме. Для сложных сценариев есть Temporal или Camunda с таймерами;
- гонка: и принятие, и просрочка делаются условным обновлением
UPDATE offer SET status = 'EXPIRED' WHERE id = ? AND status = 'OFFERED'. Кто первым изменил строку, тот и выиграл, второй получит 0 обновлённых строк и корректно откажет. В JPA то же даёт@Version; - перевыбор: исключаем подрядчиков, которые уже отказались или пропустили заказ, ограничиваем число попыток, после лимита отменяем заказ и уведомляем клиента;
- тесты: время берётся из внедрённого
Clock, тогда просрочку проверяют безThread.sleep.
Если просят решение внутри одного процесса, берите ScheduledExecutorService и храните ScheduledFuture, чтобы отменить задачу при принятии. Но сразу оговорите, что при рестарте все таймеры пропадут.
Где спотыкаются
Используют java.util.Timer на каждый заказ. Каждый Timer создаёт свой поток, необработанное исключение в задаче убивает его навсегда, а после деплоя таймеры исчезают вместе с процессом.
Проверяют статус и меняют его двумя отдельными операциями. Между ними подрядчик успевает принять заказ, а система уже отдала его другому.
Не ограничивают число перевыборов. Заказ, который никто не берёт, крутится по кругу и засыпает уведомлениями всех подрядчиков района.