ADR-0004 · Терминал без связи: норма, а не деградация#
Статус: принят · 2026-09-18
Метод: D-06 v1.0 · шаблон ADR v1.2
Участники: сопровождение (3 человека), Каменев — руководитель операционного блока
Учебная запись: часть прогона E-03, система вымышлена. Форма и рассуждение настоящие.
Контекст#
Вход — 00-context.md. 108 пунктов из 900 (12 %) теряют связь суммарно до 40 минут в сутки, в сезон до двух часов [замер].
Два применения, а не одно#
D-06 группирует функции по классу важности, инварианту и зависимости. Зависимость одна — ядро; класс важности один; инварианты разные, и это делит функции надвое.
| Применение | Функции | Инвариант | Кто его держит |
|---|---|---|---|
| Первое | Выдача | И2 · выдача происходит один раз | Физический мир. Коробка одна; отдать её дважды нельзя, сколько бы раз ни нажали кнопку |
| Второе | Приёмка, возврат | И1 · у посылки одно место и один ответственный | Никто. Проверка «эта посылка предназначена этому пункту» существует только в ядре |
Третий вопрос D-06 решил здесь больше остальных вместе взятых, и ответ у него физический. Выдача офлайн безопасна не потому, что мы что-то построили, а потому, что инвариант закрывает полка. Приёмка офлайн опасна потому, что её инвариант не закрывает никто — и именно из неё система производит собственные расхождения, те самые 0,4 %.
Семь вопросов#
| Вопрос | Ответ |
|---|---|
| Какая функция и какая зависимость может отказать отдельно | Приём, выдача, возврат на пункте; зависимость — ядро учёта |
| Что обещано пользователю сейчас | Ничего явно. Терминал молча принимает и выдаёт, данные уезжают позже [орг] |
| Какой инвариант держит функция и закрывается ли где-то ещё | См. таблицу выше — разошлись [требования] |
| Какие суррогаты существуют | Локальный снимок: что этот пункт держит на момент последней связи. Существует уже сегодня [схема] |
| Класс важности относительно соседних | Высший: пункт без приёма и выдачи не работает, 1 400 монтажников не получают наряды [продукт] |
| Как система узнаёт об отказе — и совпадает ли это с тем, что видит потребитель | Терминал знает. Ядро — нет: со стороны ядра молчащий пункт неотличим от пункта без операций [схема] |
| Что станет с нагрузкой на зависимость при деградации | Ничего: суррогат локальный, обращений к ядру не добавляется [схема] |
Дисквалификаторы#
Из пяти сработали три, один перевернулся, один не сработал.
| Условие | Что вышло |
|---|---|
| Деградация нарушает инвариант; молчаливая подмена там, где по данным принимают решение | Сработал. Кладовщик, принимая посылку офлайн, решает, куда её положить и кому она предназначена. Данных для этого решения у него нет, и молчание превращает приёмку в обман. Отсекает нулевой вариант — то, как работает система сегодня |
| Путь деградации не исполняется регулярно | Перевернулся. Путь исполняется ежедневно на 108 пунктах. Дисквалификатор написан против редких путей и здесь не просто не срабатывает — он указывает в обратную сторону, см. замечание |
| Суррогат добывается через отказавшую зависимость | Не сработал: локальный снимок лежит на терминале и ядра не требует |
| Система не умеет заметить отказ | Сработал, и в обе стороны. Терминал отказ видит. Ядро — нет, а решение о разборе принимает ядро. Дисквалифицирован способ обнаружения: пока со стороны ядра молчащий пункт неотличим от пункта без работы, любой вариант останется невключённым |
| У режима нет выхода | Сработал частично. Условие снятия есть — связь восстановилась. Владельца разбора непроверенных операций нет |
Рассмотренные варианты#
Матрица для второго применения — приёмка и возврат. Первое применение матрицы не требует: инвариант держит физика, и вариантов, кроме «работать как работает», нет.
| Ось | Что записывать | 0 · как сейчас | Явный отказ | С пометкой, выдачу блокировать | С пометкой, без блокировки |
|---|---|---|---|---|---|
| Сохранность инварианта | Держится ли И1 | Нет — приёмка вслепую и производит расхождения | Да | Да — непроверенная посылка не уходит дальше | Нет: выдать можно то, что принято ошибочно |
| Что видит пользователь и понимает ли | Дословно | Ничего, как в норме | «Нет связи. Приём невозможен» | «Нет связи, данные на 09:14. Принято без проверки, выдача до восстановления связи заблокирована» | «Принято без проверки» |
| Нагрузка на отказавшую зависимость | Прирост обращений | 0 | 0 | 0 | 0 |
| Стоимость построения | Что строим | 0 | Экран отказа | Статус посылки, экран, блокировка выдачи | Статус и экран |
| Стоимость владения путём | Постоянная цена | 0 | Простой пункта до двух часов в сезон | Разбор непроверенных операций: владелец и регламент | Разбор без блокировки — то есть постфактум |
| Обратимость: как выходим | Условие и владелец | Неприменимо | Связь восстановилась | Связь восстановилась, ядро подтвердило, статус снят | Связь восстановилась |
Что стоит блокировка выдачи. Офлайн длится 4,8 % рабочего времени на 12 % пунктов, то есть задевает 0,57 % приёмок [расчёт]. Из них блокировка мешает только тем, за чьей посылкой пришли в то же окно, — доля, которую мы не мерили, но верхняя граница уже мала. Явный отказ, для сравнения, останавливает приём целиком на всё окно.
Решение#
Офлайн описан как нормальный режим работы, а не как деградация — см. замечание к методу. Формы:
- Выдача офлайн разрешена без ограничений. Терминал показывает полосу «Нет связи с ядром, данные на 09:14». Инвариант И2 держит полка.
- Приёмка и возврат офлайн разрешены с пометкой. Посылка получает статус «принята без проверки»; в этом статусе выдача заблокирована до подтверждения ядром. Экран кладовщика: «Принято без проверки. Выдача до восстановления связи заблокирована».
- Ядро получает сигнал «пункт не выходил на связь N минут». Без него молчащий пункт неотличим от пункта без работы, и дисквалификатор о способе обнаружения остаётся несnятым. Значение N и то, чем его мерить, — на этапе 6.
- Владелец разбора непроверенных операций — диспетчер склада-куратора. Регламент: подтверждённые ядром статусы снимаются автоматически; не подтверждённые за смену уходят в разбор поимённо.
- Регулярное исполнение пути не строится отдельно. Обязательная строка записи
D-06выполняется сама: путь исполняется ежедневно на 108 пунктах. Это единственный известный случай, когда она не требует работы.
Подтверждение со стороны продукта: Каменев, руководитель операционного блока, — обещание «принимаем всегда, выдаём только проверенное».
Обоснование#
Решил третий вопрос, а не матрица. Как только выяснилось, что И2 держит физический мир, а И1 — никто, две функции разошлись, и для одной из них вариантов не осталось вовсе. Матрица понадобилась только для второй.
Нулевой вариант отсечён дисквалификатором, а не проигрышем по осям — и это единственный вариант, который сегодня работает в проде. Система одиннадцать лет принимает вслепую и молчит об этом.
Блокировка выдачи выбрана против варианта без блокировки по единственной оси, где они расходятся: сохранность инварианта. Цена — 0,57 % приёмок в худшем случае; цена отказа от неё — сохранить ровно тот механизм, который производит расхождения.
Последствия#
Отрицательные:
- Появляется статус, которого не было, и он виден кладовщику. Одиннадцать лет терминал не различал проверенное и непроверенное; обучение 1 900 терминалов и их пользователей в цену решения входит.
- Разбор непроверенных операций — постоянная работа, которой сегодня нет ни у кого. Объём неизвестен: сколько операций уходит в офлайн, не меряли.
- Сигнал «пункт молчит» строится с нуля. Предметных метрик нет ни одной; это будет первая.
- 0,57 % приёмок получают задержку до двух часов в худшем случае, и эта доля сезонная — в ноябре выше.
- Блокировка выдачи ляжет на кладовщика, а не на систему: получатель стоит перед ним, а коробка есть. Регламент разговора в решение не входит, но без него решение не работает.
Положительные: механизм, производивший расхождения, назван и перекрыт; обещание впервые сформулировано словами и подтверждено именем; путь исполняется ежедневно и потому не может отказать от неиспользования.
Замечание к методу: у D-06 есть условие пересмотра без парного дисквалификатора#
Раздел 7 D-06 содержит условие: «Режим включался чаще, чем ожидалось. Частое срабатывание означает, что это не деградация, а нормальный режим работы, и его надо описывать как норму».
Здесь это условие выполнено до принятия решения, а не через год наблюдений: офлайн случается ежедневно и известен замером на входе. Документ применяется к тому, что по его же правилу не является его предметом.
Дисквалификатора, соответствующего этому условию пересмотра, у D-06 нет. Условие пересмотра срабатывает после, дисквалификатор — до; здесь нужен второй, а есть только первый.
И при этом документ оказался полезен, отведя себя. Он дал:
| Что дал | Откуда |
|---|---|
| Разделение на два применения с разными инвариантами | Правило «что считать одной функцией» |
| Асимметрию выдачи и приёмки | Третий вопрос — кто держит инвариант |
| Отсечение того, как система работает сегодня | Дисквалификатор о молчаливой подмене |
| Требование сигнала со стороны ядра | Дисквалификатор о способе обнаружения |
| Владельца разбора | Дисквалификатор о выходе из режима |
Это не «применим и бесполезен» — режим, которого ждали от D-04 и который ни разу не повторился. Это обратное: применим, отводит себя по собственному правилу и всё равно даёт результат. Как это записать в метод — вопрос ревизии; здесь зафиксировано наблюдение.
Условия пересмотра#
- Доля офлайна выросла или упала на порядок — 40 минут в сутки перестали быть верхней границей.
- Появился способ проверить принадлежность посылки пункту без ядра — например, признак в самом штрихкоде; тогда приёмка офлайн перестаёт быть слепой.
- Разбор непроверенных операций перестал выполняться в срок — решение просрочено, а не действует.
- Сигнал «пункт молчит» не ловит случай, когда терминал отвечает, а операции не уходят, — это серый отказ, и обнаружение придётся строить со стороны ядра иначе.
- Блокировка выдачи стала задевать заметно больше 0,57 % приёмок.
- Класс важности приёмки изменился относительно выдачи.
Самая дорогая будущая граница#
Граница между тем, что терминал знает сам, и тем, что знает только ядро. Сегодня она проходит по одному признаку — принадлежности посылки пункту. Каждая новая проверка, которую захотят добавить в приёмку, попадёт на ту же линию и потребует того же выбора: блокировать, помечать или принимать вслепую. Линия задана здесь и один раз; переносить её потом означает менять поведение 1 900 терминалов, обновляющихся неделями.
Связанные ADR#
Зависит от: ADR-0002 — локальная очередь терминала объявлена копией со сроком жизни 48 часов. Зависимые: цели и измеритель (этап 6) — оттуда придут значение N для сигнала «пункт молчит» и доля офлайновых операций.