Приём отвечает 200 до свёртки, свёртку ведёт фоновый воркер
- Очередью служит сама таблица: доставка ждёт свёртки в статусе `pending`, канал несёт только бит «есть работа». Переполнять нечего, падение процесса очередь не теряет, а подбор `pending` при старте — обычный проход воркера, а не отдельный код. Классификация исхода общая с пересборкой журнала. - Исход разбора начал отражать доставку, а не обстоятельства: отмена и занятость базы статус не меняют (иначе конкуренция за базу выводила бы доставку из очереди навсегда), паника свёртки больше не валит процесс, а учёт доставки идёт через транзакцию с повторами. - Длинный бюджет ответа выдан маршруту приёма, а не всему серверу: `write_timeout` в Go покрывает и чтение тела, и общий подъём снял бы защиту с остальных маршрутов.
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
package replay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"git.vakhrushev.me/av/healthlog/internal/fold"
|
||||
"git.vakhrushev.me/av/healthlog/internal/hae"
|
||||
"git.vakhrushev.me/av/healthlog/internal/store"
|
||||
)
|
||||
|
||||
// Классификация исхода — та половина, которую пересборка и фоновый воркер
|
||||
// обязаны делить. Проверяется перебором классов, без базы и без архива: второй
|
||||
// классификатор разошёлся бы с первым молча, а по счётчику `partial`
|
||||
// принимается решение о судьбе тела в архиве.
|
||||
func TestClassifyРазводитИсходыПоКлассам(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
err error
|
||||
want Outcome
|
||||
}{
|
||||
{"успех", nil, Outcome{Folded: 1}},
|
||||
{"слой не выведен", hae.ErrLayerUnknown, Outcome{FailedLayer: 1}},
|
||||
{"слой не выведен, обёрнут", fmt.Errorf("свёртка: %w", hae.ErrLayerUnknown), Outcome{FailedLayer: 1}},
|
||||
{"содержимое не разбирается", hae.ErrMalformed, Outcome{FailedMalformed: 1}},
|
||||
{"база занята", store.ErrBusy, Outcome{Deferred: 1}},
|
||||
{"база занята, обёрнута", fmt.Errorf("слияние: %w", store.ErrBusy), Outcome{Deferred: 1}},
|
||||
{"работу прекратили снаружи", context.Canceled, Outcome{Deferred: 1}},
|
||||
// Дедлайн — свойство доставки, а не обстоятельств: она не уложится в
|
||||
// бюджет и в следующий раз, а повтор безнадёжного останавливает очередь.
|
||||
{"свёртка не уложилась в бюджет", context.DeadlineExceeded, Outcome{FailedOther: 1}},
|
||||
{"прочее", errors.New("диск отвалился"), Outcome{FailedOther: 1}},
|
||||
// Паника — дефект нашего кода, а не обстоятельство: доставка выводится
|
||||
// из очереди, тело ждёт пересборки.
|
||||
{"свёртка паниковала", fold.ErrPanicked, Outcome{FailedOther: 1}},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := classify(c.err); got != c.want {
|
||||
t.Errorf("Classify(%v) = %+v, ожидалось %+v", c.err, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Накопление — сложение по классам: у пересборки и у воркера один набор имён
|
||||
// для одних исходов.
|
||||
func TestOutcomeAddСкладываетПоКлассам(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var total Outcome
|
||||
total.Add(Outcome{Folded: 1, Partial: 1})
|
||||
total.Add(Outcome{Folded: 1, Incomparable: 2})
|
||||
total.Add(Outcome{Deferred: 1})
|
||||
|
||||
want := Outcome{Folded: 2, Deferred: 1, Partial: 1, Incomparable: 2}
|
||||
if total != want {
|
||||
t.Errorf("сумма %+v, ожидалась %+v", total, want)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user