httpapi: точки метрики за период отдаются одним запросом
- `GET /api/v1/metrics/{name}?from&to&layer` — ряд точек за период; конверт
объявляет слой, измеренный род, его применимость к отданному ряду и границу
окна измерения, а сам ряд собирается из одного слоя, выбранного по охвату
точек внутри периода
- use-case вынесен в `internal/points`, чтение — одним входом `store.ReadSeries`
под одной транзакцией; правило выбора слоя остаётся в домене и приходит в
хранилище колбэком
- `writeJSON` перестал экранировать HTML-символы и перестал глушить отказ
записи: дословность содержимого точки иначе не удерживается, а оборванное
тело уходило под видом успешного `200`
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
## Why
|
||||
|
||||
Точки лежат в витрине и наружу не отдаются: «вес за год» достаётся только
|
||||
`sqlite3` на хосте. Каталог уже отвечает, **что** есть, — но ни один из трёх
|
||||
потребителей не может получить сами значения.
|
||||
|
||||
Ответ обязан быть самоописательным. Метрика лежит сразу в нескольких слоях
|
||||
подробности, а род её свёртки не объявлен источником, а **измерен** окном в 48
|
||||
самых свежих **общих** часов — окном, которое замирает при выключенной минутной
|
||||
автоматизации HAE и продолжает объявлять род. Клиент, не видящий ни слоя, ни
|
||||
границы этого окна, принимает решение по числу, происхождения которого не знает.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Новый читающий маршрут `GET /api/v1/metrics/{name}?from&to&layer` — все точки
|
||||
метрики за период, одним запросом, без доступа к файлу базы.
|
||||
- Конверт ответа объявляет **фактические** параметры выдачи: `metric`, `from`,
|
||||
`to`, `layer`, `bucket`, `aggregation` (род, его применимость к отданному ряду
|
||||
и граница окна измерения), `points`. Поля присутствуют всегда — клиент не
|
||||
выводит их наличием или отсутствием.
|
||||
- Слой выбирается правилом, а не молча: параметр `layer` задаёт разрез явно, без
|
||||
него берётся слой с наибольшим охватом **точек** внутри запрошенного периода,
|
||||
при равенстве — самый мелкий. Смены слоя внутри одного ответа не бывает.
|
||||
- Род свёртки в конверте — тот же измеренный `catalog.Style`, что у каталога, и
|
||||
измеряется он тем же правилом и тем же окном. Род неизвестен — так и сказано
|
||||
словом `unknown`, а не молчанием. Рядом едет применимость: `cumulative` на
|
||||
нижнем слое HAE объявляется неприменимым, иначе конверт приглашает потребителя
|
||||
сложить интерполяцию и завысить втрое.
|
||||
- Свёртка в этом изменении **не выполняется**: `bucket` всегда `null`, а
|
||||
присутствие параметра `bucket` в запросе отвергается `400`, а не игнорируется
|
||||
молча.
|
||||
- Ответ снимается **одной транзакцией чтения**, а метка ответа включает
|
||||
канонизированную форму запроса **и горизонт измерения** — иначе соседняя
|
||||
задача условного запроса подтвердит `304` на сменившемся роде.
|
||||
- Форма провода — по образцу каталога (ADR
|
||||
`ADR-2026-08-04-forma-provoda-prinadlezhit-transportu`): типы `*Wire` в
|
||||
`internal/httpapi`, перевод присваиванием, строка в таблице образцов
|
||||
`wire_internal_test.go`.
|
||||
|
||||
Схема базы не трогается: обе выборки отвечают по существующим ключу и
|
||||
покрывающему индексу `bucket_catalog`.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `points`: точки метрики за период — форма запроса и его разбор, правило выбора
|
||||
слоя, состав конверта ответа, объявление измеренного рода вместе с границей
|
||||
окна измерения, дословность значений точки.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `read-api`: добавляются два общих правила читающих маршрутов — сериализация
|
||||
ответа не экранирует содержимое (иначе `&` внутри дословно сохранённой точки
|
||||
уезжает как `&`) и ответ чтения помечается непригодным для разделяемого
|
||||
кеша. Оба механизма общие (`writeJSON`, `setReadHeaders`), и решать их заново
|
||||
на каждом маршруте — тот же второй способ.
|
||||
|
||||
`catalog` (правило измерения рода) применяется новым маршрутом без изменения его
|
||||
требований.
|
||||
|
||||
## Impact
|
||||
|
||||
- `internal/httpapi` — маршрут, разбор параметров, форма провода точек,
|
||||
выключение HTML-экранирования в общем `writeJSON`.
|
||||
- `internal/points` (новый) — use-case «точки метрики за период»: выбор слоя,
|
||||
сборка ряда, измерение рода и метка ответа по одной метрике.
|
||||
- `internal/store` — один вход чтения ряда (`ReadSeries`) в одной транзакции:
|
||||
охваты слоёв в периоде, точки выбранного слоя, объекты окна измерения.
|
||||
- `docs/architecture.md` — разделы «Read API» (форма ответа, правило выбора
|
||||
слоя) и таблица компонентов.
|
||||
- Потребители: HTTP-клиенты; следом этот же конверт копируют свёртка по сетке,
|
||||
условный запрос по точкам, тренировки, записи и MCP.
|
||||
Reference in New Issue
Block a user