Кратко

  • В RFC 1045 февраля 1988 года Дэвид Черитон описал VMTP для обмена запросами и ответами, в том числе удалённых вызовов процедур. Client, 32-битный номер Transaction и счётчик пересылок определяли транзакцию; серверное состояние помогало отсекать дубликаты.
  • Если ответ отмечен как допускающий безопасное повторение, клиент заново посылает запрос, а сервер повторяет работу и создаёт свежий ответ. Для результата, который надо сохранить, сервер держит копию или ждёт подтверждения. Новое чтение часов лучше старой записки о времени.

Повторить отправку легко на бумаге. Труднее понять, успел ли сервер выполнить первое поручение. Именно этот вопрос разбирал RFC 1045, спецификация Versatile Message Transaction Protocol Дэвида Черитона. Протокол предназначался для запросов и ответов поверх ненадёжной доставки дейтаграмм, где пакет мог исчезнуть. Среди его задач были удалённые вызовы процедур. При пропаже ответа клиент видел тишину, а сервер мог уже закончить работу. Новый запрос приносил не только повторный пакет, но и спор о судьбе первой операции.

VMTP выдавал операции составной номер. В пределах домена сущностей её определяли Client, Transaction и ForwardCount. 32-битный номер Transaction начинался со случайного значения при создании клиента и обычно увеличивался после каждой сделки. Четырёхбитный ForwardCount оставался нулём, пока запрос не пересылали другому серверу. Сервер хранил запись состояния клиента и узнавал повтор с теми же координатами. Приёмная могла сказать: «Этот посетитель уже был», прежде чем снова звать исполнителя.

Запись держали и после отправки ответа: старая копия запроса могла приплестись по сети позже. Когда записи нет, спецификация предлагает несколько способов решить вопрос. Можно запросить текущий номер у управляющего модуля клиента через Probe. Можно признать запрос новым, если записи сохранялись дольше предельной жизни пакета и времени повторов, а сервер за это время не перезагружался. Можно безопасно повторить заведомо идемпотентную операцию. За одним нажатием «повторить» скрыта проверка памяти, часов сети и свойств самой работы.

Когда ответ объявлен идемпотентным, VMTP выбирает повторную работу. Он не сохраняет прежний ответ. Клиент вновь посылает Request, сервер передаёт приложению запрос последней транзакции, и приложение создаёт новый ответ. Более старые задержавшиеся запросы отбрасываются. Черитон объясняет механизм вопросом о текущем времени: ответ из старой карточки сообщает уже прошедшую минуту; взгляд на часы сейчас сообщает нынешнюю. Аккуратный архивариус здесь проигрывает тому, кто не поленился поднять голову.

У результата, который нельзя так обновить, другой тариф. Модуль обязан держать копию ответа — обычную или с копированием при записи — либо задержать сервер до подтверждения. Оплата производится памятью или ожиданием. Время можно спросить ещё раз. Изменение счёта или записи при повторе может произойти дважды. Метка в заголовке должна отражать смысл операции, а не желание программиста освободить пару ячеек памяти.

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

История с часами смешная ровно до момента, когда вместо времени повторно меняют реальный объект. RFC 1045 оставил ясное распределение ролей. Протокол хранит координаты и отсекает устаревшие копии; приложение знает, можно ли безопасно выполнить поручение снова. Потерянный ответ заставляет кого-то платить — памятью, временем или повторным действием. У часов счёт скромный. У других операций он может оказаться совсем другим.