store: при равной полноте точек побеждает пришедшая доставка
- байтовый порядок канонических форм остался тай-брейком только внутри одной доставки: на живом корпусе он решал 98,8% спорных координат и системно хранил меньшее значение, из-за чего step_count терял род и verify:archive был красным - правило перестало быть коммутативным осознанно, поэтому порядок свёртки приведён к журнальному: проход воркера прекращается на отложенной доставке, а свёртка вне порядка журнала пишет WARN - заведены счётчики PointsHeld и PointsErased — удержание полнотой и единственное направление, в котором правило теряет содержание
This commit is contained in:
@@ -120,19 +120,76 @@
|
||||
порядке журнала — `(received_at, id)`, как он определён capability пересборки.
|
||||
Распараллеливать свёртку MUST NOT.
|
||||
|
||||
Порядок здесь — не удобство отладки, а условие правильности содержимого:
|
||||
тай-брейк при равной полноте точек разрешается в пользу пришедшей доставки, то
|
||||
есть исход слияния есть функция порядка свёртки. Свёрнутая не в порядке журнала
|
||||
доставка возвращает координату к версии, которую источник уже пересчитал, и
|
||||
живая витрина расходится с тем, что даёт пересборка.
|
||||
|
||||
Проход воркера SHALL прекращаться на первой доставке, чей исход свёртки
|
||||
**классифицирован как отложенный** (занятость базы, отмена снаружи), а не
|
||||
перешагивать её. Перешагнув, проход свернул бы её преемниц раньше неё.
|
||||
|
||||
Предикат остановки — именно класс исхода, а не статус доставки. Доставка,
|
||||
оставшаяся `pending` из-за отказа записи самого исхода, курсор двигает и проход
|
||||
не останавливает: иначе проход выбирал бы её бесконечно, свёртка встала бы
|
||||
целиком, а приём продолжал бы отвечать `200`.
|
||||
|
||||
Очередь при этом не встаёт: собственный дедлайн свёртки обстоятельством
|
||||
MUST NOT считаться — доставка, не уложившаяся в бюджет, получает `failed` и
|
||||
очередь освобождает, а занятость базы блокирует запись всем одинаково. Проход
|
||||
возобновляется сигналом приёма или периодическим пробуждением.
|
||||
|
||||
Стоящая голова очереди молчать MUST NOT: у неё нет верхнего предела ожидания, и
|
||||
снаружи она неотличима от здорового потока, потому что приём продолжает отвечать
|
||||
`200`. Метка отставания (см. ниже) SHALL писаться и на выходе прохода по
|
||||
барьеру, а не только на пустой выборке: занятая голова очереди до пустой
|
||||
выборки не пропускает проход НИКОГДА, и метка, привязанная к ней, молчала бы
|
||||
ровно в том состоянии, ради которого заведена. Флаг «первый проход завершён»
|
||||
при этом взводиться MUST NOT — проход до конца очереди не дошёл.
|
||||
|
||||
Достижимая гарантия называется точно: в порядке `(received_at, id)`
|
||||
сворачиваются все доставки, **видимые воркеру** на момент выборки. Доставка,
|
||||
ставшая видимой позже курсора прохода, подбирается следующим проходом;
|
||||
абсолютного порядка при конкурентных приёмах система не обещает.
|
||||
абсолютного порядка при конкурентных приёмах система не обещает, потому что
|
||||
строка учёта становится видимой только после записи тела (измерено 184 мс на
|
||||
62 МиБ).
|
||||
|
||||
Последствие этого предела называется вслух: доставка без плотных метрик,
|
||||
свёрнутая раньше своей предшественницы, слоя не выведет и получит `failed` — то
|
||||
есть её точки в витрину не попадут до пересборки. Живое состояние в этом случае
|
||||
расходится с тем, что даёт `healthlog reindex`. Окно узкое (обе доставки должны
|
||||
приниматься одновременно, и только у автоматизации без плотных метрик), и
|
||||
изменение его сужает, а не открывает: прежде свёртка шла в порядке завершения
|
||||
обработчиков. Устранение предела — отдельный вопрос, оно требует удерживать
|
||||
порядок на самом приёме.
|
||||
Остаточное окно молчать MUST NOT. Перед свёрткой воркер SHALL спрашивать
|
||||
журнал, есть ли доставка **позже** этой по `(received_at, id)`, уже записавшая
|
||||
исход разбора в витрину — то есть в статусе `parsed` или `partial`. Есть —
|
||||
пишется одна запись `WARN` на доставку, с её идентификатором, ожиданием в
|
||||
секундах и без значений точек.
|
||||
|
||||
Спрашивать журнал система SHALL **до** свёртки, а писать запись — **после** и
|
||||
только если свёртка состоялась: после свёртки предикат уже видит саму эту
|
||||
доставку разобранной, а отложенная доставка витрину не трогала, и запись о
|
||||
свёртке вне порядка утверждала бы событие, которого не было, — да ещё
|
||||
повторялась бы каждым проходом, пока голова очереди занята. Это единственное наблюдение, по которому расхождение живой витрины с
|
||||
пересборкой вообще обнаружимо до сверки отпечатков; лечится оно
|
||||
`healthlog reindex`.
|
||||
|
||||
Статус `failed` в предикат входить MUST NOT: такая доставка в витрину ничего не
|
||||
записала, и перестановка относительно неё содержимого не разводит. А
|
||||
`failed` — штатный исход (невыводимый слой), и его учёт превратил бы `WARN` в
|
||||
шум, на который перестают смотреть.
|
||||
|
||||
Пересборка эту проверку выполнять MUST NOT: она идёт в порядке журнала по
|
||||
построению, и её тишина здесь содержательна.
|
||||
|
||||
Проверка эта — **страж окна, а не постоянная часть свёртки**: закрыв порядок на
|
||||
самом приёме, её SHALL снять вместе с окном. Сказано здесь потому, что иначе
|
||||
страж переживёт стерегомое и станет тем, что следующий читатель удалит без
|
||||
объяснения.
|
||||
|
||||
Последствие предела называется вслух: доставка без плотных метрик, свёрнутая
|
||||
раньше своей предшественницы, слоя не выведет и получит `failed` — то есть её
|
||||
точки в витрину не попадут до пересборки. Та же перестановка при равной полноте
|
||||
точек оставляет в витрине версию не той доставки, что стоит в журнале последней.
|
||||
Окно узкое (обе доставки должны приниматься одновременно), и изменение его
|
||||
сужает, а не открывает: прежде проход перешагивал отложенную доставку.
|
||||
Устранение предела — отдельный вопрос, оно требует удерживать порядок на самом
|
||||
приёме.
|
||||
|
||||
Воркер SHALL продвигаться по неразобранным доставкам строго возрастающим
|
||||
курсором в пределах одного прохода. Курсор обязателен для завершимости:
|
||||
@@ -159,6 +216,21 @@
|
||||
- **AND** доставка без плотных метрик наследует слой предшествующей ей по этому
|
||||
порядку доставки той же автоматизации, а не соседа по времени вставки
|
||||
|
||||
#### Scenario: Отложенная доставка держит очередь
|
||||
|
||||
- **GIVEN** в очереди несколько доставок, и свёртка первой из них отложена
|
||||
занятостью базы
|
||||
- **WHEN** воркер делает проход
|
||||
- **THEN** её преемницы в этом проходе не сворачиваются
|
||||
- **AND** следующий проход снова начинает с отложенной доставки
|
||||
|
||||
#### Scenario: Свёртка вне порядка журнала не молчит
|
||||
|
||||
- **GIVEN** доставка стала видимой после того, как её преемница по журналу уже
|
||||
вышла из очереди
|
||||
- **WHEN** воркер сворачивает её
|
||||
- **THEN** система пишет `WARN` с идентификатором доставки и без значений точек
|
||||
|
||||
#### Scenario: Доставка, не записавшая исход, не зацикливает проход
|
||||
|
||||
- **WHEN** свёртка доставки не смогла записать исход разбора и оставила её
|
||||
@@ -170,7 +242,6 @@
|
||||
- **GIVEN** доставка осталась `pending`, и новых доставок не приезжает
|
||||
- **WHEN** наступает очередное периодическое пробуждение
|
||||
- **THEN** воркер пробует свернуть её снова
|
||||
|
||||
### Requirement: Подбор неразобранного при старте — та же операция
|
||||
|
||||
При старте система SHALL сворачивать доставки, числящиеся неразобранными, тем
|
||||
@@ -320,4 +391,3 @@ NOT: факт уходит в `DEBUG`, приём продолжается.
|
||||
- **THEN** отказ логируется на уровне `ERROR` вместе с путём тела в архиве
|
||||
- **AND** запись отличает занятость базы от прочих причин отказа
|
||||
- **AND** тот же отказ по другой причине этого признака не несёт
|
||||
|
||||
|
||||
Reference in New Issue
Block a user