- отношение победы было нетранзитивным: полнота (частичный порядок) плюс тай-брейк (тотальный) в попарной свёртке давали цикл, из-за которого одна и та же доставка меняла содержимое объекта при каждой пересборке - надмножество побеждает только при совпадении значений общих содержательных ключей: иначе точка без единого измерения вытесняла измерение - Less стал тотальным, isEmpty не материализует значение, имя метрики в координате столкновения обрезается, отпечаток витрины включает units и sealed - на живом архиве строгий no-op: 1737 объектов, содержимое совпало побайтово
9.0 KiB
9.0 KiB
1. Полнота как множество (internal/canon)
- 1.1
isEmpty:null,"", число, равное нулю (0,0.0,0e0,-0),{},[];false— НЕ пустота. Считается по литералу, БЕЗ материализации значения: разбор целиком разворачивалheartbeatSeriesв[]anyна каждое сравнение (замер ревью: 410 мс на точку 16.5 МБ) - 1.2 Тип
Fullness(Equal/Superset/Subset/Incomparable, нумерация с единицы,String()) иRelateFullness(a, b []byte) Fullness;sourceв множества не входит; содержимое, не разбирающееся в объект, даёт пустое множество - 1.3 Второй разряд: при равенстве множеств содержательных ключей и совпадении их значений сравниваются множества всех ключей; несравнимость на втором разряде исходом не является
- 1.6 (по ревью) Надмножество побеждает только при совпадении значений общих содержательных ключей — иначе точка без единого измерения вытесняла измерение (
{qty:0.001,p1:null,p2:null}против{qty:72.5}) - 1.7 (по ревью)
Lessтотален: при отказе канонизации сравниваются исходные байты. Прежде на паре из двух неразбираемых значенийLessв обе стороны давалfalse, и победителем оказывался просто второй аргумент - 1.8 (по ревью) Разбор точки вынесен в
Fields/Analyzeи переиспользуется: сравнений квадратично по числу кандидатов - 1.4
Completenessудалить — второй меры полноты в проекте не остаётся - 1.5 Тесты
canonпо приёмочным критериям ниже
2. Разрешение столкновения (internal/store)
- 2.1
resolveвыбирает победителя из МНОЖЕСТВА кандидатов координаты: отбрасываются превзойдённые по полноте, среди оставшихся — минимум по каноническому порядку - 2.4 (по ревью, критическое) Попарная свёртка заменена на выбор из множества. Полнота — частичный порядок, тай-брейк — тотальный; вместе они давали нетранзитивное отношение победы, из-за которого одна и та же доставка, свёрнутая дважды, давала два состояния витрины поочерёдно
- 2.5 (по ревью) Имя метрики в координате столкновения обрезается: оно приходит из тела дословно, предел приёма 64 МиБ, и без обрезки одна доставка порождала WARN-строку в десятки мегабайт
- 2.2
MergeStats: счётчикIncomparableи координатыIncomparableAt(потолок тот же, что уCollisions); несравнимость — частный случай столкновения,Overwritesрастёт вместе с ней - 2.3 Тесты
store: регрессия «пять полей без содержания не стирают измерение», «надмножество побеждает», «при равном содержании поля не теряются», «несравнимые наборы считаются и дают координаты», «повторная свёртка не меняет состояние», «исход не зависит от перестановки (6 перестановок × вместе/порознь)», «падинг не вытесняет измерение»
3. Наблюдаемость (internal/fold)
- 3.1 Пробросить счётчик и координаты в
Stats; признак идёт атрибутом всегда, независимо от выбранной ветви - 3.2 Ветвь
WARNвlogResultвыше ветви перезаписей, без значений точек - 3.3 Тест на уровень, сообщение и набор атрибутов
4. Проверка на живых данных
- 4.1
task gateзелёный - 4.2
task verify:archiveпроходит и печатает отпечаток содержимого витрины - 4.5 (по ревью) Отпечаток включает
unitsиsealed, а поля переменной длины идут с длиной впереди: разделитель|допустим внутри имени метрики, и две разошедшиеся витрины давали один отпечаток - 4.3 Отпечаток совпадает с прежним:
05db720966e47bd5670bfe6b022ddf4a2194dc946aedf93db926b3e944f226d6(объектов 1737) — перепроверен после всех правок ревью: содержимое витрины не изменилось ни на одной координате. Сверка сделана временным возвратом прежнего формата отпечатка, иначе сравнивать было бы нечего. Отпечаток в новом формате —ba40c1d9bc8e6acfe953b5a207c2f8278d8bfbac116e2f1bee42484854c57fd6 - 4.4 Счётчик несравнимых наборов на всём архиве равен нулю (проверяет посылку «объединять поля не нужно»)
5. Документация
- 5.1
docs/architecture.md: правило полноты — множество ключей, а не их число; заодно строка «последние пришедшие данные всегда актуализируют картину», противоречащая правилу слияния - 5.2
docs/local-research.md, находка 49: замер под расширенным определением пустоты (несравнимых по-прежнему ноль) и наблюдение про нулевые значения как настоящие измерения - 5.3 Комментарии у изменённых функций отражают основание, а не только механику
Приёмочные критерии (из ревью предложения, профиль design)
- П1
RelateFullnessтотальна и не паникует:nil, пустой срез, усечённый JSON, не-объект ([],"s",123,null), объект с тысячами ключей - П2 исход не зависит от порядка — проверен не только на паре, но и на всех шести перестановках тройки, и при разбиении на разные доставки. На паре критерий выполнялся и у дефектной реализации: сломано было именно на трёх
- П3
resolve(a,a)возвращаетaдословно; повторная доставка часа не растит счётчики и не пишетWARN - П4 победитель — одна из входных точек дословно: ни объединения полей, ни возврата канонической формы
- П5 отношение согласовано:
Equalустойчива к порядку ключей и пробелам,Superset(a,b) ⟺ Subset(b,a),Incomparableсимметрична - П6 совпадение канонических форм влечёт
Equal; пустота и хеш считают числа одним кодом - П7 таблица записей пустоты:
0/0.0/0e0/-0/{}/{ }/[]/""/nullпусты,false/" "/"0"/[null]/{"a":null}— нет - П8 заданный исход на вырожденном входе: невалидный JSON, не-объект,
{}против непустой точки - П9 счётчик
Incomparableрастёт и после того, как список координат упёрся в потолок - П10 атрибуты
WARN— только счётчики и координаты объекта: ни значений, ни имён полей точки