Глава 9. Обработка ошибок

9.1 Модель обработки ошибок

Реализация DTP MUST следовать трёхэтапной модели обработки ошибок «обнаружить-уведомить-восстановить»:

  1. Обнаружить: выявить аномальные условия.
  2. Уведомить: отправить информацию об ошибке пиру или верхнему уровню.
  3. Восстановить: предпринять действия по восстановлению в соответствии с типом ошибки.

Реализация MUST NOT молча отбрасывать фреймы при обнаружении ошибки без уведомления.

9.2 Система кодов ошибок

DTP MUST использовать следующую систему кодов ошибок. Коды ошибок MUST быть неотрицательными целыми числами, разделёнными на восемь диапазонов по функциональным модулям:

ДиапазонКатегорияСтратегия обработки
1xxxОшибки обработки фреймовОтбросить фрейм + уведомить отправителя
2xxxОшибки шифрованияОтбросить фрейм + уведомить отправителя + может инициировать повторное согласование ключей
3xxxОшибки AgreementОтбросить Fragment + уведомить отправителя + может инициировать повторные переговоры
4xxxОшибки DAGОтклонить Fragment + уведомить отправителя или закэшировать и ждать
5xxxОшибки SessionПопытаться восстановить Session + закрыть и уведомить верхний уровень при неудаче
6xxxОшибки возобновленияПриостановить отправку + уведомить приложение верхнего уровня
7xxxОшибки версииОтправить уведомление о несовместимости версий + попытаться понизить версию
8xxxОшибки прав доступаОтклонить операцию + уведомить запрашивающего

9.3 Определения кодов ошибок

Реализация MUST использовать следующие нормативные определения кодов ошибок. MUST NOT использовать неуказанные коды ошибок. Любое расширение MUST определяться в последующей версии настоящей спецификации.

9.3.1 Ошибки обработки фреймов (1xxx)

КодИмяУсловие срабатывания
1001FRAME_DESERIALIZATION_FAILEDПолученные бинарные данные не могут быть десериализованы в LogicalFrame
1002FRAME_INVALID_FORMATСтруктура LogicalFrame некорректна или отсутствуют обязательные поля

9.3.2 Ошибки шифрования (2xxx)

КодИмяУсловие срабатывания
2001DECRYPTION_FAILEDРасшифровка Payload не удалась (неверный ключ или повреждённые данные)
2002KEY_NOT_READYОбмен ключами CAP ещё не завершён; передача данных отклонена

9.3.3 Ошибки Agreement (3xxx)

КодИмяУсловие срабатывания
3001AGREEMENT_NOT_FOUNDFragment ссылается на неизвестный Agreement_ID
3002AGREEMENT_EXPIREDУказанный Agreement истёк (превышен validityPeriod)
3003AGREEMENT_NEGOTIATION_FAILEDПереговоры не могут завершиться (тайм-аут, отказ пира от повторных переговоров и т.п.)

9.3.4 Ошибки DAG (4xxx)

КодИмяУсловие срабатывания
4001DAG_CYCLE_DETECTEDДобавление Fragment приведёт к образованию цикла в DAG
4002DAG_DEPENDENCY_UNRESOLVEDЗависимости Fragment не разрешены в пределах тайм-аута кэширования

9.3.5 Ошибки Session (5xxx)

КодИмяУсловие срабатывания
5001SESSION_NOT_FOUNDУказанный Session_ID не существует
5002SESSION_TIMEOUTSession истекла по тайм-ауту из-за бездействия
5003SESSION_RESTORE_FAILEDВосстановление Session после переподключения не удалось

9.3.6 Ошибки возобновления (6xxx)

КодИмяУсловие срабатывания
6001BUFFER_FULLКэш неподтверждённых Fragment отправителя достиг ёмкости
6002RETRANSMISSION_TIMEOUTТайм-аут повторной передачи Fragment без подтверждения (число попыток исчерпано)

9.3.7 Ошибки версии (7xxx)

КодИмяУсловие срабатывания
7001VERSION_INCOMPATIBLEВерсия протокола полученного фрейма выше, чем поддерживаемая данной реализацией

9.3.8 Ошибки прав доступа (8xxx)

КодИмяУсловие срабатывания
8001PERMISSION_DENIEDТекущей роли не разрешено выполнять данную операцию
8002OBSERVER_WRITE_DENIEDObserver попытался выполнить операцию записи (Observer работают только на чтение)

9.4 Уникальность кодов ошибок

Реализация MUST гарантировать уникальность кодов ошибок:

  1. Каждый код ошибки MUST соответствовать уникальному типу ошибки.
  2. Различные типы ошибок MUST NOT разделять один и тот же код ошибки.
  3. Реализация MUST NOT переопределять коды ошибок, уже назначенные настоящей спецификацией.

9.5 Механизм уведомлений об ошибках

9.5.1 ErrorNotificationFrame

Уведомления об ошибках MUST передаваться через ControlFrame, определённый следующим образом:

interface ErrorNotificationFrame {
  frameType: "control";
  controlType: "error_notification";
  errorCode: DTPErrorCode;
  errorMessage: string;
  relatedFrameId?: FragmentID;
  relatedAgreementId?: AgreementID;
  details?: Record<string, unknown>;
}
ПолеНормативное требование
frameTypeMUST быть "control"
controlTypeMUST быть "error_notification"
errorCodeMUST быть одним из кодов ошибок, определённых в разделе 9.3
errorMessageMUST быть человекочитаемым описанием ошибки. SHOULD использовать язык, понятный пиру
relatedFrameIdOPTIONAL. Когда ошибка вызвана конкретным фреймом, MUST содержать fragmentId этого фрейма
relatedAgreementIdOPTIONAL. Когда ошибка связана с конкретным Agreement, MUST содержать Agreement_ID
detailsOPTIONAL. MAY содержать дополнительную информацию для отладки

9.5.2 Транспорт уведомлений об ошибках

ErrorNotificationFrame MUST передаваться по обычному каналу LogicalFrame, и его Payload MUST быть зашифрован.

Если сама ошибка препятствует шифрованию (например, ошибка KEY_NOT_READY), реализация MAY возвращать ошибку через определяемый реализацией внеполосный канал, но MUST NOT отправлять ошибку в открытом виде по зашифрованному каналу.

9.6 Ключевые рабочие процессы обработки ошибок

9.6.1 Сбой десериализации (1001)

Когда полученный LogicalFrame не может быть десериализован, получатель MUST:

  1. Отбросить фрейм.
  2. Отправить уведомление об ошибке FRAME_DESERIALIZATION_FAILED.
  3. MUST NOT закрывать Session из-за сбоя десериализации.

9.6.2 Сбой расшифровки (2001)

Когда полученный Payload не может быть расшифрован, получатель MUST:

  1. Отбросить фрейм.
  2. Отправить уведомление об ошибке DECRYPTION_FAILED.
  3. Подсчитывать число последовательных сбоев расшифровки.
  4. Если последовательные сбои превышают пороговое значение (RECOMMENDED значение 3), SHOULD инициировать повторное согласование ключей через CAP.

9.6.3 Обнаружение цикла DAG (4001)

При обнаружении цикла DAG:

  1. Получатель MUST отклонить Fragment.
  2. MUST вернуть ошибку DAG_CYCLE_DETECTED.
  3. MUST NOT добавлять Fragment в DAG.
  4. MUST NOT влиять на состояние существующих Fragment.

9.6.4 Неизвестный Agreement (3001)

Когда Fragment ссылается на неизвестный Agreement_ID:

  1. Получатель MUST отбросить Fragment.
  2. Вернуть ошибку AGREEMENT_NOT_FOUND.
  3. MAY инициировать повторные переговоры (определяется реализацией).

9.6.5 Ключ не готов (2002)

Когда передача данных предпринимается до завершения обмена ключами CAP:

  1. DTP_Engine MUST отказать в отправке.
  2. MUST вернуть ошибку KEY_NOT_READY вызывающему верхнего уровня.
  3. MUST NOT отвечать в открытом виде по каналу связи.

9.6.6 Переполнение буфера (6001)

При достижении кэшем неподтверждённых Fragment верхней границы:

  1. Отправитель MUST приостановить отправку новых Fragment.
  2. Отправить уведомление BUFFER_FULL приложению верхнего уровня.
  3. Возобновить отправку после освобождения места в кэше за счёт подтверждений.
  4. MUST NOT отбрасывать уже закэшированные Fragment.

9.6.7 Тайм-аут повторной передачи (6002)

При исчерпании числа повторных передач Fragment:

  1. Отправитель MUST уведомить приложение верхнего уровня об ошибке RETRANSMISSION_TIMEOUT.
  2. SHOULD инициировать приостановку или прекращение Session.
  3. MUST NOT повторно передавать бесконечно.

9.6.8 Несовместимость версий (7001)

При получении фрейма с несовместимой версией протокола (см. подробности в главе 10):

  1. Получатель MUST не обрабатывать фрейм.
  2. MUST вернуть ошибку VERSION_INCOMPATIBLE.
  3. MUST включить наибольшую поддерживаемую им версию в поле details ошибки.

9.6.9 Отказ в правах доступа (8001, 8002)

Для незаконных операций ролей:

  1. DTP_Engine MUST отклонить операцию.
  2. Операции записи Observer MUST возвращать OBSERVER_WRITE_DENIED (8002).
  3. Другие отказы в правах доступа MUST возвращать PERMISSION_DENIED (8001).

9.7 Стратегия восстановления при ошибках

Реализация MUST применять соответствующую стратегию восстановления по типу ошибки:

Категория ошибкиСтратегия восстановления
1xxx Обработка фреймовОтбросить фрейм, записать в журнал, продолжить приём последующих фреймов
2xxx ШифрованиеЕдиничный сбой: отбросить фрейм; последовательные сбои: инициировать повторное согласование ключей
3xxx AgreementОтбросить Fragment; может инициировать повторные переговоры
4xxx DAGЦикл: отклонить; не разрешено: закэшировать и ждать
5xxx SessionПопытаться восстановить Session; закрыть при неудаче
6xxx ВозобновлениеПриостановить отправку, ждать ответа пира или вмешательства верхнего уровня
7xxx ВерсияОтправить уведомление о версии; попытаться понизить версию
8xxx Права доступаОтклонить операцию; MUST NOT автоматически повторять

Реализация MUST NOT автоматически повторять одну и ту же операцию при возникновении ошибки прав доступа (8xxx).