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
+60
View File
@@ -151,3 +151,63 @@ func seedPending(t *testing.T, st *store.Store, id string, at time.Time) {
t.Fatalf("запись доставки %q: %v", id, err)
}
}
// Свёртка вне порядка журнала обнаруживается запросом «есть ли позже меня уже
// разобранная доставка». Это единственное наблюдение, по которому расхождение
// живой витрины с пересборкой видно до сверки отпечатков.
func TestParsedAfterВидитТолькоЗаписавшихВВитрину(t *testing.T) {
t.Parallel()
st := open(t)
ctx := context.Background()
at := ts(t, "2026-08-01T12:00:00Z")
early := at
late := at.Add(time.Second)
seedPending(t, st, "d-early", early)
seedPending(t, st, "d-late", late)
// Пока преемница в очереди, расхождения нет.
found, err := st.ParsedAfter(ctx, early, "d-early")
if err != nil {
t.Fatalf("ParsedAfter: %v", err)
}
if found {
t.Error("неразобранная преемница засчитана расхождением")
}
// `failed` преемницей не считается: в витрину она ничего не записала, и
// перестановка относительно неё содержимого не разводит. Исход этот
// штатный — невыводимый слой, — и его учёт превратил бы WARN в шум.
if err := st.FinishParse(ctx, "d-late", store.ParseOutcome{Status: store.ParseFailed}); err != nil {
t.Fatalf("FinishParse: %v", err)
}
found, err = st.ParsedAfter(ctx, early, "d-early")
if err != nil {
t.Fatalf("ParsedAfter: %v", err)
}
if found {
t.Error("отказавшая преемница засчитана расхождением")
}
// А разобранная — считается.
if err := st.FinishParse(ctx, "d-late", store.ParseOutcome{Status: store.ParseDone}); err != nil {
t.Fatalf("FinishParse: %v", err)
}
found, err = st.ParsedAfter(ctx, early, "d-early")
if err != nil {
t.Fatalf("ParsedAfter: %v", err)
}
if !found {
t.Error("разобранная преемница не замечена: свёртка вне порядка журнала осталась бы молчащей")
}
// Сама преемница расхождением не является: предикат строго «позже».
found, err = st.ParsedAfter(ctx, late, "d-late")
if err != nil {
t.Fatalf("ParsedAfter: %v", err)
}
if found {
t.Error("доставка засчитала расхождением саму себя")
}
}