добавлен словарь категориальных значений HAE → коды HealthKit
- фазы сна, контекст пульса и имена тренировок попадают в реестр `category_value` (миграция 00010): строка хранится дословно, выведенный код лежит рядом отдельной записью, а не полем внутри точки - словарь и синонимы кодов живут в бинаре (`internal/healthkit`); локаль из `Accept-Language` сужает поиск, но в ключ реестра не входит — заголовков в сыром архиве нет - наблюдение входит в отпечаток витрины, выведенный код — нет: он производная от словаря, а не от журнала
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
# Код HealthKit кладётся реестром рядом, а не полем внутри точки
|
||||
|
||||
- Дата: 2026-08-03
|
||||
- Источник: openspec/changes/archive/2026-08-03-slovar-kategorialnyh-znachenij/design.md
|
||||
|
||||
## Решение
|
||||
|
||||
Стабильный код HealthKit для локализованной строки хранится **отдельной строкой
|
||||
таблицы `category_value`** с ключом `(метрика, поле, значение)`, а не полем
|
||||
`value_code` внутри точки, как рисовал `architecture.md`. Словарь и таблица
|
||||
синонимов живут в бинаре (`internal/healthkit`), а не в базе. Наблюдение входит
|
||||
в отпечаток витрины, выведенный код — **нет**.
|
||||
|
||||
## Почему
|
||||
|
||||
Рассматривались три формы, и отвергнутые названы вместе с ценой.
|
||||
|
||||
**Поле внутри точки** — отвергнуто. Цитата источника: «Точка хранится
|
||||
**исходными байтами**; дописать в неё ключ можно только пересериализацией, а она
|
||||
теряет литерал (`1.0` → `1`, целые больше 2^53 сдвигаются, невалидный UTF-8 →
|
||||
U+FFFD) — ровно то, от чего `Point.Raw` защищает. Побайтовая врезка в чужой
|
||||
JSON — фокус, а не решение. Параллельный массив кодов в `bucket` завёл бы
|
||||
производную величину в путь слияния и хеширования: правило полноты, тай-брейк и
|
||||
`content_hash` пришлось бы учить носить код, не давая ему влиять на исход.
|
||||
Правка на поверхности `critical`-инвариантов ради нуля новых сведений — код есть
|
||||
**функция** от того, что уже лежит».
|
||||
|
||||
**Код нигде не хранится, выводится на чтении** — отвергнуто по одной причине:
|
||||
«тогда код недостижим ничем, кроме бинаря. Владелец сегодня читает витрину
|
||||
`sqlite` на хосте (`Read API` ещё нет), а вся задача затевается против того, что
|
||||
„клиент угадывает словарь“. Реестр без кода сообщает только „такая строка
|
||||
была“ — это половина ответа».
|
||||
|
||||
**Словарь в базе, а не в бинаре** — отвергнуто: «словарь стал бы входом,
|
||||
которого нет в журнале, и `import + replay` перестал бы задавать состояние
|
||||
однозначно. `stateOfMind` уже единственная дыра в журнале; вторую заводить
|
||||
незачем».
|
||||
|
||||
**Код вне отпечатка** — обратная сторона того же решения: «Ключ и провенанс —
|
||||
функция журнала; `code` — функция журнала **и версии словаря в бинаре**. Включи
|
||||
его в отпечаток, и он перестал бы отвечать на свой единственный вопрос („дал ли
|
||||
повтор журнала то же состояние“) ровно тогда, когда его задают: всякое
|
||||
пополнение словаря — а оно объявлено рабочим циклом — давало бы расхождение при
|
||||
побайтно совпавшем журнале, и человек, принимающий необратимое решение о
|
||||
подмене базы, читал бы это как дефект».
|
||||
|
||||
Prior art: FHIR `ConceptMap` (отображение «чужая система значений → своя») и
|
||||
`CodeSystem` с `replaced-by` для устаревших имён — те же два отношения,
|
||||
разведённые по разным сущностям. Форма взята, реализация FHIR отвергнута ценой.
|
||||
|
||||
## Последствия
|
||||
|
||||
- `+` Инвариант «точки хранятся дословно» не тронут вовсе: точка не меняется ни
|
||||
байтом, обратное преобразование возможно всегда.
|
||||
- `+` Пути слияния, тай-брейка и `content_hash` не знают о кодах — правки на
|
||||
поверхности `critical`-инвариантов не потребовалось.
|
||||
- `+` Пополнение словаря меняет десяток строк реестра, а не каждый объект с
|
||||
фазами сна; отпечаток при этом не двигается, потому что код в него не входит.
|
||||
- `−` Потребитель обязан делать соединение по `(метрика, поле, значение)` вместо
|
||||
чтения одного поля. Форма ответа Read API это скроет, когда он появится.
|
||||
- `−` Код в базе отстаёт от словаря в бинаре для строк, переставших приезжать.
|
||||
Лечится пересборкой; на сходимость не влияет.
|
||||
- `−` Ключ реестра зафиксирован миграцией `00010`: смена формы ключа стоит
|
||||
второй миграции и пересборки.
|
||||
+5
-2
@@ -33,8 +33,11 @@
|
||||
| Дата | Запись | Статус |
|
||||
| --- | --- | --- |
|
||||
|
||||
Записей пока нет: каталог заведён переездом на канон 2026-08-03. Сырьё для
|
||||
промоута накоплено — девять архивных изменений в
|
||||
- [ADR-2026-08-03-kod-ryadom-so-strokoj-reestrom](ADR-2026-08-03-kod-ryadom-so-strokoj-reestrom.md)
|
||||
— код HealthKit кладётся реестром рядом со строкой, а не полем внутри точки;
|
||||
словарь живёт в бинаре, выведенный код в отпечаток витрины не входит.
|
||||
|
||||
Сырьё для промоута накоплено — архивные изменения в
|
||||
`openspec/changes/archive/`, из них решения с дорогим откатом и намеренные
|
||||
отказы есть как минимум в `2026-08-01-polnota-tochki-mnozhestvom-klyuchey`
|
||||
(идентичность точки и тай-брейк), `2026-08-02-reindex-iz-arhiva` (подмену базы
|
||||
|
||||
+31
-7
@@ -1103,21 +1103,45 @@ HAE отдаёт перечислимые значения строками из
|
||||
Он объявлен источником истины, и на нём держится ретеншен нижнего слоя —
|
||||
но сверить покрытие по этим полям было бы нечем.
|
||||
|
||||
Поэтому строка **хранится дословно, а рядом кладётся выведенный код**:
|
||||
Поэтому строка **хранится дословно, а рядом кладётся выведенный код** —
|
||||
отдельной строкой реестра `category_value`, а не полем внутри точки:
|
||||
|
||||
```
|
||||
value "БДГ" ← как прислал HAE
|
||||
value_code "HKCategoryValueSleepAnalysisAsleepREM" ← выведено по словарю
|
||||
category_value sleep_analysis / value / "БДГ" → HKCategoryValueSleepAnalysisAsleepREM
|
||||
точка {"date": …, "value": "БДГ", …} ← не тронута
|
||||
```
|
||||
|
||||
Словарь ключуется парой `(локаль, строка)`; локаль берётся из
|
||||
`Accept-Language`, который мы уже сохраняем (находка 32). Для незнакомой
|
||||
строки код пустой — пустота честнее догадки, и она же видна в `/stats` как
|
||||
список того, что пора добавить в словарь.
|
||||
Рядом, а не внутри, по трём причинам: точка хранится исходными байтами и
|
||||
дописать в неё ключ можно только пересериализацией; параллельный массив кодов в
|
||||
`bucket` завёл бы производную величину в путь слияния и хеширования; пополнение
|
||||
словаря переписывало бы каждый объект с фазами сна. Обоснование целиком — в
|
||||
[журнале решений](adr/README.md).
|
||||
|
||||
Ключ реестра — `(метрика, поле, значение)`. Словарь при этом ключуется парой
|
||||
`(локаль, строка)`, локаль берётся из `Accept-Language` (находка 32) и **в ключ
|
||||
реестра не входит**: заголовков в сыром архиве нет, и ключ с локалью сделал бы
|
||||
состояние функцией от того, уцелела ли учётная строка. Локаль сужает поиск; её
|
||||
отсутствие вывода не отменяет, если строка однозначна по всем локалям.
|
||||
|
||||
Словарь и таблица синонимов кодов живут в бинаре (`internal/healthkit`), а не в
|
||||
базе: словарь, наполняемый руками, стал бы входом, которого нет в журнале, и
|
||||
`import + replay` перестал бы задавать состояние однозначно. Синонимы нужны
|
||||
потому, что коды тоже не вечны: Apple переименовала `…Asleep` в
|
||||
`…AsleepUnspecified` и переписывает историю при выгрузке (находка 43).
|
||||
|
||||
Для незнакомой строки код пустой — пустота честнее догадки, и перечень таких
|
||||
строк в реестре есть заявка на пополнение словаря. Счётчик неизвестных строк
|
||||
уходит в лог свёртки числом; сами строки — данные о здоровье и в лог не
|
||||
попадают.
|
||||
|
||||
Дословность инварианта не нарушена: код **приписывается**, а не подменяет
|
||||
строку. Обратное преобразование всегда возможно.
|
||||
|
||||
Реестр — единица хранения витрины и входит в отпечаток **наблюдением**, но не
|
||||
выведенным кодом: код производен от словаря в бинаре, а не от журнала, и в
|
||||
отпечатке он превратил бы всякое пополнение словаря в расхождение при совпавшем
|
||||
журнале.
|
||||
|
||||
### Тренировки и прочие секции
|
||||
|
||||
<!-- канон: поведение → openspec/specs/parsing -->
|
||||
|
||||
@@ -16,6 +16,24 @@
|
||||
единица, которой нет в счётчиках, делает расхождение безадресным: человек
|
||||
видит «не совпало» при неизменившемся числе объектов и принимает по этому
|
||||
необратимое решение о подмене базы.
|
||||
- **Провенанс, входящий в отпечаток, обязан быть явной функцией журнала.**
|
||||
«Кто первым записал строку» — функция порядка свёртки, а он порядку журнала не
|
||||
равен: живой приём и пересборка разойдутся при одинаковом журнале. Там, где
|
||||
провенанс в отпечаток не идёт, слабое правило допустимо и должно быть названо
|
||||
слабым на месте — иначе его скопируют туда, где оно неверно (`bucket` против
|
||||
`category_value`).
|
||||
- **Колонка, производная от бинаря, а не от журнала, в отпечаток не входит.**
|
||||
Кэш чистой функции (код по словарю, справочное имя) в отпечатке превращает
|
||||
всякую правку бинаря в расхождение при побайтно совпавшем журнале — и человек,
|
||||
принимающий по отпечатку необратимое решение о подмене базы, читает это как
|
||||
дефект. Правильность самой производной проверяют её тесты: это другой вопрос,
|
||||
и смешение обесценивает оракул сходимости.
|
||||
- **Граница на число элементов, набираемых из чужого тела, применяется при
|
||||
накоплении, а не при выдаче.** Накопитель без границы растёт вместе с телом,
|
||||
а тело контролирует отправитель; отказ по памяти в фоновой горутине не
|
||||
перехватывается, и перезапуск берёт ту же доставку. Усечение при этом обязано
|
||||
остаться функцией множества (например, N наименьших ключей), иначе порядок
|
||||
элементов на проводе решает состав витрины.
|
||||
- Правило выбора между двумя версиями одних данных объявляется либо **функцией
|
||||
множества версий**, либо явно **функцией порядка журнала** — третьего
|
||||
состояния нет. «Побеждает последняя пришедшая» третьим состоянием и является:
|
||||
|
||||
@@ -23,6 +23,16 @@
|
||||
архиве, и ответ «пересворачивать нечего» произносится с числом.** Утверждение
|
||||
без числа не отличается от предположения, а цена ошибки здесь — необратимое
|
||||
решение о судьбе тел.
|
||||
- **Значение, попадающее в ключ витрины или в словарь, приёмочный тест берёт из
|
||||
`testdata`, а не из литерала в тесте.** Литерал, набранный руками, не
|
||||
воспроизводит невидимые символы источника — Apple шлёт неразрывные пробелы
|
||||
внутри своих строк (находка 24), — и совпадение теста с реализацией доказывает
|
||||
только согласие автора с самим собой.
|
||||
- **Утверждение о таблице-константе обходит саму таблицу, а не её видимые
|
||||
следствия.** Проверка «таблица синонимов плоская», написанная через
|
||||
экспортированные функции, обходит лишь записи, достижимые из словаря: с
|
||||
неплоской таблицей она остаётся зелёной (воспроизведено). Такие утверждения
|
||||
живут во внутреннем тесте пакета и перебирают саму структуру.
|
||||
- **Тест «в логе нет значения» проверяет запись без служебных полей, а не сырой
|
||||
буфер.** Метка времени содержит доли секунды, поэтому искомая подстрока
|
||||
находится в ней сама: проверка на «5.1» краснела примерно раз на сотню
|
||||
|
||||
@@ -46,6 +46,17 @@ SQLite (`modernc.org/sqlite`, чистый Go), миграции — goose, фа
|
||||
│ created_at TEXT │ └──────────────────────────┘
|
||||
│ updated_at TEXT │
|
||||
└──────────────────────────┘
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ category_value │
|
||||
│ ───────────────────────── │
|
||||
│ metric TEXT ┐ │
|
||||
│ field TEXT ├PK │
|
||||
│ value TEXT ┘ │
|
||||
│ code TEXT │
|
||||
│ first_seen_utc TEXT │
|
||||
│ first_delivery_id TEXT │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
Связь `bucket.first_delivery_id → delivery.id` **внешним ключом не объявлена**
|
||||
@@ -156,6 +167,47 @@ ROWID` строка целиком, вместе со сжатым `payload`, ж
|
||||
журнала, а не свёрнутая последней. Подробности и обоснование — в
|
||||
`architecture.md`, раздел «Тренировки и прочие секции».
|
||||
|
||||
## `category_value` — реестр категориальных значений
|
||||
|
||||
Какие перечислимые строки поток приносил и какой у них стабильный код
|
||||
HealthKit. HAE отдаёт фазу сна как «БДГ», контекст пульса как «Сидячий образ
|
||||
жизни», тип тренировки как «В помещении Ходьба» — строками локали телефона, а
|
||||
родной экспорт Apple говорит кодами; без словаря источники не сходятся
|
||||
(находка 37). Словарь фаз сна выведен сопоставлением потока с экспортом за тот
|
||||
же период (находка 43).
|
||||
|
||||
| Колонка | Смысл |
|
||||
|---|---|
|
||||
| `metric` | имя метрики или секции, **то же**, которым адресуется единица хранения (`sleep_analysis_summary` после разделения схем, `workouts` у тренировок). Второе имя для того же понятия развело бы наблюдение и объект по разным ключам |
|
||||
| `field` | имя поля внутри точки или сущности дословно как у HAE: `value`, `context`, `name` |
|
||||
| `value` | строка **дословно**, как прислал HAE. Код приписывается рядом, а не подменяет её: инвариант «точки хранятся дословно» это и означает |
|
||||
| `code` | канонический код HealthKit. Пустая строка — законное состояние: «словарь этой строки не знает», и перечень таких строк есть заявка на пополнение словаря. **Это кэш**: код производен от словаря в бинаре, а не от журнала, и потому в отпечаток витрины не входит. Строка, переставшая приезжать, держит код прежнего словаря до пересборки |
|
||||
| `first_seen_utc`, `first_delivery_id` | провенанс **первой** встречи, минимум по журналу `(received_at, id)`. Минимум идемпотентен при повторной свёртке той же доставки; счётчик встреч не идемпотентен и потому не заводится вовсе. Отвечает на вопрос «когда сменился язык телефона», а язык доставки восстанавливается по `delivery.headers` |
|
||||
|
||||
Ключ — тройка без локали, и это решение, а не упущение. Локаль приезжает
|
||||
заголовком `Accept-Language`, а заголовков в сыром архиве нет: они были
|
||||
заголовками запроса, а не телом. Доставка, восстановленная из осиротевшего
|
||||
тела, приходит без локали — ключ с локалью положил бы вторую строку на то же
|
||||
значение, то есть состояние стало бы функцией от того, уцелела ли учётная
|
||||
строка. Локаль при выводе кода сужает поиск по словарю; её отсутствие вывода не
|
||||
отменяет, если строка однозначна.
|
||||
|
||||
Таблица `WITHOUT ROWID`: обращение всегда по полному первичному ключу, а строк
|
||||
единицы — на живом потоке различных значений по всем трём полям около
|
||||
одиннадцати. Индексов нет: чтение идёт целиком, в порядке ключа.
|
||||
|
||||
Границы разбора не дают доставке положить больше 64 различных значений и
|
||||
значение длиннее 128 байт (измерено: ~11 значений, самое длинное 36 байт).
|
||||
Слишком длинное **отбрасывается со счётчиком, а не обрезается** — обрезанная
|
||||
строка неотличима от настоящей и стала бы самостоятельным ключом; сама точка
|
||||
при этом хранится целиком.
|
||||
|
||||
Data-миграции у таблицы нет и быть не может: коды выводятся из тел, а тела
|
||||
лежат в архиве. Реестр рабочей витрины наполняется по мере свёртки новых
|
||||
доставок и целиком — пересборкой. Отсюда первое расхождение отпечатков после
|
||||
выкатки: оно законно, и отчёт `reindex` называет его ожидаемым классом
|
||||
«появилась единица хранения».
|
||||
|
||||
## Представление данных
|
||||
|
||||
- **Точки часового объекта лежат сжатым BLOB** (`gzip`) в колонке `payload`.
|
||||
|
||||
@@ -1369,6 +1369,14 @@ RFC3339 Z 20 data.stateOfMind[].end = 2026-07-31T18:03:51
|
||||
То есть первую и главную часть словаря не надо составлять вручную — она
|
||||
выводится сопоставлением потока с экспортом за тот же период.
|
||||
|
||||
**Замер покрытия, 2026-08-03.** Прогон всего живого архива (145 доставок) через
|
||||
разбор с этим словарём даёт **12 различных категориальных строк** по трём полям:
|
||||
6 фаз сна — все с кодом, 6 без кода (`heart_rate.context` и имена тренировок,
|
||||
для которых словарь не выводился). То есть шесть выведенных строк покрывают
|
||||
поток целиком, а не частично: неопознанных фаз сна на корпусе ноль. Заголовков
|
||||
в архиве нет, поэтому прогон идёт с пустой локалью — и коды всё равно выводятся,
|
||||
что подтверждает: сопоставление по строке однозначно, пока словарь одноязычен.
|
||||
|
||||
## 44. `Correlation` — структурный элемент, и он появился только что
|
||||
|
||||
Давление приезжает не записью, а обёрткой из двух записей:
|
||||
|
||||
@@ -120,6 +120,13 @@
|
||||
- **`reimpl`** запускается по триггеру «новое правило слияния, идентичности или
|
||||
разбора». Единственный раз, когда триаж назвал его отсутствие дырой
|
||||
покрытия, — это была задача с новым правилом слияния сущностей.
|
||||
Второй замер (2026-08-03, словарь категориальных значений): триггер сработал
|
||||
на новом правиле разбора и ключе реестра, проход **окупился** — он независимо
|
||||
подтвердил замером две находки, до того имевшие только одно измерение (пик
|
||||
памяти накопителя: 1002 МиБ против 780 на базе; единицы счётчика отброшенных),
|
||||
и отдельно назвал семь мест, где существующее решение оказалось **лучше** его
|
||||
собственного. Второе ценно не меньше первого: оно показывает, где проход
|
||||
соглашается, а не только где спорит.
|
||||
- **`quick`** — правка документов, конфигурации, сообщений; ничего, что меняет
|
||||
хранимое.
|
||||
|
||||
@@ -360,6 +367,48 @@
|
||||
- **Итог по конвейеру:** `quick` 4, `standard` 6, `deep` 7–8, `design` 3.
|
||||
Было 11 на коде и 4 на дизайне.
|
||||
|
||||
## 2026-08-03 — метка от часов в отпечатке сделала тест функцией секунды прогона [пойман]
|
||||
|
||||
- **Где:** `internal/fold/categorical_test.go`, `TestFoldЛокальНеМеняетСостояния`
|
||||
- **Симптом:** гейт покраснел на одном подтесте из четырёх: «заголовок
|
||||
`{"Accept-Language":["de"]}` сдвинул отпечаток витрины». Три подтеста прошли.
|
||||
- **Причина:** тест сравнивал отпечатки четырёх независимых витрин, а метку
|
||||
приёма доставки брал из `store.Now()`. Провенанс первой встречи входит в
|
||||
отпечаток реестра — значит отпечаток зависел от того, уложились ли подтесты в
|
||||
одну секунду. Тест был флаки по построению и краснел бы у того, кто мимо
|
||||
проходил.
|
||||
- **Чем воспроизведён:** сам гейт; после замены `store.Now()` на фиксированную
|
||||
метку — `go test ./internal/fold -count=2` зелёный.
|
||||
- **Что меняем:** ничего в конвейере — гейт сработал ровно так, как задуман, и
|
||||
поймал класс, который прошлый раз (2026-08-02, подстрока «5.1» в метке
|
||||
времени) прожил незамеченным. Правило то же и уже записано в
|
||||
[conventions/testing.md](conventions/testing.md): величина, зависящая от хода
|
||||
часов, не участвует в утверждении. Запись здесь — потому что это второй случай
|
||||
одного класса за два дня, и третий стоит считать сигналом, а не совпадением.
|
||||
|
||||
## 2026-08-03 — прогон живого архива красный на master, и это не заметили две задачи подряд [проскочил]
|
||||
|
||||
- **Где:** `internal/replay/archive_test.go`, `measureStyles`
|
||||
- **Симптом:** `task verify:archive` в задаче про словарь категориальных
|
||||
значений упал на `step_count: противоречащих часов 1 при 22 согласных`.
|
||||
Проверено прогоном **базовой ревизии** `3df42af` из копии дерева на том же
|
||||
архиве: те же 2875 объектов, те же 285 координат сна, тот же отказ. Краснота
|
||||
унаследована, изменением не внесена.
|
||||
- **Причина:** утверждение «противоречий ноль» — посылка «род измерим», верная
|
||||
на корпусе, где её снимали. Корпус вырос до 145 доставок, и у `step_count`
|
||||
появился час, где минутный и часовой слои разошлись. Сама система при этом
|
||||
ведёт себя правильно: род объявляется только при единогласном свидетельстве,
|
||||
и `step_count` числится `unknown`.
|
||||
- **Почему не поймали:** ровно та же причина, что и в записи 2026-08-02, — у
|
||||
проверки, которую гейт не гоняет, краснота никому не видна. Разница в том, что
|
||||
тогда протухла константа, а теперь под вопросом сама посылка: противоречие —
|
||||
это либо дефект правила, либо законное свойство корпуса, и решать это не
|
||||
прогону.
|
||||
- **Что меняем:** конвейер — ничего. Решение о том, чем стал `step_count`
|
||||
(дефект измерения рода или законное противоречие, которое надо печатать, а не
|
||||
утверждать), принадлежит владельцу и заведено задачей отдельно от этого
|
||||
изменения. Названо здесь, чтобы третья задача подряд не открывала его заново.
|
||||
|
||||
## 2026-08-03 — ответ владельца не превращал задачу в берущуюся [проскочил]
|
||||
|
||||
- **Где:** конвейер, а не код — учёт задач, шаг «ответ на вопрос»
|
||||
|
||||
+13
-2
@@ -32,8 +32,10 @@ disabled`, `read auth disabled`), но стартовать не отказыв
|
||||
значения, метки времени, имена источников и устройств, имена секций, `id`
|
||||
тренировок и записей, содержимое маршрута.
|
||||
- **Заголовки доставки** — включая `automation-id`, `automation-aggregation`,
|
||||
`User-Agent`, `Accept-Language`, `Upload-Complete`; они пишутся в `delivery` и
|
||||
участвуют в выводе слоя. Заголовки полуправдивы: `automation-aggregation`
|
||||
`User-Agent`, `Accept-Language`, `Upload-Complete`; они пишутся в `delivery`,
|
||||
участвуют в выводе слоя, а `Accept-Language` — ещё и в выводе кода
|
||||
категориального значения (тег ограничен по длине и по форме, не тег даёт
|
||||
пустую локаль). Заголовки полуправдивы: `automation-aggregation`
|
||||
реальной гранулярности не описывает (разведка, находка 33).
|
||||
- **Размер тела** — предела на одну сущность нет; наблюдалось 63 МиБ на одной
|
||||
координате и 768 МиБ пика кучи на теле 40 МиБ.
|
||||
@@ -57,6 +59,15 @@ disabled`, `read auth disabled`), но стартовать не отказыв
|
||||
отчёт, имеет названный предел длины.
|
||||
- **Ключ сущности** — `род секции + id` из HealthKit для `record`, `id` для
|
||||
`workout`. `id` приходит из тела.
|
||||
- **Ключ наблюдённого категориального значения** — `метрика + поле + значение`.
|
||||
Значение приходит из тела дословно и уезжает в первичный ключ: предел на него
|
||||
назван числом (128 байт), число различных значений одной доставки ограничено
|
||||
(64), и **граница применяется при накоплении, а не при выдаче** — иначе
|
||||
накопитель растёт вместе с телом, а тело контролирует отправитель (измерено:
|
||||
миллион различных значений в теле 60 МиБ поднимал пик процесса с 780 до
|
||||
1002 МиБ). Значение, которое разбор JSON подменил (невалидный UTF-8, одинокий
|
||||
суррогат), наблюдением не считается вовсе: в ключ обязано попасть то, что
|
||||
пришло, а не то, что получилось.
|
||||
- **Файл базы и каталог архива** — из конфига, не из запроса.
|
||||
|
||||
## Что разграничивает доступ
|
||||
|
||||
Reference in New Issue
Block a user