Плохо знаешь данные - рискуешь здоровьем
Я следовал твоим рекомендациям, мой код написан почти идеально и полностью покрыт тестами. Более того, он уже год работает в проде! Почему вдруг сегодня всё упало?!

На этой неделе я наткнулся на не очень приятный кусок кода и понял, что в причины возникновения проблем с CPU/RAM не включил еще одну: плохое знание своих данных.
Почему мы плохо знаем данные и как это контролировать - тема отдельного обсуждения. Не до конца разобрались в предметной области; не предполагали такого сценария использования системы; не рассчитывали на популярность какой-то функции; не заметили наличия бэкдора; тестовый стенд содержал мало данных; временное решение осталось постоянным; всосали всю таблицу в кэш и т.д. Главное, что подобные упущения приводят к тому, что в некоторых сценариях существующие алгоритмы начинают работать крайне неэффективно или даже деструктивно. И мы на собственной шкуре начинаем понимать, что такое сложность O(n), проблема с N+1 запросами, почему файлы нужно обрабатывать блоками, почему не нужно логику засовывать в транзакцию БД и т.п.
Например, недавно был кейс. Пользователи нашего API догадались завести “синтетический агрегат”, на который отношением many-to-one подвесили несколько тысяч связанных сущностей. Изначально предполагалось, что количество связей не будет превышать сотни. Естественно, что большая часть кода была написана без расчёта на подобный объем данных.
Но вот на днях попался пример откровенного вредительства. Возможно, некоторые скажут, что это могло быть временное решение, которое осталось постоянным, но на самом деле, немного зная историю проекта, я так сказать не могу. Приведу пример кода на Java, я его изменил, но суть проблемы должна быть ясна.
public List<City> getCities(String regionCode) {
return entityManager.createNativeQuery(
"SELECT DISTINCT " +
" city_code AS code, " +
" city_name AS text " +
"FROM patients " +
"WHERE region_code = :regionCode " +
"ORDER BY city_name ASC", City.class)
.setParameter("moCode", regionCode)
.getResultList().stream()
.filter(Objects::nonNull)
.toList();
}
Вопрос даже не в том, как это сделано, а в том, что тут делается и почему! Ясно, что с ростом таблицы patients будет расти время выполнения запроса; следовательно, соединение БД будет держаться дольше; быстрей упрёмся в нехватку соединений. Вишенка на торте: у таблицы patients нет индекса на region_code.
Если мы плохо знаем свои данные, мы закладываем уродливую пасхалку и мину замедленного действия.
Как этого избежать?
При работе с данными я всегда себя спрашиваю: “А что если?” Что, если тут будет не 100 элементов, а 100000? Что, если этот запрос будет выполняться не 10 мс, а 10 секунд?
Конечно, это не всегда спасает, но приём точно рабочий!
Понравилась статья?
Посмею напомнить, что у меня есть Telegram-канал Архитектоника в ИТ, где я публикую материал на похожие темы примерно раз в неделю. Подписчики меня мотивируют, но ещё больше мотивируют живые дискуссии, ведь именно в них рождается истина. Поэтому подписывайтесь на канал и будем оставаться на связи! ;-)
Статьи из той же категории: