Что внутри RackGrid, часть 4/7
График на дашборде выглядит как самая простая часть интерфейса. Пока не посчитаешь. 1000 ASIC × снимок раз в минуту = 1 440 000 строк в сутки. При хранении 30 дней — десятки миллионов. И всё это ради графика, на котором помещается несколько десятков точек: за сутки их ровно 72. Такие данные нельзя ни писать по одной строке, ни читать целиком. Запись. Один пакетный INSERT раз в минуту, одной транзакцией, в обход обычного пути ORM — без подгрузки связей и без обновления объектов после вставки. Тысяча строк уходит одним движением. Чтение — вот тут главное. Точки схлопываются в бакеты прямо в SQL-запросе: время округляется до размера бакета, дальше GROUP BY и агрегаты. При этом размер бакета зависит от запрошенного диапазона — минута для часа, шесть минут для шести часов, двадцать минут для суток, двенадцать часов для тридцати дней. Результат: график всегда получает сопоставимое количество точек независимо от глубины истории, а в приложение не приезжает ни одной лишней строки. Разница с наивным «выбрать всё за период и усреднить в коде» — это разница между 200 строками и миллионом. Пара деталей, которые всплыли уже в процессе: — В том же запросе агрегируются не только средние значения хешрейта, но и флаги: была ли в этом промежутке ошибка, было ли устройство на обслуживании. Иначе короткий сбой внутри двенадцатичасового бакета просто растворится в среднем и его никто не увидит. — Вычисление эпохи из времени различается в SQLite и PostgreSQL. Проект работает и там, и там, поэтому это единственное диалектно-зависимое место — и оно спрятано в одну переменную. — Ретеншн: ежедневная задача чистит данные старше 30 дней, плюс телеметрия удаляется точечно вместе с устройством. Без этого база растёт линейно и вечно. Правило, которое из этого выношу: агрегируй там, где лежат данные.