Дайджесты новостей
Студийный предметный натюрморт с тремя прозрачными слоями данных пользовательской сессии и шкалой дифференцированного хранения.

Архитектура записи сессий на диск: почему тепловые карты тяжелее трейсов и как устроен retention

Каждый разработчик, внедрявший в свой проект системы записи сессий и поведенческой аналитики (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. Слой 1: Поток событий верстки (Event Stream): Включает сериализованные мутации узлов DOM, перемещения курсора мыши с квантованием каждые 50 миллисекунд, события скролла, ввод в поля формы (с маскированием приватных данных) и встроенные копии стилей CSS.
    • Медианный вес сжатой сессии составляет около 220 КБ;
    • Каждая десятая сессия весит больше 1 МБ;
    • На динамических SPA с бесконечным скроллом каталога вес сессии запросто улетает за 20 МБ. Именно этот слой формирует до 97% всего объема дискового хранилища.
  2. Слой 2: Технический слой (Диагностика): Сюда входят сообщения console.log и console.error, сетевые вызовы XHR/Fetch с кодами статусов и трассировки исключений JavaScript.
    • На массиве из 100 000 сессий технический слой занял всего 1.3 ГБ — это в 45 раз меньше первого слоя!
    • Любопытный парадокс: разработчикам часто кажется, что логи консоли переполняют базу. В реальности 90% объема этого слоя (1.2 ГБ из 1.3 ГБ) забрали именно сетевые заголовки и URL запросов, тогда как логи консоли заняли скромные 78 МБ.
  3. Слой 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% диска без потери аналитики

Осознав эту анатомию, инженеры перешли к архитектуре раздельного срока хранения, опирающейся на фундаментальные паттерны системного дизайна:

  1. Короткоживущая корзина (TTL 14–30 дней): Включает «тяжеловесов»: сырые потоки мутаций DOM rrweb и скользящий пул подложек для тепловых карт. Они дают ответ на вопрос: «Что именно видел перед собой пользователь в момент ошибки?». Этот вопрос актуален только по горячим следам инцидента. Ротация этих данных освобождает до 95% дискового пространства.
  2. Долгосрочная корзина (TTL 6–12 месяцев или бессрочно): Включает технический слой: логи ошибок, стектрейсы, сетевые сбои, профили пользователей и UX-аномалии. Они дают ответ на вопрос: «Случался ли этот сбой раньше и кого из клиентов он затронул?».

Поскольку легкий слой весит на два порядка меньше видеозаписей, его длительное хранение увеличивает общий объем хранилища всего на 2–3%, но сохраняет полную диагностическую картину для аналитики и поиска регрессий.

Такая дифференциация доказывает: в управлении инфраструктурой нет места грубым плоским решениям. Понимание природы и ценности каждого байта позволяет сократить дисковые расходы в разы, не жертвуя качеством расследования инцидентов.