Документация

Окна обслуживания

Версия v2.3.1 · Обновлено

Окна обслуживания#

Окна обслуживания определяют планируемые периоды, когда IncidentRelay должен изменить обработку совпадающих алертов. Окно может быть нацелено на группу, команду, сервис или маршрут и может либо подавлять уведомления, либо подавлять создание инцидентов, либо создавать инцидент обслуживания, либо приостанавливать эскалации.

Понятия#

Окно обслуживания сопоставляется с входящими алертами после маршрутизации и разрешения сервиса. Это означает, что маршрут, команда и сервис уже известны до того, как IncidentRelay решает, применяется ли окно обслуживания.

Окна обслуживания хранятся отдельно от инцидентов, но информация о сопоставлении прикрепляется к созданной группе алертов и дочерним алертам через поля обслуживания. В модели данных IncidentRelay AlertGroup является моделью хранения инцидентов.

Область действия#

Каждое окно обслуживания должно иметь хотя бы одну область действия.

Поддерживаемые типы областей:

ОбластьОписание
groupПрименяется ко всем командам, сервисам, маршрутам и алертам в рамках группы.
teamПрименяется к выбранной команде.
serviceПрименяется к выбранному сервису.
routeПрименяется к выбранному маршруту алертов.

Если одному и тому же алерту соответствует несколько активных окон, репозиторий возвращает первым самое новое совпадающее активное окно, упорядоченное по starts_at, а затем по id.

Виды поведения#

Поведение обслуживания управляет тем, что происходит, когда совпадающий алерт приходит во время активного окна.

ПоведениеИнцидент созданСтатус инцидентаУведомленияЭскалацияmaintenance_suppressed
suppress_notificationsДаСтатус входящего алертаПодавленыПриостановленаtrue
suppress_incidentНетНеприменимоНеприменимоНеприменимоНеприменимо
create_maintenance_incidentДаmaintenanceНе подавляются флагом обслуживанияОбычная, если не настроено иноеfalse
pause_escalation_onlyДаСтатус входящего алертаОбычныеПриостановленаfalse

suppress_notifications#

Создаёт группу алертов и дочерний алерт, прикрепляет окно обслуживания и приостанавливает весь жизненный цикл уведомлений, пока окно активно. Сюда входят:

  • первое уведомление;
  • обновления группы в очереди;
  • напоминания и повторные уведомления;
  • отложенная доставка на уровне пользователя;
  • последующие шаги эскалации и уведомления об эскалации.

Уже доставленные уведомления остаются в истории и не отзываются. Пропущенные напоминания и шаги эскалации не воспроизводятся после обслуживания.

Когда период обслуживания заканчивается, для всё ещё активной группы алертов планируется одно обычное уведомление или обновление. Счётчик напоминаний сбрасывается, а отсчёт эскалации по текущему правилу возобновляется с момента завершения обслуживания.

Используйте это, когда алерты должны оставаться видимыми в IncidentRelay, но ответственных не следует оповещать во время планируемых работ.

suppress_incident#

Не создаёт новую группу алертов для нового совпадающего алерта.

Если существующий алерт или группа уже существуют, поток дедупликации/обновления может продолжаться. Это позволяет избежать нарушения существующего поведения жизненного цикла инцидента, одновременно предотвращая создание новых инцидентов во время планового обслуживания.

Используйте это, когда совпадающие алерты ожидаемы и не должны отображаться как инциденты.

create_maintenance_incident#

Создаёт инцидент со статусом maintenance и прикрепляет окно обслуживания.

Это не устанавливает maintenance_suppressed=true. Флаг maintenance_suppressed означает только подавление уведомлений, вызванное suppress_notifications.

Используйте это, когда алерты обслуживания должны явно отслеживаться как инциденты.

pause_escalation_only#

Создаёт группу алертов и дочерний алерт, прикрепляет окно обслуживания и устанавливает следующее время эскалации в None.

Используйте это, когда инцидент должен быть видимым и уведомления по-прежнему могут отправляться, но эскалация не должна продвигаться, пока окно обслуживания активно.

Поведение часового пояса#

starts_at и ends_at — это значения даты и времени по «настенным часам» в выбранном часовом поясе окна обслуживания. Они не должны преобразовываться в UTC фронтендом перед отправкой в API.

Пример полезной нагрузки для окна обслуживания по Москве:

{
  "starts_at": "2026-06-07T07:52:00",
  "ends_at": "2026-06-07T11:37:00",
  "timezone": "Europe/Moscow"
}

IncidentRelay интерпретирует это как с 07:52 до 11:37 в Europe/Moscow, а не в UTC.

Эффективный статус вычисляется динамически на основе:

  • starts_at
  • ends_at
  • timezone
  • rrule
  • status
  • enabled
  • deleted

Сохранённый status может оставаться scheduled, в то время как API возвращает эффективный status как active или finished в зависимости от текущего времени.

Эффективный статус#

API возвращает как сохранённый, так и эффективный статус:

{
  "status": "active",
  "stored_status": "scheduled"
}

status — это эффективный статус, и он должен использоваться в UI.

stored_status — это значение из базы данных, полезное для отладки или аудита.

Правила расчёта статуса:

УсловиеЭффективный статус
status = cancelledcancelled
enabled = falseНе считается активным для сопоставления
Текущее локальное время раньше starts_atscheduled
Текущее локальное время между starts_at и ends_atactive
Текущее локальное время после ends_atfinished
У RRULE есть текущее вхождениеactive
У RRULE есть будущее вхождениеscheduled
У RRULE нет будущего вхожденияfinished

Повторяющиеся окна#

Окна обслуживания поддерживают опциональные строки RFC5545 RRULE в поле rrule.

RRULE хранится без префикса RRULE:. Если API получает префикс, он нормализует его перед сохранением.

Примеры:

ПовторениеRRULE
Ежедневно, 3 вхожденияFREQ=DAILY;COUNT=3
Еженедельно, 5 вхожденийFREQ=WEEKLY;COUNT=5
Ежемесячно, 2 вхожденияFREQ=MONTHLY;COUNT=2
Каждый понедельникFREQ=WEEKLY;BYDAY=MO

Первое вхождение начинается в starts_at. Каждое вхождение использует одинаковую длительность, вычисленную как ends_at - starts_at.

Для повторяющихся окон API может возвращать текущее или следующее вхождение:

{
  "occurrence": {
    "status": "active",
    "starts_at": "2026-06-07T07:00:00",
    "ends_at": "2026-06-07T08:00:00",
    "timezone": "Europe/Moscow",
    "recurring": true
  }
}

UI должен отображать времена вхождений для повторяющихся окон, а не только базовые starts_at и ends_at.

Сопоставление алертов и инцидентов#

Сопоставление обслуживания происходит в upsert_alert() после того, как разрешены маршрут, команда, сервис и ротация.

Решение о сопоставлении основано на:

  • id группы
  • id команды
  • id сервиса
  • id маршрута
  • текущем времени в часовом поясе окна обслуживания
  • полях enabled/deleted/status

Для сервисов и маршрутов active_maintenance является динамическим. Если сервис или маршрут в данный момент находится на обслуживании, сериализатор может вернуть активное обслуживание, даже если во время окна не было создано ни одного алерта.

Для алертов и инцидентов active_maintenance является прикреплённым состоянием. Существующие группы алертов не должны помечаться как обслуживание задним числом только потому, что позже стало активным новое окно обслуживания команды/сервиса/маршрута.

Правильное поведение:

ОбъектИсточник active_maintenance
СервисДинамический поиск по текущему активному окну обслуживания
МаршрутДинамический поиск по текущему активному окну обслуживания
Группа алертов / инцидентПрикреплённый maintenance_window_id в группе алертов
Дочерний алертПрикреплённый maintenance_window_id в алерте

API#

Список окон обслуживания#

GET /api/maintenance-windows?include_finished=1

Ответ:

{
  "items": [
    {
      "id": 12,
      "group_id": 1,
      "group_name": "Platform",
      "team_id": 2,
      "team_name": "SRE",
      "name": "Payments deploy",
      "description": "Planned deployment",
      "status": "active",
      "stored_status": "scheduled",
      "behavior": "suppress_notifications",
      "timezone": "Europe/Moscow",
      "rrule": null,
      "starts_at": "2026-06-07T07:52:00",
      "ends_at": "2026-06-07T11:37:00",
      "occurrence": {
        "status": "active",
        "starts_at": "2026-06-07T07:52:00",
        "ends_at": "2026-06-07T11:37:00",
        "timezone": "Europe/Moscow",
        "recurring": false
      },
      "enabled": true,
      "deleted": false,
      "scopes": [
        {
          "id": 33,
          "scope_type": "service",
          "service_id": 10,
          "service_name": "payments-api"
        }
      ]
    }
  ]
}

Создание окна обслуживания#

POST /api/maintenance-windows
Content-Type: application/json
{
  "name": "Payments deploy",
  "description": "Planned deployment for payments-api",
  "behavior": "suppress_notifications",
  "timezone": "Europe/Moscow",
  "starts_at": "2026-06-07T07:52:00",
  "ends_at": "2026-06-07T11:37:00",
  "enabled": true,
  "rrule": null,
  "scopes": [
    {
      "scope_type": "service",
      "service_id": 10
    }
  ]
}

Обновление окна обслуживания#

PUT /api/maintenance-windows/{window_id}
Content-Type: application/json

Поддерживаются частичные обновления. Если starts_at, ends_at, timezone, behavior или rrule опущены, текущие значения сохраняются.

Когда scopes опущен, существующие области действия сохраняются.

{
  "name": "Updated deploy",
  "description": "Deployment postponed"
}

Чтобы заменить области действия, отправьте scopes явно:

{
  "scopes": [
    {
      "scope_type": "team",
      "team_id": 2
    }
  ]
}

Отмена окна обслуживания#

POST /api/maintenance-windows/{window_id}/cancel
Content-Type: application/json
{
  "reason": "Deployment postponed"
}

Отмена устанавливает:

status = cancelled
enabled = false
deleted = false

Удаление окна обслуживания#

DELETE /api/maintenance-windows/{window_id}

Удаление является мягким удалением и устанавливает:

deleted = true
enabled = false

RBAC#

Окна обслуживания используют разрешения на запись для выбранной области действия.

ОбластьТребуемое разрешение
groupДоступ на запись к группе
teamДоступ на запись к команде
serviceДоступ на запись к команде сервиса
routeДоступ на запись к команде маршрута

При обновлении окна:

  • Если scopes передан, разрешения проверяются относительно новых областей действия.
  • Если scopes опущен, разрешения проверяются относительно существующего владельца окна или существующих областей действия.

Журнал аудита#

Успешные действия записи создают записи аудита:

ДействиеДействие аудита
Созданиеmaintenance_window.create
Обновлениеmaintenance_window.update
Отменаmaintenance_window.cancel
Удалениеmaintenance_window.delete

Данные аудита включают финальное состояние окна обслуживания и полезную нагрузку запроса.

Поведение UI#

Страница окон обслуживания должна показывать:

  • сводные плитки
  • представление списком/таблицей
  • бейдж статуса
  • поведение
  • значение повторения
  • текущее или следующее вхождение
  • начало/окончание в выбранном часовом поясе
  • панель деталей
  • модальное окно создания/редактирования

Выбор часового пояса должен повторно использовать общий помощник AppTimezones, используемый слоями ротации.

Фронтенд должен отправлять значения datetime-local как обычные строки по «настенным часам» через AppTimezones.normalizeDatetimeLocal(). Он не должен вызывать new Date(value).toISOString() для окон обслуживания.

Бейджи обслуживания#

Сервисы и маршруты могут динамически показывать бейджи активного обслуживания.

Группы алертов и инциденты должны показывать бейджи только тогда, когда обслуживание было прикреплено на момент обработки алерта.

Текст бейджа должен включать:

  • имя окна обслуживания
  • метку поведения
  • время текущего или следующего вхождения
  • часовой пояс

Пример подсказки:

Payments deploy · Suppress notifications · 07/06 07:52 — 07/06 11:37 Europe/Moscow

Покрытие тестами#

Тесты окон обслуживания должны покрывать:

  • создание/обновление/отмену/удаление
  • записи аудита
  • RBAC для областей service/team/route/group
  • статус активности с учётом часового пояса
  • повторяющиеся окна RRULE
  • расчёт вхождений
  • динамические бейджи активного обслуживания сервисов и маршрутов
  • прикреплённые бейджи обслуживания групп алертов
  • отсутствие пометки старых групп алертов задним числом
  • все виды поведения обслуживания

Матрица поведения, которую нужно держать покрытой:

suppress_notifications:
  incident is created
  maintenance_suppressed = true
  notification queue is cleared
  reminders and user-level delayed deliveries are suppressed
  escalation is paused
  after maintenance, one normal notification/update is scheduled
  missed reminders and escalation steps are not replayed

suppress_incident:
  new incident is not created
  upsert_alert returns (None, False)

create_maintenance_incident:
  incident is created
  status = maintenance
  maintenance_suppressed = false

pause_escalation_only:
  incident is created
  next_escalation_at = None
  maintenance_suppressed = false

Ретроактивная повторная проверка#

У каждого окна обслуживания есть две независимые настройки жизненного цикла:

  • Apply to existing unresolved alerts по умолчанию отключена. Если её включить, активное окно немедленно начинает влиять на совпадающие неразрешённые группы алертов, которые существовали до начала периода обслуживания. Настройка недоступна для Suppress incident, поскольку уже созданную группу нельзя ретроактивно сделать несуществующей.
  • Reactivate affected alerts when this maintenance ends по умолчанию включена. Если её отключить, применённый эффект сохранится после завершения, отключения или отмены окна. Чтобы снять сохранённые эффекты, включите настройку позднее и сохраните окно. Удаление окна, исключение группы из его области действия или переход к несовместимому виду поведения всегда снимает эффекты, которые больше не относятся к окну.

Явные изменения через API согласуются немедленно. Задание планировщика обрабатывает запланированные начала, истечение и повторяющиеся периоды. Обработка идемпотентна, а перекрывающиеся окна отслеживаются независимо. Группа возобновляет работу только после прекращения последнего активного или сохранённого эффекта. Пропущенные напоминания и шаги эскалации не воспроизводятся.