Приём отвечает 200 до свёртки, свёртку ведёт фоновый воркер
- Очередью служит сама таблица: доставка ждёт свёртки в статусе `pending`, канал несёт только бит «есть работа». Переполнять нечего, падение процесса очередь не теряет, а подбор `pending` при старте — обычный проход воркера, а не отдельный код. Классификация исхода общая с пересборкой журнала. - Исход разбора начал отражать доставку, а не обстоятельства: отмена и занятость базы статус не меняют (иначе конкуренция за базу выводила бы доставку из очереди навсегда), паника свёртки больше не валит процесс, а учёт доставки идёт через транзакцию с повторами. - Длинный бюджет ответа выдан маршруту приёма, а не всему серверу: `write_timeout` в Go покрывает и чтение тела, и общий подъём снял бы защиту с остальных маршрутов.
This commit is contained in:
@@ -1,8 +1,40 @@
|
||||
package store
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ErrNotFound — записи нет. Граничную ошибку драйвера (sql.ErrNoRows)
|
||||
// транслируем в доменную здесь же, у источника, чтобы выше по коду не торчал
|
||||
// database/sql.
|
||||
var ErrNotFound = errors.New("запись не найдена")
|
||||
|
||||
// ErrBusy — база занята, и повторы транзакции этого не пересидели.
|
||||
//
|
||||
// Доменная ошибка, а не код драйвера: на неё ветвится свёртка. Отказ по
|
||||
// занятости не является свойством доставки — работа просто не сделана, и
|
||||
// доставка обязана остаться в очереди. Без этого различения конкуренция за
|
||||
// базу выводила бы доставку из очереди навсегда.
|
||||
var ErrBusy = errors.New("база занята")
|
||||
|
||||
// Transient отвечает, вызван ли отказ ОБСТОЯТЕЛЬСТВАМИ, а не данными.
|
||||
//
|
||||
// Ровно два случая: работу прекратили снаружи и база оказалась занята дольше,
|
||||
// чем длятся повторы транзакции. Оба означают «не сделано», а не «не выходит»,
|
||||
// поэтому работа обязана остаться к повторению.
|
||||
//
|
||||
// Определение живёт здесь, в одном месте, и его читают двое: тот, кто пишет
|
||||
// исход разбора доставки, и тот, кто классифицирует этот исход в счётчики. Две
|
||||
// копии правила разошлись бы, и доставка одновременно осталась бы в очереди и
|
||||
// числилась отказавшей.
|
||||
//
|
||||
// Дедлайн самой операции сюда НЕ входит: не уложившаяся в бюджет работа не
|
||||
// уложится в него и в следующий раз, а бесконечный повтор заведомо
|
||||
// безнадёжного — это очередь, которая не движется.
|
||||
func Transient(err error) bool {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return false
|
||||
}
|
||||
return errors.Is(err, context.Canceled) || errors.Is(err, ErrBusy)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user