- Очередью служит сама таблица: доставка ждёт свёртки в статусе `pending`, канал несёт только бит «есть работа». Переполнять нечего, падение процесса очередь не теряет, а подбор `pending` при старте — обычный проход воркера, а не отдельный код. Классификация исхода общая с пересборкой журнала. - Исход разбора начал отражать доставку, а не обстоятельства: отмена и занятость базы статус не меняют (иначе конкуренция за базу выводила бы доставку из очереди навсегда), паника свёртки больше не валит процесс, а учёт доставки идёт через транзакцию с повторами. - Длинный бюджет ответа выдан маршруту приёма, а не всему серверу: `write_timeout` в Go покрывает и чтение тела, и общий подъём снял бы защиту с остальных маршрутов.
68 lines
3.0 KiB
Go
68 lines
3.0 KiB
Go
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)
|
|
}
|
|
}
|