отработаны находки ревью кода
Триаж свёл 62 сырые находки девяти проходов к 33 причинам: 3 блокера, 4 «сейчас», 2 развилки. Все закрыты регрессионными тестами. - схема точки сна определяется по самой точке, а не по индексу в исходном массиве: одна пропущенная точка меняла эпизод и сводку местами - доставка сворачивается одной транзакцией: частичное состояние было недетерминированным (восемь прогонов — семь состояний) - граница размера на распакованном теле: 400 КиБ gzip разворачивались в 400 МиБ мимо max_body_mb - столкновение — расхождение канонических форм, а не байтов; WARN с координатами объекта; payload без HTML-экранирования - выравнивание по местной метке: получасовые зоны уводили часовую выгрузку в minute - доставка из одних суточных сводок больше не отвергается целиком - единицы не переписываются молча; счётчик считает сохранённые точки - все выходы Fold логируются, исход пишется на переживающем отмену контексте Четыре развилки вынесены блокерами в беклог.
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# Разнести ответ приёма и свёртку доставки
|
||||
|
||||
**Приоритет:** блокеры
|
||||
|
||||
Вынуто ревью кода задачи `razbor-metrik-v-obekty` (профиль `deep`, находка №4
|
||||
триажа, severity major).
|
||||
|
||||
## Что решить
|
||||
|
||||
Свёртка выполняется **синхронно внутри обработчика запроса**, поэтому время
|
||||
ответа равно времени свёртки. Вопрос: разносить ли их, и какой ценой.
|
||||
|
||||
## Оракул: измерено
|
||||
|
||||
`WriteTimeout` в Go ставится в `readRequest` — **до** чтения тела и до вызова
|
||||
обработчика (`net/http/server.go:993-997`, прочитано в исходниках). Значит
|
||||
30 секунд по умолчанию это бюджет на всё сразу: дочитать до 64 МиБ по
|
||||
мобильной сети, сделать `fsync` архива, вставить доставку и свернуть.
|
||||
|
||||
Воспроизведено минимальной программой: сервер с `WriteTimeout=200ms`,
|
||||
обработчик спит 500 мс.
|
||||
|
||||
```
|
||||
handler: WriteHeader(200), body Write err=<nil>
|
||||
client: elapsed=501ms err=EOF
|
||||
```
|
||||
|
||||
Сервер считает, что отдал `200` — ошибки записи не видно, ответ ушёл в буфер и
|
||||
сбрасывается позже. Клиент получил обрыв. Код обработчика этого не видит, а
|
||||
`accessLog` честно запишет `status_code=200`: единственный сегодняшний канал
|
||||
наблюдаемости в этом сценарии врёт.
|
||||
|
||||
Стоимость свёртки измерена **до** перехода на одну транзакцию на доставку:
|
||||
|
||||
| тело | объектов | свёртка |
|
||||
|---|---|---|
|
||||
| 80 КиБ | 1001 | 815 мс |
|
||||
| 323 КиБ | 4001 | 3.07 с |
|
||||
| 1302 КиБ | 16001 | 11.07 с |
|
||||
|
||||
Одна транзакция на доставку убрала около 0.7 мс на объект (прогон живого
|
||||
архива ускорился с 64 до 52 секунд), но порядок величины остался: широкая
|
||||
доставка по-прежнему измеряется секундами.
|
||||
|
||||
Бьёт это по **широким проходам** — `Today`, `Previous 7 Days`, ручной
|
||||
экспорт, — то есть ровно по тем, ради которых заведён инвариант «дыры
|
||||
закрываются сами».
|
||||
|
||||
## Варианты и цена
|
||||
|
||||
**(а) Отвечать `200` сразу после архивации и учёта; свёртка — воркером в
|
||||
порядке журнала, с подбором `pending` при старте.**
|
||||
Цена: средняя — воркер, очередь, подбор при старте. Бонусом закрываются ещё
|
||||
две дыры: параллельные доставки одной автоматизации перестают гонять
|
||||
наследование слоя (сейчас вторая может не найти слоя первой и уйти в
|
||||
`failed`), и доставка, застрявшая в `pending` из-за сбоя записи, наконец
|
||||
кем-то подбирается.
|
||||
|
||||
**(б) Поднять `write_timeout` до согласованного с `foldTimeout`.**
|
||||
Цена: малая. Но худший случай (64 МиБ) всё равно минуты, и молчание
|
||||
`accessLog` остаётся.
|
||||
|
||||
**(в) Оставить как есть, задокументировав потолок размера доставки.**
|
||||
Цена: нулевая. Широкие проходы продолжают рваться.
|
||||
|
||||
## Рекомендация
|
||||
|
||||
**(а).** Единственный вариант, который решает причину, а не симптом, и попутно
|
||||
снимает две смежные находки. Он же приближает `reindex`: подбор `pending` при
|
||||
старте — его половина.
|
||||
|
||||
## Что стоит без решения
|
||||
|
||||
Ничего: свёртка работает, просто рискует не уложиться в таймаут на самых
|
||||
широких доставках. Данные при этом не теряются — тело ложится в архив **до**
|
||||
свёртки.
|
||||
|
||||
Связано: [reindex-iz-arhiva](reindex-iz-arhiva.md),
|
||||
[stats-nablyudaemost](stats-nablyudaemost.md) — метка «ответ не уложился в
|
||||
таймаут» должна попасть туда.
|
||||
Reference in New Issue
Block a user