@Scheduled(fixedRate) без @Transactional?Расписание в Spring через
@Scheduled — удобный способ запускать задачи по таймеру. Но часто разработчики забывают про транзакции, особенно с fixedRate, и попадают в ловушку.📌 Пример проблемы:
@Scheduled(fixedRate = 10_000)
public void cleanUp() {
List<Job> jobs = jobRepository.findAllByStatus(PENDING);
jobs.forEach(job -> {
job.setStatus(PROCESSING);
jobRepository.save(job);
});
}
🧨 Каждые 10 секунд метод запускается заново. Если выполнение предыдущего ещё не закончено, начнётся второй поток, который заберёт те же
PENDING -записи.В итоге — дублирование обработки, гонки, повреждение данных.
📉 Особенно критично при долгих задачах или высокой нагрузке.
✅ Решение — обернуть метод в транзакцию + использовать блокировки:
@Transactional
@Scheduled(fixedRate = 10_000)
public void cleanUp() {
List<Job> jobs = jobRepository.findAllByStatusForUpdate(PENDING); // SELECT ... FOR UPDATE
jobs.forEach(job -> {
job.setStatus(PROCESSING);
jobRepository.save(job);
});
}
📌 Или добавить флаг “locked”, чтобы явно помечать взятые задачи.
💡 Лучше использовать
@Scheduled(fixedDelay) — он ждёт завершения предыдущего запуска. Это безопаснее по умолчанию.🧠 Подумайте о том, чтобы заменить
@Scheduled на:* Spring Batch (если сложные джобы)
* Spring Integration / Flowable / Camunda (если нужны гарантии и retry)
* Quartz (если нужен контроль и очереди)
📲 Мы в MAX
👉@BookJava
