- Правило покрытия получило второй разряд (условный, как у точек), запрет вырождения формы и счёт содержательных элементов ряда: скелет из скаляров и ряд из null больше не затирают маршрут. Победитель внутри доставки стал функцией множества версий — общим помощником с точками, — а провенанс поднимается и при совпавшем хеше, иначе отложенная доставка возвращала витрину к прежнему содержимому. - Одно поле не того типа больше не уносит сущность, а пропуски видны в учётной записи доставки (миграция 00008, NULL = «не измерялось»); каноническая форма считается один раз и вне транзакции; откат бинаря поверх новой схемы отказывает на старте; текст ошибки разбора не несёт значений из тела. - Ревью кода профилем deep (девять проходов) нашло две регрессии и обе закрыты: безусловный второй разряд запирал законный досчёт навсегда, а выбор победителя был квадратичен по числу присланных версий одного ключа.
11 KiB
Why
Изменение «тренировки и записи с собственным id» (f8200f7) прошло ревью не
полностью: проходы adversary, ops и архитектурный на коде не запускались.
Дозапуск нашёл девять причин, триаж оставил семь — у каждой прогнанный оракул.
Две из них необратимы по последствиям: обеднённая версия тренировки затирает
маршрут молча (в экспорте Apple маршрута нет, reindex проиграет то же
поражение), а одно поле не той формы уносит тренировку целиком, причём доставка
при этом числится разобранной — то есть ретеншен, решающий «что потеряется,
если тело удалить», получит ложное «терять нечего».
Остальные пять — молчание там, где обещана детерминированность: откат бинаря
поверх новой схемы стартует без слова, победитель внутри доставки зависит от
порядка элементов на проводе, провенанс устаревает на каждой из ~26 повторных
присылок, канонизация идёт внутри транзакции вопреки собственному комментарию
(измерено: 768 МиБ пика, 5.019 с удержания блокировки), а тело доставки в 8 МиБ
целиком уезжает в текст ошибки и оттуда в WARN.
What Changes
- Сравнение содержания сущностей получает второй разряд и запрет вырождения
формы. Сегодня
Coversсмотрит только наличие ключа и длину верхнеуровневого массива, поэтому версия-скелет (каждый массив заменён массивом той же длины изnull, каждый вложенный объект — скаляром) признаётся равной настоящей и выигрывает тай-брейк журнала. Добавляются: ключи без содержания вторым разрядом (как у точек — «иначе ключ с пустым значением исчезает по жребию»), запрет покрывающей версии подменять объект или массив скаляром, и счёт содержательных элементов верхнеуровневого массива рядом с его длиной. Предел правила остаётся названным вслух и не закрывается: сокращение внутри элемента ряда (точка маршрута безaltitude) ловится только сверкой с телом в архиве. - Победитель внутри одной доставки становится функцией множества версий, а не
порядка элементов массива. Стандарт уже записан для точек и для сущностей
молча не применён: попарная свёртка частичного порядка с тотальным тай-брейком
нетранзитивна —
[A,B,C]даётC,[B,C,A]даётA. Механизм выносится в одного помощника, общего с точками. Счётчик различающихся версий при этом считает по канонической форме, а не по байтам: требование постановки («differs при разных байтах») исполнено по форме, потому что порядок ключей у HAE нестабилен и побайтовый счётчик срабатывал бы на норме потока; детерминизм обеспечен тай-брейком по байтам, а не счётчиком. - Провенанс обновляется при совпавшем хеше. Сегодня совпадение хеша
пропускает запись вместе с провенансом, и в сущности остаётся первая
свёрнутая копия, а не победитель по журналу; при возврате содержимого к
прежнему живая витрина расходится с
reindex. - Одно поле не того ТИПА больше не уносит сущность. Пять полей заголовка
читаются мягко — тем же принципом, который уже записан в коде для
duration. Граница названа вслух: правило закрывает смену типа значения, но не смену формата строки, а наблюдался именно дрейф формата дат — тренировка с незнакомой датой по-прежнему теряется целиком, и закрыть это может только хранение сущности с неразобранной меткой (остаток). Пропуски сущностей при этом становятся видны в учётной записи доставки, а не только в логе, причём «не измерялось» отличимо от нуля. - Счётчик удержанных версий выводится в отчёт пересборки. Новое правило строже прежнего, а сходимость отпечатка его не проверяет по построению: живой приём и пересборка пользуются одним правилом и одинаково сойдутся на одинаково удержанной версии.
store.Openсверяет версию схемы перед миграцией. Версия базы выше версии бинаря — отказ, а не повод мигрировать; прецедент записан вOpenForRead.- Канонизация сущности уезжает за транзакцию по-настоящему. Каноническая
форма считается один раз на версию, до входа в транзакцию, и из неё же
берётся хеш — сегодня форма считается дважды (в хеше и в отложенном разборе),
причём второй раз внутри
inTx, который повторяется до пяти раз. - Текст ошибки разбора перестаёт нести значения из тела: называется тип токена и смещение во входе.
- Лог
delivery failedна приёме отличает занятость базы от прочих причин. - Не входит: хранение сущности с
id, но неразобранной меткой (NULL-метка); пределы на размер одной сущности и секции и потоковый расчёт формы и хеша; принцип «data-миграции не отбирают строки по обрезаемым спискам»; длина очередиpendingв/stats. Всё четыре уходят задачами беклога.
Capabilities
New Capabilities
Новых нет. Все семь пунктов — уточнения уже записанных правил разбора и хранения; новое понятие ввёл бы второй словарь для того же домена.
Modified Capabilities
parsing: заголовок сущности читается мягко — поле не той формы пропускает само поле, а не сущность; диагностика разбора не несёт значений из тела.storage: содержание сущностей сравнивается с двумя разрядами, запретом вырождения формы и счётом содержательных элементов массива; победитель внутри доставки объявлен функцией множества с тотальным порядком; провенанс обновляется при совпавшем хеше, а метка изменения — нет; учётная запись доставки несёт число пропущенных сущностей, отличая ноль от «не измерялось»; открытие базы сверяет версию схемы; каноническая форма сущности считается вне транзакции и один раз.ingest: лог отказа приёма отличает занятость базы от прочих причин.reindex: число пропущенных сущностей внесено в перечень производных полей, которые пересборка не переносит; отчёт пересборки называет число удержанных версий сущностей.
Impact
internal/canon—Covers, форма массива, экспорт хеша по готовой форме.internal/store—entity.go(версия сущности, слияние, провенанс),bucket.go(комментарий о канонизации),store.go(страж версии схемы), новая миграция00008(колонкаskipped_entitiesу доставки).internal/hae—entity.go(мягкий заголовок),hae.go(текст ошибок).internal/fold,internal/ingest— проброс счётчика пропусков в учёт, различение занятости базы в логе.docs/architecture.md,docs/database.md,docs/conventions.md,docs/review-journal.md.- Оракулы из
tmp/adv/переезжают обычными тестами вinternal/canon,internal/store,internal/hae.