Skip to main content
Режим обслуживания — это переключатель, который останавливает приём Omniflow новых денег, которые он пока не может отработать. Это одна строка в maintenance_state, и он намеренно узок: он блокирует новые покупки и удерживает исходящую выдачу доступа, не трогая больше ничего. Расчёты, возвраты и история заказов продолжают работать всё это время, потому что сбой, ради предотвращения которого он существует, — это списание с клиента за доступ, который установка не может выдать, а не заморозка установки, наполовину обработавшей уже принятые ею платежи. Двигать его могут оператор — из панели или по bearer-API — и автоматическое определение состояния зависимостей, работающее в процессе API.

Что он блокирует и что намеренно нет

Пока окно открыто, отклоняются ровно два действия. Третье, просмотр, разрешено явно, чтобы клиент по-прежнему мог добраться до объявления, до своих заказов и до поддержки. Проверка стоит на создании заказа, во всех пяти путях, которые его создают, поэтому новая покупка никогда не может дойти до платёжного провайдера. Это не вторая проверка внутри платёжного слоя — именно поэтому уже созданный заказ остаётся рассчитываемым. Заказ, оплаченный до открытия окна, по-прежнему рассчитывается, по-прежнему пополняет кошелёк или создаёт своё право на услугу и по-прежнему засчитывается реферальной программе. Ждёт только исходящий вызов Remnawave.

Удержанная задача выдачи доступа откладывается, а не проваливается

Когда воркер подхватывает задачу выдачи доступа во время окна, он паркует операцию в состоянии retrying с last_error_code = maintenance_active, уменьшает счётчик попыток обратно и откладывает задачу на минуту. Поэтому удержание не стоит ни одной единицы бюджета повторов, сколько бы окно ни длилось, а операция сохраняет свою запись, свой идентификатор корреляции и свой ключ идемпотентности. Она выполняется, как только окно закрывается. См. Задачи и очереди.

Что видит клиент

В боте поверхность покупки заменяется экраном обслуживания в точке входа — до того, как клиент что-то выберет, а не после того, как он нажал «оплатить». Экран показывает ваше объявление на его языке или встроенный локализованный текст, когда объявление пустое, плюс ожидаемое время возврата, если оно задано, с кнопками «повторить» и «открыть поддержку». Неудачное чтение состояния никогда не блокирует клиента: обслуживание — это сигнал оператору, а не отказоустойчивая блокировка, поэтому, если состояние прочитать не удалось, путь покупки остаётся открытым. В клиентской веб-панели дашборд показывает служебное объявление поверх всего остального, потому что инцидент объясняет состояние каждой подписки ниже. Он использует русское объявление для клиента с русской локалью, когда оно было задано, и английское в остальных случаях. Оформление заказа или покупка в магазине, предпринятая вопреки этому, отвечает 503 с кодом maintenance_active, который приложение отрисовывает как «покупки приостановлены», а не как общую ошибку. См. Клиентская веб-панель. Экраны только для чтения не блокируются никогда. Клиент в окне обслуживания по-прежнему видит свою подписку, свои заказы и свой кошелёк — ровно то, что он и захочет проверить.

Управление из панели

1

Откройте System → Health

Войдите на /admin и откройте System (право доступа system.read), вкладку Health. Она обновляется каждые 30 секунд. Карточка обслуживания показывает бейдж вкл/выкл и, пока режим включён, бейдж источника.
2

Прочитайте источник, прежде чем что-то трогать

manual означает, что кто-то включил режим и выключить его тоже может только кто-то. Любой другой источник означает, что окно открыло автоопределение и оно же закроет его при восстановлении. Показ того, что именно действует, — это то, что не даёт оператору ждать восстановления, которое никогда не наступит; а когда действует автоматическое окно, карточка объясняет, что снятие его вручную заново включит покупки, пока зависимость всё ещё лежит.
3

Заполните причину и оба объявления

С правом system.write вам доступны поле Reason, обязательное всегда, а также Notice RU и Notice EN, обязательные при включении. Переключатель остаётся заблокированным, пока все три не заполнены. Клиент, который читает только на одном языке и получает пустой экран, обслужен хуже, чем тот, кто получает объявление, которое может прочитать.
4

Переключите тумблер

Панель отправляет PUT /v1/panel/overview/maintenance с причиной в заголовке X-Operator-Reason. Для снятия нужна одна лишь причина.
Изменение, сделанное из панели, всегда записывается с source: "manual", даже если зависимость в этот момент лежит. Изменение из панели никогда не должно выдавать себя за автоопределение, потому что снимаются они разными вещами. Каждый переход пишет строку maintenance_events с действием, источником, причиной, типом и идентификатором актора, а также событие аудита — panel.maintenance.activated или panel.maintenance.cleared, категория system. Именно это позволяет оператору объяснить сбой потом. См. Панель администратора.
expectedReturnAt принимается API и отрисовывается клиентам, но в панели для него нет элемента управления. Задавайте его по bearer-API, если хотите видеть время возврата в объявлении.

Управление по bearer-API

Поверхность /v1/admin аутентифицируется через APP_OPERATOR_TOKEN и существует для скриптов и регламентов. Её правила отличаются от правил панели, и эта разница важна.
Снятие — это тот же вызов с "active": false. GET /v1/admin/maintenance возвращает текущее состояние, источник, причину, оба объявления, время включения и ожидаемое время возврата.
Поскольку bearer-путь принимает пустую причину и пустые объявления, скрипт может открыть окно, которое ничего не сообщает клиенту и не оставляет объяснения в maintenance_events. Задавайте и то и другое и предпочитайте формулировки, которые говорят что-то конкретное о причине и об ожидаемом возврате.

Автоопределение

Определение работает только в процессе API, потому что это тот процесс, который и так опрашивает каждую зависимость для своего эндпоинта готовности. Оно переиспользует тот же реестр проб, поэтому переключатель обслуживания и /readyz не могут разойтись во мнении о том, работает ли PostgreSQL.
  • Окно открывается после FAILURE_STREAK нездоровых замеров подряд, так что один тайм-аут никогда не закрывает установку.
  • Оно снимается после RECOVERY_STREAK здоровых замеров подряд, так что мигающая зависимость не открывает покупки заново в тот момент, когда она один раз ответила.
  • Окно, открытое оператором, никогда не снимается автоматически. Восстановление снимает только то окно, которое автоопределение открыло само.

Что оно отслеживает на самом деле

Автоопределение отслеживает postgres, remnawave и valkey. Сбой Valkey уже ломает ограничение частоты запросов и защиту от повтора однократного использования, отказывая закрыто, — см. Безопасность. Без наблюдения за Valkey клиент посреди оформления заказа получал бы противоречивые отказы вместо понятного уведомления об обслуживании. Наблюдение закрывает этот пробел: после той же серии отказов, что и для любой другой зависимости, сбой Valkey приостанавливает новые покупки так же, как это делает сбой PostgreSQL или Remnawave. Когда одновременно отказали несколько отслеживаемых зависимостей, источник записывается сначала как database, затем remnawave, затем valkey. База данных названа первой, потому что именно она объясняет остальные. Проба Remnawave — это аутентифицированный вызов, поэтому отозванный или истёкший токен панели считается нездоровой зависимостью и, после набора серии, приостановит покупки. Обычно это то поведение, которого вы и хотите, — выдача доступа всё равно бы провалилась, — но это значит, что небрежно проведённая смена токена закрывает магазин. См. Интеграция с Remnawave.

Как за этим следить

Каждый замер выставляет omniflow_maintenance_mode в 1 или 0 и omniflow_dependency_up по каждой пробе, и только в процессе API. Настройте оповещение на то, что датчик остаётся в 1 дольше запланированного окна: сигнал — это автоопределение, открывшее окно, которого вы не планировали, а инцидент — это то, что окно не закрывается. См. Наблюдаемость. Каждый переход также логируется на уровне warn:
Полоса инцидентов в панели на /admin читает строки maintenance_events, так что окно, которое открылось и закрылось ночью, утром всё ещё видно. Каждый переход — автоматический или ручной — также публикуется в топик incident операторской группы. Контроллер работает в процессе api, а группа принадлежит bot, поэтому api ставит уведомление в очередь operator_notifications, а бот его доставляет; если операторская группа не настроена, уведомление просто ждёт. См. Уведомления оператора.

Отказы и краевые случаи

  • Панель: отсутствующая причина или включение без обоих объявлений — это ошибка валидации, и переключатель не двигается.
  • Bearer: отсутствующий operatorId — это 400 missing_operator; отклонённое изменение состояния — 422 maintenance_rejected.
  • Покупка, предпринятая во время окна, падает с maintenance_active — 503 в клиентском веб-API, экран обслуживания в боте.
  • Строка maintenance_state создаётся при первом чтении, поэтому свежая установка сообщает «неактивен», а не «отсутствует».
  • Таблица обеспечивает CHECK (NOT active OR activated_at IS NOT NULL): активное окно всегда знает, когда оно началось.
  • Снятие окна ничего не отменяет, не возвращает и не просрочивает. Удержанные операции выдачи доступа просто снова становятся к исполнению.