Files
healthlog/docs/tasks/items/read-api-bucketing.md
T
av 79331ac670 tasks: закрыт разбор и хранилище, начат спринт по чтению данных клиентами
- цель parsing-and-storage закрыта по своему критерию; незакрываемый остаток
  (новые формы от источника, ручные секции задним числом) переехал в тему
  parsing-completeness
- цель mcp поглощена целью read-api, переименованной в «Чтение данных
  клиентами»: адаптер — последний шаг того же направления, а не своё
- read-api-points разложена на конверт с точками, свёртку по сетке и
  тренировки с записями; спринт 2026-08-04 набран пятью задачами
2026-08-04 14:01:38 +03:00

5.1 KiB

Свёртка по сетке и предел размера ответа

  • Секция: ядро
  • Зачем: Враждебный запрос к каталогу стоил 693 мс и +153 МиБ кучи, а у точек множители те же и потолка нет ни у одного
  • Теги: goal:read-api

Потребитель просит разбивку (?from&to&bucket) и получает свёртку по сетке — либо честную ошибку вместо тихо подменённой сетки, — а сервис получает названный предел на то, сколько он готов отдать за один запрос.

Вторая из трёх частей read-api-points. Берётся после конверта ответа: сетка — параметр того же маршрута.

Решение по размеру ответа принято, вариант «б». Разбивка не задана и ответ не влезает — сервер сам берёт сетку погрубее и называет её в ответе; разбивка задана явно и не влезает — ошибка со списком доступных сеток, а не тихая подмена. Различие существенно: иначе агент, попросивший минутную сетку, получит суточные суммы и не узнает об этом.

Порог неполного ведра решается здесь, и вместе с ним — его полярность. Измерению рода агрегации порог не понадобился, свёртке в ответе он нужен, а готовые решения задают его противоположно: Graphite xFilesFactor — доля обязательно известных точек (умолчание 0.5 при роллапе и 0 при рендере, один параметр с двумя умолчаниями), RRDtool xff — доля допустимо неизвестных. Обе величины выглядят как «0.5», означая разное; полярность придётся назвать вслух в architecture.md, иначе через полгода два места кода поймут поле по-разному.

Цена измерена, и она унаследована. На каталоге враждебный запрос (20 метрик × 8 часов × 5000 точек) дал 693 мс и +153 МиБ живой кучи, при том что приём в том же процессе уже даёт пик 768 МиБ на теле 40 МиБ. Условный запрос снял повтор, но первый запрос стоит столько же, а множители «метрики × окно × точки × одновременные запросы» по-прежнему без потолка. Сюда же уезжают отложенные варианты задачи «цена читающего маршрута»: собственный дедлайн маршрута и потоковое измерение по метрике — второе только если счётчик заговорит.

Инвариант, который здесь легче всего нарушить: нижний слой HAE не суммируется ни при какой сетке — это интерполяция, а не сэмплы.

Критерии приёмки

  • «шаги за неделю по дням» отвечаются одним запросом, и в ответе названа фактическая сетка — оракул: запрос к поднятому сервису на живом архиве
  • явно заданная сетка, которая не влезает в предел, даёт ошибку со списком доступных сеток, а не подменённый ответ — оракул: тест
  • неполное ведро обрабатывается объявленным порогом, и полярность порога названа в docs/architecture.md вслух — оракул: тест на границе плюс глазами по разделу
  • нижний слой HAE не суммируется ни при какой сетке — оракул: тест на накопительной метрике, у которой есть и нижний, и часовой слой

Рамки

Схема не трогается, данные только читаются, сервис перезапускается. Берётся после конверта ответа. Против ./data — только task up / task run.

Связано: docs/architecture.md → «Свёртка и размер ответа»; идея NDJSON-потока закрывает другую сторону того же предела.