21. Сделать ревью метода поиска товаров по фильтру
Условие задачи:
Интернет-магазин обрабатывает около 200 запросов в секунду. Пользователь задаёт фильтры по цене, рейтингу и другим параметрам, после чего сервис ищет подходящие товары.
Примерно в половине запросов подходящих товаров нет. Нужно провести code review метода и определить, как уменьшить лишнюю нагрузку на БД и улучшить API метода.
Код:
class ServiceImpl {
// 200 RPS
// 50% -> return new ArrayList<>();
ArrayList<Product> find(Filter filter) {
int count = productRepo.count(filter);
if (count > 0) {
return productRepo.find(filter);
}
return new ArrayList<>();
}
}
Спойлеры к решению
Подсказки
count() здесь не нужен.💡 Для найденных товаров сейчас выполняются два запроса в БД.
💡 Лучше зависеть от
List<Product>, а не от конкретного ArrayList.💡 Для большого каталога стоит подумать о пагинации и индексах.
Решение
Проблемы:
перед поиском выполняется лишний
count();для непустого результата получается два запроса:
COUNT+SELECT;при 200 RPS и 50% непустых результатов это примерно 300 запросов к БД вместо 200;
возвращается конкретный
ArrayList<Product>вместо интерфейсаList<Product>;вручную создаётся пустой список, хотя поиск сам может вернуть пустой результат;
нет проверки
filter;при большом количестве результатов отсутствует пагинация;
для часто используемых условий фильтрации могут потребоваться подходящие индексы.
Как лучше исправить
Предварительный count() не нужен — сразу выполняем поиск:
class ServiceImpl {
List<Product> find(Filter filter) {
if (filter == null) {
throw new IllegalArgumentException(
"Filter must not be null"
);
}
return productRepo.find(filter);
}
}
Если товаров нет, нормальный контракт repository — вернуть пустую коллекцию.
Для большого каталога лучше добавить пагинацию:
Page<Product> find(
Filter filter,
Pageable pageable
) {
return productRepo.find(filter, pageable);
}
Если общее количество найденных товаров клиенту не требуется, лучше рассмотреть Slice<Product>: тогда не нужен дополнительный запрос для подсчёта общего количества записей.
Также стоит проверить индексы по наиболее селективным и часто используемым полям фильтрации и ограничить максимальный размер страницы.
Главное исправление — не выполнять COUNT перед SELECT, если данные всё равно нужно получить.