Каждый разработчик, внедрявший в свой проект системы записи сессий и поведенческой аналитики (Hotjar, FullStory, Smartlook или отечественные аналоги), помнит тот самый момент, когда системный администратор приходит с круглыми глазами и графиком дискового пространства в Grafana. База данных или объектное хранилище S3 за пару недель разрастается до сотен гигабайт, а счета за облачную инфраструктуру начинают расти по экспоненте.
Первая инстинктивная реакция команды — выставить в конфигурационном файле жесткий срок жизни для всех входящих событий:
retention_ttl_days: 30 # Удалять абсолютно все записи старше одного месяца
Кажется, что проблема решена: дисковый аппетит стабилизирован, старые данные удаляются по расписанию. Но в реальности такой плоский подход оказывается одновременно разорительным для бюджета и губительным для технической поддержки.
Давай разберем инженерный опыт команды сервиса Broux, которая исследовала реальный вес сотен тысяч сессий, и посмотрим, как устроен грамотный двухуровневый retention.
Ловушка плоского TTL: как «удалять всё через 30 дней» ломает расследование инцидентов
Почему единый срок хранения — это классическая архитектурная ловушка? Она страдает от двух взаимоисключающих пороков:
С одной стороны, она чрезмерно расточительна. Львиную долю дискового пространства (до 97%) занимают тяжелые потоки DOM-мутаций. Но давай зададимся простым вопросом: когда инженеры или продакт-менеджеры реально смотрят видеозапись сессии клиента?
По статистике Broux, 90% просмотров происходят в первые 72 часа после возникновения инцидента или жалобы в техподдержку. Спустя две недели вероятность того, что кто-то откроет старую запись экрана, падает практически до нуля. Держать сотни гигабайт тяжелых видеопотоков на быстром диске целый месяц — непозволительная роскошь.
С другой стороны, эта политика разрушительно жадна. Вместе с мутациями верстки под нож идут легковесные метаданные: стек-трейсы ошибок, логи консоли браузера, сетевые коды ответов и траектории кликов пользователей.
Представь жизненную ситуацию: разработчики нашли сложный плавающий баг в марте, поправили его и забыли. В мае выходит крупный релиз, который случайно возвращает старую ошибку (регрессия). Инженер открывает мониторинг, но при 30-дневном TTL система считает ошибку совершенно новой:
- Потеряна история динамики и частоты сбоя;
- Потерян список клиентов, у которых баг воспроизводился два месяца назад;
- Потеряна связь с историческими коммитами.
Разработчик начинает расследование с нуля, тратя часы на то, что уже было исследовано весной.
Три слоя пользовательской сессии: где прячутся гигабайты мутаций DOM
Чтобы спроектировать эффективное хранилище, нужно заглянуть внутрь пользовательской сессии. В современных инструментах запись строится на базе открытого стандарта библиотеки rrweb. Клиентский скрипт делает полный снимок (DOM snapshot) в момент открытия вкладки, а затем непрерывно пишет дифференциальный поток событий.
Данные сессии четко делятся на три слоя с колоссальной разницей в объемах:
- Слой 1: Поток событий верстки (Event Stream):
Включает сериализованные мутации узлов DOM, перемещения курсора мыши с квантованием каждые 50 миллисекунд, события скролла, ввод в поля формы (с маскированием приватных данных) и встроенные копии стилей CSS.
- Медианный вес сжатой сессии составляет около 220 КБ;
- Каждая десятая сессия весит больше 1 МБ;
- На динамических SPA с бесконечным скроллом каталога вес сессии запросто улетает за 20 МБ. Именно этот слой формирует до 97% всего объема дискового хранилища.
- Слой 2: Технический слой (Диагностика):
Сюда входят сообщения
console.logиconsole.error, сетевые вызовы XHR/Fetch с кодами статусов и трассировки исключений JavaScript.- На массиве из 100 000 сессий технический слой занял всего 1.3 ГБ — это в 45 раз меньше первого слоя!
- Любопытный парадокс: разработчикам часто кажется, что логи консоли переполняют базу. В реальности 90% объема этого слоя (1.2 ГБ из 1.3 ГБ) забрали именно сетевые заголовки и URL запросов, тогда как логи консоли заняли скромные 78 МБ.
- Слой 3: Производные аналитические данные: Классифицированные группы ошибок, детекторы яростных множественных кликов (rage clicks), клики в пустоту (dead clicks) и клиентские профили. На 100 000 сессий этот слой весит всего около 100 МБ — в сотни раз меньше записей! При этом именно он дает львиную долю пользы при долгосрочном анализе стабильности приложения.
Парадокс тепловых карт: почему снимки верстки втрое тяжелее консольных логов
Казалось бы, тепловая карта кликов (Heatmap) должна быть почти невесомой: пара числовых координат (X, Y) на экране. Координаты кликов сотен тысяч пользователей действительно занимают менее полугигабайта.
Но тепловую карту невозможно отобразить в вакууме: ей требуется фоновая подложка — визуальный снимок страницы.
Здесь инженеры сталкиваются с динамичностью современного веба. Сайты непрерывно меняются: раскатываются новые баннеры, проводятся A/B-тесты, верстка адаптируется под экраны смартфонов и мониторов 4K. Если наложить клики на устаревший снимок, точки зависнут в пустоте:
- В Broux снимки разделили по классам устройств (Mobile, Tablet, Desktop);
- На каждую пару «URL + устройство» в базе поддерживается скользящий пул из 3 эталонных снимков;
- Снимки оцениваются алгоритмом скоринга: наличие перекрывающих модальных окон, активных лоадеров-спиннеров или незагруженных блоков снижает рейтинг подложки;
- Снимкам старше недели начисляется временной штраф, благодаря чему свежий снимок автоматически вытесняет старый после выкатки релиза.
Но один качественный эталон верстки со встроенными инлайн-стилями весит от 100 КБ до 1 МБ. На интернет-магазине с 5000 уникальных URL пул снимков разрастается до 12 000 штук и весит 2.5 ГБ. Суммарно подложки для тепловых карт заняли 4.3 ГБ — втрое больше всего технического слоя диагностики!
Двухуровневый retention на практике: как сэкономить 90% диска без потери аналитики
Осознав эту анатомию, инженеры перешли к архитектуре раздельного срока хранения, опирающейся на фундаментальные паттерны системного дизайна:
- Короткоживущая корзина (TTL 14–30 дней): Включает «тяжеловесов»: сырые потоки мутаций DOM rrweb и скользящий пул подложек для тепловых карт. Они дают ответ на вопрос: «Что именно видел перед собой пользователь в момент ошибки?». Этот вопрос актуален только по горячим следам инцидента. Ротация этих данных освобождает до 95% дискового пространства.
- Долгосрочная корзина (TTL 6–12 месяцев или бессрочно): Включает технический слой: логи ошибок, стектрейсы, сетевые сбои, профили пользователей и UX-аномалии. Они дают ответ на вопрос: «Случался ли этот сбой раньше и кого из клиентов он затронул?».
Поскольку легкий слой весит на два порядка меньше видеозаписей, его длительное хранение увеличивает общий объем хранилища всего на 2–3%, но сохраняет полную диагностическую картину для аналитики и поиска регрессий.
Такая дифференциация доказывает: в управлении инфраструктурой нет места грубым плоским решениям. Понимание природы и ценности каждого байта позволяет сократить дисковые расходы в разы, не жертвуя качеством расследования инцидентов.
