feat: разбор и хранение тренировок и состояния разума
- секции `workouts` и `stateOfMind` покрыты разбором: тренировка лежит одной строкой вместе с маршрутом и внутренними рядами, запись — по ключу `род + id`; миграция 00007 заводит обе таблицы и возвращает в очередь `partial`-доставки с этими ключами - сущность заменяется целиком, но условно: приехавшая побеждает, если не теряет содержания сохранённой (множество ключей и длины верхнеуровневых массивов), а при равном содержании выигрывает версия из более поздней доставки ЖУРНАЛА — «побеждает приехавшая» было бы функцией порядка свёртки, и живая витрина расходилась бы с пересборкой молча - отпечаток витрины покрывает тренировки и записи и снимается одним снимком базы; отчёт `reindex` считает «было и стало» по каждой единице хранения
This commit is contained in:
@@ -83,12 +83,21 @@ func TestReplayЖивогоАрхива(t *testing.T) {
|
||||
t.Fatal("ни одна доставка не свернулась")
|
||||
}
|
||||
|
||||
// Половина живого потока не несёт metrics вовсе (находка 50): такие
|
||||
// доставки обязаны быть отличимы от разобранных целиком, иначе ретеншен
|
||||
// срежет тела, которые для stateOfMind единственный источник.
|
||||
if first.Partial == 0 {
|
||||
t.Error("ни одной частично разобранной доставки — перечисление непокрытых секций не работает")
|
||||
// Половина живого потока не несёт metrics вовсе (находка 50): это
|
||||
// тренировки и состояние разума. С тех пор как обе секции покрыты,
|
||||
// частично разобранных доставок в архиве нет — и вместо них проверяется то,
|
||||
// ради чего покрытие делалось: сущности доехали до витрины.
|
||||
//
|
||||
// Проверяется свойство, а не число: корпус растёт с каждой доставкой, а
|
||||
// прогон живого архива в гейт не входит, так что протухшая константа
|
||||
// покраснела бы молча.
|
||||
if first.Workouts == 0 {
|
||||
t.Error("тренировок в витрине нет — секция workouts не разбирается")
|
||||
}
|
||||
if first.Records == 0 {
|
||||
t.Error("записей в витрине нет — секция stateOfMind не разбирается")
|
||||
}
|
||||
t.Logf("тренировок %d, записей %d", first.Workouts, first.Records)
|
||||
|
||||
// Повторное проигрывание того же журнала даёт то же состояние: свёртка
|
||||
// детерминирована, и пересборка даёт то же, что живой приём.
|
||||
@@ -101,6 +110,10 @@ func TestReplayЖивогоАрхива(t *testing.T) {
|
||||
if second.Buckets != first.Buckets {
|
||||
t.Errorf("повторное проигрывание изменило число объектов: %d → %d", first.Buckets, second.Buckets)
|
||||
}
|
||||
if second.Workouts != first.Workouts || second.Records != first.Records {
|
||||
t.Errorf("повторное проигрывание изменило число сущностей: %d/%d → %d/%d",
|
||||
first.Workouts, first.Records, second.Workouts, second.Records)
|
||||
}
|
||||
if second.Fingerprint != first.Fingerprint {
|
||||
t.Errorf("повторное проигрывание изменило содержимое объектов:\n %s\n %s",
|
||||
first.Fingerprint, second.Fingerprint)
|
||||
|
||||
@@ -75,7 +75,12 @@ type Report struct {
|
||||
// исходов разошлись бы при первой же правке классификации.
|
||||
Outcome
|
||||
|
||||
Buckets int64
|
||||
Buckets int64
|
||||
// Workouts и Records — остальные единицы хранения витрины. Считаются рядом
|
||||
// с объектами потому, что отпечаток отвечает «да/нет» за витрину целиком, а
|
||||
// решение о подмене базы необратимо и требует направления расхождения.
|
||||
Workouts int64
|
||||
Records int64
|
||||
Fingerprint string
|
||||
|
||||
// Canceled — проигрывание прервано отменой, а не дошло до конца.
|
||||
@@ -171,6 +176,14 @@ func Run(ctx context.Context, o Options) (Report, error) {
|
||||
if err != nil {
|
||||
return stopOr(rep, err)
|
||||
}
|
||||
rep.Workouts, err = o.Target.CountWorkouts(ctx)
|
||||
if err != nil {
|
||||
return stopOr(rep, err)
|
||||
}
|
||||
rep.Records, err = o.Target.CountRecords(ctx)
|
||||
if err != nil {
|
||||
return stopOr(rep, err)
|
||||
}
|
||||
rep.Fingerprint, err = o.Target.Fingerprint(ctx)
|
||||
if err != nil {
|
||||
return stopOr(rep, err)
|
||||
@@ -190,7 +203,9 @@ func Run(ctx context.Context, o Options) (Report, error) {
|
||||
"failed_other", rep.FailedOther,
|
||||
"partial", rep.Partial,
|
||||
"incomparable", rep.Incomparable,
|
||||
"buckets", rep.Buckets)
|
||||
"buckets", rep.Buckets,
|
||||
"workouts", rep.Workouts,
|
||||
"records", rep.Records)
|
||||
return rep, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user