Цена читающего маршрута: чекпойнт WAL по таймеру и условный запрос
- рядом с воркером свёртки живёт горутина, раз в минуту разбирающая журнал пассивным чекпойнтом; «журнал не разбирается» видно строкой владельцу, а не только по `df`. Признак — пара чисел, а не флаг занятости: тот молчит под удерживаемым читателем (`busy=0` при 6256 страницах и пяти перенесённых), а при занятой блокировке отдаёт `-1` вместо ответа, и `-1 >= -1` читалось бы как «разобрано целиком» - каталог отвечает `304` на `If-None-Match`, не открывая снимок витрины. Метка собрана из всего, от чего зависит ответ: версии витрины (`data_version` с закреплённого соединения плюс поколение — значение локально для соединения и не переживает переоткрытия), горизонта измерения и области действия ресурса. Версия снимается до и после сборки: снятая после пометила бы устаревший снимок свежим номером - предел и дедлайн ответа отложены в задачу Read API точек вместе с измеренной ценой первого запроса; попутно починен флаки-тест чужой задачи, искавший значение точки в сыром буфере записи лога
This commit is contained in:
@@ -129,7 +129,7 @@ func TestКаталогОтдаётРазрезыИИзмеренныйРод(t
|
||||
incoming("sleep_analysis", "raw", "hr", time.Date(2026, 6, 1, 3, 7, 0, 0, time.UTC), 1),
|
||||
})
|
||||
|
||||
metrics, err := service(t, st).Metrics(context.Background())
|
||||
metrics, err := metricsOf(context.Background(), service(t, st))
|
||||
if err != nil {
|
||||
t.Fatalf("каталог: %v", err)
|
||||
}
|
||||
@@ -179,7 +179,7 @@ func TestКаталогНеИзмеряетПоНижнемуСлою(t *testing
|
||||
}
|
||||
merge(t, st, points)
|
||||
|
||||
metrics, err := service(t, st).Metrics(context.Background())
|
||||
metrics, err := metricsOf(context.Background(), service(t, st))
|
||||
if err != nil {
|
||||
t.Fatalf("каталог: %v", err)
|
||||
}
|
||||
@@ -198,7 +198,7 @@ func TestКаталогОграничиваетОкно(t *testing.T) {
|
||||
st := openStore(t)
|
||||
fill(t, st, "step_count", catalog.Window+7, true)
|
||||
|
||||
metrics, err := service(t, st).Metrics(context.Background())
|
||||
metrics, err := metricsOf(context.Background(), service(t, st))
|
||||
if err != nil {
|
||||
t.Fatalf("каталог: %v", err)
|
||||
}
|
||||
@@ -227,7 +227,7 @@ func TestКаталогПоказываетРасхождениеЕдиниц(t
|
||||
incoming("walking_running_distance", "minute", "m", base.Add(2*time.Hour), 2),
|
||||
})
|
||||
|
||||
metrics, err := service(t, st).Metrics(context.Background())
|
||||
metrics, err := metricsOf(context.Background(), service(t, st))
|
||||
if err != nil {
|
||||
t.Fatalf("каталог: %v", err)
|
||||
}
|
||||
@@ -249,7 +249,7 @@ func TestКаталогПоказываетРасхождениеЕдиниц(t
|
||||
func TestКаталогПустойВитриныПуст(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
metrics, err := service(t, openStore(t)).Metrics(context.Background())
|
||||
metrics, err := metricsOf(context.Background(), service(t, openStore(t)))
|
||||
if err != nil {
|
||||
t.Fatalf("каталог: %v", err)
|
||||
}
|
||||
@@ -268,7 +268,7 @@ func TestКаталогПересчитываетРодНаКаждыйЗапр
|
||||
ctx := context.Background()
|
||||
|
||||
fill(t, st, "step_count", 2, true)
|
||||
before, err := svc.Metrics(ctx)
|
||||
before, err := metricsOf(ctx, svc)
|
||||
if err != nil {
|
||||
t.Fatalf("каталог: %v", err)
|
||||
}
|
||||
@@ -277,7 +277,7 @@ func TestКаталогПересчитываетРодНаКаждыйЗапр
|
||||
}
|
||||
|
||||
fill(t, st, "step_count", 5, true)
|
||||
after, err := svc.Metrics(ctx)
|
||||
after, err := metricsOf(ctx, svc)
|
||||
if err != nil {
|
||||
t.Fatalf("каталог: %v", err)
|
||||
}
|
||||
@@ -310,7 +310,7 @@ func TestКаталогНеИзмеряетПоБудущимЧасам(t *testi
|
||||
merge(t, st, future)
|
||||
|
||||
handler := &logged{}
|
||||
metrics, err := catalog.New(st, slog.New(handler)).Metrics(context.Background())
|
||||
metrics, err := metricsOf(context.Background(), catalog.New(st, slog.New(handler)))
|
||||
if err != nil {
|
||||
t.Fatalf("каталог: %v", err)
|
||||
}
|
||||
@@ -344,7 +344,7 @@ func TestКаталогНеСверяетСлоиРазныхЕдиниц(t *tes
|
||||
}
|
||||
merge(t, st, points)
|
||||
|
||||
metrics, err := service(t, st).Metrics(context.Background())
|
||||
metrics, err := metricsOf(context.Background(), service(t, st))
|
||||
if err != nil {
|
||||
t.Fatalf("каталог: %v", err)
|
||||
}
|
||||
@@ -369,7 +369,7 @@ func TestКаталогПоказываетМетрикуСПустымИмен
|
||||
incoming("step_count", "minute", "count", base, 2),
|
||||
})
|
||||
|
||||
metrics, err := service(t, st).Metrics(context.Background())
|
||||
metrics, err := metricsOf(context.Background(), service(t, st))
|
||||
if err != nil {
|
||||
t.Fatalf("каталог: %v", err)
|
||||
}
|
||||
@@ -402,7 +402,7 @@ func TestКаталогПишетПредупреждениеОПротивор
|
||||
merge(t, st, points)
|
||||
|
||||
handler := &logged{}
|
||||
metrics, err := catalog.New(st, slog.New(handler)).Metrics(context.Background())
|
||||
metrics, err := metricsOf(context.Background(), catalog.New(st, slog.New(handler)))
|
||||
if err != nil {
|
||||
t.Fatalf("каталог: %v", err)
|
||||
}
|
||||
@@ -439,7 +439,7 @@ func TestКаталогОграничиваетЧислоЕдиниц(t *testing
|
||||
}
|
||||
merge(t, st, points)
|
||||
|
||||
metrics, err := service(t, st).Metrics(context.Background())
|
||||
metrics, err := metricsOf(context.Background(), service(t, st))
|
||||
if err != nil {
|
||||
t.Fatalf("каталог: %v", err)
|
||||
}
|
||||
@@ -463,7 +463,7 @@ func TestКаталогСообщаетОбОтказеХранилища(t *tes
|
||||
}
|
||||
|
||||
handler := &logged{}
|
||||
if _, err := catalog.New(st, slog.New(handler)).Metrics(context.Background()); err == nil {
|
||||
if _, err := metricsOf(context.Background(), catalog.New(st, slog.New(handler))); err == nil {
|
||||
t.Fatal("каталог на закрытой базе собрался")
|
||||
}
|
||||
rec, ok := handler.find("catalog failed")
|
||||
@@ -488,7 +488,7 @@ func TestКаталогНеПутаетОтменуСоСбоем(t *testing.T)
|
||||
cancel()
|
||||
|
||||
handler := &logged{}
|
||||
if _, err := catalog.New(st, slog.New(handler)).Metrics(ctx); err == nil {
|
||||
if _, err := metricsOf(ctx, catalog.New(st, slog.New(handler))); err == nil {
|
||||
t.Fatal("каталог собрался на отменённом контексте")
|
||||
}
|
||||
if _, ok := handler.find("catalog failed"); ok {
|
||||
@@ -498,3 +498,25 @@ func TestКаталогНеПутаетОтменуСоСбоем(t *testing.T)
|
||||
t.Error("отмена не отмечена вовсе — исход операции обязан быть виден")
|
||||
}
|
||||
}
|
||||
|
||||
// metricsOf — список метрик каталога без версии витрины: версию проверяют
|
||||
// отдельные тесты, остальным нужен только состав ответа.
|
||||
func metricsOf(ctx context.Context, s *catalog.Service) ([]catalog.Metric, error) {
|
||||
snap, err := s.Metrics(ctx)
|
||||
return snap.Metrics, err
|
||||
}
|
||||
|
||||
// Каталог подписывает свой ответ версией витрины: без неё транспорту нечего
|
||||
// поставить в `ETag`, и условный запрос не работает вовсе.
|
||||
func TestКаталогОтдаётсяСВерсиейВитрины(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
st := openStore(t)
|
||||
snap, err := service(t, st).Metrics(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("каталог: %v", err)
|
||||
}
|
||||
if snap.Version == "" {
|
||||
t.Error("каталог собран на стоящей витрине и остался без версии")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user