store: при равной полноте точек побеждает пришедшая доставка

- байтовый порядок канонических форм остался тай-брейком только внутри одной
  доставки: на живом корпусе он решал 98,8% спорных координат и системно хранил
  меньшее значение, из-за чего step_count терял род и verify:archive был красным
- правило перестало быть коммутативным осознанно, поэтому порядок свёртки
  приведён к журнальному: проход воркера прекращается на отложенной доставке,
  а свёртка вне порядка журнала пишет WARN
- заведены счётчики PointsHeld и PointsErased — удержание полнотой и
  единственное направление, в котором правило теряет содержание
This commit is contained in:
av
2026-08-04 11:16:24 +03:00
parent ae607f1ceb
commit b278501a6e
44 changed files with 3780 additions and 172 deletions
+27
View File
@@ -247,6 +247,17 @@ func (s *Service) logResult(ctx context.Context, deliveryID string, st Stats) {
"buckets", st.Buckets,
"unchanged", st.Unchanged,
"overwrites", st.Overwrites,
// Удержания точек идут атрибутом, а не отдельной ветвью WARN: под новым
// тай-брейком это правило полноты, работающее штатно (на живом корпусе
// 981 координата из 80 129 спорных), и эскалация обесценила бы уровень.
// Наблюдаемым событие делает то, что оно посчитано отдельно от
// перезаписей и печатается прогоном пересборки.
"points_held", st.PointsHeld,
// Разрушительное направление считается отдельно и ЭСКАЛИРУЕТСЯ ниже:
// на живом корпусе это 2 координаты из 80 129 спорных, шквала не будет,
// а событие означает потерю содержания — обратимую пересборкой, пока
// жив архив.
"points_erased", st.PointsErased,
"incomparable", st.Incomparable,
"units_conflicts", st.UnitsConflicts,
"sealed_hits", st.SealedHits,
@@ -285,6 +296,12 @@ func (s *Service) logResult(ctx context.Context, deliveryID string, st Stats) {
if len(st.IncomparableAt) > 0 {
attrs = append(attrs, "incomparable_at", formatCollisions(st.IncomparableAt))
}
if len(st.PointsHeldAt) > 0 {
attrs = append(attrs, "points_held_at", formatCollisions(st.PointsHeldAt))
}
if len(st.PointsErasedAt) > 0 {
attrs = append(attrs, "points_erased_at", formatCollisions(st.PointsErasedAt))
}
if len(st.HeldAt) > 0 {
attrs = append(attrs, "held_at", formatEntityRefs(st.HeldAt))
}
@@ -338,6 +355,16 @@ func (s *Service) logResult(ctx context.Context, deliveryID string, st Stats) {
// информативнее, на живом потоке не случавшееся ни разу. Признаки при
// этом идут атрибутами всегда, так что выбор ветви ничего не прячет.
s.log.WarnContext(ctx, "delivery folded, incomparable point fields", attrs...)
case st.PointsErased > 0:
// НИЖЕ несравнимости, хотя событие тяжелее: на живом корпусе эти два
// множества совпадают (2 координаты и там, и там), и несравнимость
// сообщает больше — она называет ещё и то, что объединять поля было бы
// что. Ветвь эта говорит про случай, которого несравнимость не
// покрывает: сохранённая была надмножеством по именам, но значения
// общих ключей разошлись, разряд полноты погас, и содержание унесла
// пришедшая. Счётчики обеих ветвей идут атрибутами всегда, так что
// порядок ветвей ничего не прячет.
s.log.WarnContext(ctx, "delivery folded, arriving point dropped stored field", attrs...)
case st.Overwrites > 0:
// Единственное наблюдение, по которому проверяется правило слияния.
// В INFO оно тонуло: поток идёт раз в пять минут.