предложение переработано по итогам ревью дизайна
- три решения опровергнуты экспериментами: повтор при SQLITE_BUSY не сходится без _txlock=immediate (242 из 800 против 800 из 800), разбор в map[string]any держит 197 МиБ против 54, канонизация без json.Number теряет литерал - канонизации назначен дом: общий internal/canon вместо hae, иначе импорт экспорта Apple потребует второй реализации и хеш-детектор станет бесполезен - вывод слоя вернул шаг наследования, WARN сравнивается только с надёжным заголовком; в bucket возвращены units и границы содержимого
This commit is contained in:
@@ -21,15 +21,42 @@
|
||||
- **WHEN** точка с теми же координатами приезжает с другой строкой `source`
|
||||
- **THEN** она остаётся одной точкой, а не превращается в две
|
||||
|
||||
### Requirement: Эпизодные схемы не сохраняются до решения об идентичности
|
||||
|
||||
Система MUST NOT сохранять точки схем, у которых метка времени не уникальна, —
|
||||
пока модель их идентичности не определена. Такие точки разбираются и
|
||||
учитываются счётчиком, но в объекты не пишутся.
|
||||
|
||||
Известная такая схема одна: поэпизодный `sleep_analysis`. Измерено: 31
|
||||
координата в 21 доставке из 89 задвоена **внутри одной доставки**, где
|
||||
`received_at` общий и тай-брейк по нему неприменим в принципе. Сохранять их по
|
||||
правилу, о котором известно, что оно теряет, хуже, чем не сохранять: тела
|
||||
остаются в архиве, и после решения их подберёт пересборка.
|
||||
|
||||
Развилка вынесена блокером `identichnost-epizodnyh-metrik`. Суточной сводки
|
||||
(`sleep_analysis_summary`) ограничение не касается — у неё метка на полуночи
|
||||
уникальна.
|
||||
|
||||
#### Scenario: Поэпизодная запись сна не попадает в объект
|
||||
|
||||
- **WHEN** разобрана точка метрики `sleep_analysis` поэпизодной схемы
|
||||
- **THEN** она не сохраняется в часовой объект
|
||||
- **AND** факт учитывается счётчиком в итоге разбора доставки
|
||||
|
||||
### Requirement: Разрешение столкновений по полноте
|
||||
|
||||
Когда по одним координатам приходят разные содержимые, система SHALL оставлять
|
||||
**более полную** точку — ту, у которой больше значащих полей, — а не последнюю
|
||||
пришедшую. Иначе бедная доставка стирает `start`/`end` у богатой.
|
||||
|
||||
Если полнота равна, а значения различаются, исход определяет порядок
|
||||
воспроизведения, и он MUST быть по `received_at` доставки: свёртка по журналу
|
||||
обязана давать то же состояние, что приём в реальном времени.
|
||||
Если полнота равна, а значения различаются, исход MUST быть детерминированным
|
||||
и не зависеть от порядка, в котором доставки дошли до хранилища: свёртка по
|
||||
журналу обязана давать то же состояние, что приём в реальном времени.
|
||||
|
||||
Сравнение по `received_at` для этого не годится: у сохранённой точки нет
|
||||
провенанса — ни времени приёма, ни идентификатора доставки, — и сравнивать
|
||||
не с чем. Детерминизм обеспечивается свойством самих значений (например,
|
||||
порядком канонических форм), а не порядком событий.
|
||||
|
||||
#### Scenario: Бедная точка не стирает поля богатой
|
||||
|
||||
@@ -41,7 +68,19 @@
|
||||
|
||||
- **WHEN** по одним координатам приходят две одинаково полные точки с разными
|
||||
значениями
|
||||
- **THEN** побеждает точка из доставки с большим `received_at`
|
||||
- **THEN** исход определяется детерминированно и не зависит от порядка
|
||||
воспроизведения доставок
|
||||
|
||||
#### Scenario: Столкновение с различием содержимого оставляет след
|
||||
|
||||
- **WHEN** по одним координатам сохраняется точка, каноническая форма которой
|
||||
отличается от уже сохранённой
|
||||
- **THEN** система пишет запись уровня `WARN` без значений точки
|
||||
- **AND** увеличивает счётчик перезаписей в итоге разбора доставки
|
||||
|
||||
Без этого следа допущение «меньше полей не значит новее» не получит ни одного
|
||||
наблюдения, а отказ правила будет неотличим от нормальной работы до сверки с
|
||||
экспортом Apple — то есть месяцами.
|
||||
|
||||
### Requirement: Хранение часовыми объектами
|
||||
|
||||
@@ -49,6 +88,17 @@
|
||||
`метрика + слой + час (UTC)`. Содержимое объекта — сжатый gzip блоб; точки
|
||||
внутри упорядочены по времени.
|
||||
|
||||
Объект SHALL нести **единицы измерения** метрики. Внутри точки их нет — они
|
||||
живут на уровне метрики (проверено: поле `units` не встретилось ни в одной
|
||||
точке за 89 доставок), поэтому дословное хранение точек их не сохраняет. Без
|
||||
колонки единицы восстановимы только из архива, а для метрик, переставших
|
||||
приходить, — теряются навсегда.
|
||||
|
||||
Объект SHALL нести границы содержимого (первая и последняя метка) и
|
||||
идентификатор доставки, создавшей его. Первое нужно каталогу разрезов, чтобы
|
||||
не разжимать каждый блоб ради диапазона; второе — провенанс для разбора
|
||||
слияний.
|
||||
|
||||
Запись — чтение объекта, слияние точек, запись обратно. Точки из объекта
|
||||
MUST NOT удаляться.
|
||||
|
||||
@@ -78,8 +128,14 @@ MUST NOT удаляться.
|
||||
|
||||
### Requirement: Признак запечатанного часа
|
||||
|
||||
Система SHALL отмечать признаком `sealed` часы, которые уже не должны
|
||||
меняться. Изменение запечатанного объекта — не отказ, а сигнал.
|
||||
Система SHALL хранить признак `sealed` у часового объекта и SHALL реагировать
|
||||
на изменение запечатанного объекта сигналом, а не отказом.
|
||||
|
||||
Правило перевода часа в `sealed` в этой дельте **не определяется**: порог
|
||||
глубины досчёта ставится по наблюдениям, которых пока нет (наблюдалось до
|
||||
22 минут). До появления правила признак остаётся невыставленным, и сценарий
|
||||
ниже проверяется только явной установкой в тесте — это осознанная граница, а
|
||||
не упущение.
|
||||
|
||||
#### Scenario: Изменение запечатанного часа
|
||||
|
||||
|
||||
Reference in New Issue
Block a user