Дозакрыты находки ревью по слиянию сущностей
- Правило покрытия получило второй разряд (условный, как у точек), запрет вырождения формы и счёт содержательных элементов ряда: скелет из скаляров и ряд из null больше не затирают маршрут. Победитель внутри доставки стал функцией множества версий — общим помощником с точками, — а провенанс поднимается и при совпавшем хеше, иначе отложенная доставка возвращала витрину к прежнему содержимому. - Одно поле не того типа больше не уносит сущность, а пропуски видны в учётной записи доставки (миграция 00008, NULL = «не измерялось»); каноническая форма считается один раз и вне транзакции; откат бинаря поверх новой схемы отказывает на старте; текст ошибки разбора не несёт значений из тела. - Ревью кода профилем deep (девять проходов) нашло две регрессии и обе закрыты: безусловный второй разряд запирал законный досчёт навсегда, а выбор победителя был квадратичен по числу присланных версий одного ключа.
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
"time"
|
||||
|
||||
"git.vakhrushev.me/av/healthlog/internal/archive"
|
||||
"git.vakhrushev.me/av/healthlog/internal/hae"
|
||||
"git.vakhrushev.me/av/healthlog/internal/ident"
|
||||
"git.vakhrushev.me/av/healthlog/internal/store"
|
||||
)
|
||||
@@ -158,7 +159,17 @@ func (s *Service) Accept(ctx context.Context, body []byte, meta Meta) (Result, e
|
||||
if err != nil {
|
||||
// Тело уже на диске — данные не потеряны, но учёта нет. Такое тело
|
||||
// подберёт пересборка (`healthlog reindex`), заведя запись заново.
|
||||
s.log.ErrorContext(ctx, "delivery failed", "error", err, "delivery_id", res.DeliveryID, "raw_path", rawPath)
|
||||
//
|
||||
// Занятость базы называется отдельно. Уровень от этого не меняется:
|
||||
// тело осиротело в любом случае, и вернуть его в журнал может только
|
||||
// пересборка. Но лечится занятость не тем, чем сбой диска или испорченная
|
||||
// база, — это конкуренция за запись, и она будет повторяться. Признак
|
||||
// снимается с доменной ошибки, а не с предиката «обстоятельства вообще»:
|
||||
// тот включает ещё и отмену снаружи, а здесь она невозможна по
|
||||
// построению — учёт ведётся на контексте, переживающем обрыв соединения.
|
||||
s.log.ErrorContext(ctx, "delivery failed", "error", err,
|
||||
"delivery_id", res.DeliveryID, "raw_path", rawPath,
|
||||
"db_busy", errors.Is(err, store.ErrBusy))
|
||||
return Result{}, fmt.Errorf("record delivery: %w", err)
|
||||
}
|
||||
|
||||
@@ -227,7 +238,11 @@ func checkEnvelope(body []byte) error {
|
||||
|
||||
var env envelope
|
||||
if err := json.Unmarshal(body, &env); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrMalformed, err) //nolint:errorlint // причину наружу не раскрываем, она уходит в лог
|
||||
// Причина обрезается тем же пределом, что и в разборе: UnmarshalTypeError
|
||||
// кладёт в текст ЛИТЕРАЛ значения, и тело из миллиона цифр давало
|
||||
// мегабайт содержимого доставки в логе. Инвариант «тела только на DEBUG
|
||||
// и с обрезкой» относится и к DEBUG.
|
||||
return fmt.Errorf("%w: %s", ErrMalformed, hae.ClipCause(err))
|
||||
}
|
||||
if len(env.Data) == 0 {
|
||||
return fmt.Errorf("%w: нет объекта data", ErrMalformed)
|
||||
|
||||
Reference in New Issue
Block a user