Кейс: batch-джоба обрабатывала заказы дольше суток из-за одного connection
Ночная джоба выгружала заказы за прошлый день, обогащала их данными из внешнего сервиса и писала результат обратно в БД. На тесте с тысячей заказов всё укладывалось в минуты. В проде на полутора миллионах заказов джоба не укладывалась в отведённое окно и накладывалась на утреннюю нагрузку.
Разобрали код — обработка шла последовательно, одним потоком, на одном JDBC connection. Каждый заказ: запрос к внешнему сервису (сетевой round-trip 40-60ms), потом UPDATE в БД. Умножьте 1.5 млн на 50ms — это больше 20 часов только на сетевые вызовы, до всякой обработки.
Параллелизм был возможен — заказы независимы друг от друга, конфликтов между ними нет. Но джоба была написана как простой for-цикл, потому что на тесте с маленьким объёмом это работало незаметно быстро и никто не думал о масштабе.
Решение — разбить на партии и обрабатывать пул потоков параллельно, каждый со своим connection из пула. Но здесь появилась вторая проблема: пул HikariCP был настроен на 10 connections для обычного трафика приложения, а джоба захотела забрать все сразу и держать их часами, оставив основной трафик без соединений. Пришлось выделить отдельный пул под batch-задачи с явным лимитом, чтобы джоба не конкурировала с обычными запросами за одни и те же connections.
Код, который работает на тестовых данных, не говорит ничего о том, как он себя поведёт на объёме на три порядка больше. Оценка «сколько времени займёт операция на одном элементе, умноженное на количество элементов» — первое, что стоит посчитать перед тем как писать batch-обработку.
Тренажёр: 600 вопросов, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки