Кратко
ifUnchangedByпроверяет только названные свойства объектов в начале метода. Превратить эту проверку в compare-and-swap всего объекта можно лишь с надёжным серверным токеном, меняющимся при каждом пути записи.atomic:trueотдельно защищает общий commit изменений одногоFoo/set. Ни один механизм не доказывает делегирование полномочий или завершение работы внешних систем.
Команда считала поле changed универсальной версией. Веб-клиент, синхронизатор и фоновая служба обновляли его при любой модификации. Затем оператор восстановил часть данных административным инструментом. Значения объекта изменились, а метка осталась прежней.
Позже клиент отправил условную запись: выполнить изменение, если changed всё ещё равно увиденному значению. Условие прошло. На уровне протокола сервер честно сравнил два одинаковых числа. На уровне утверждения «объект не менялся» доказательство оказалось ложным, потому что дисциплина токена была неполной.
Этот пример показывает центральную границу JMAP Conditional Set. Текущий draft-ietf-jmap-conditional-00 датирован 15 сентября 2026 года и истекает 19 марта 2027 года. На момент исследования это был активный Internet-Draft рабочей группы JMAP, предназначенный для Standards Track и обновляющий RFC 8620 в случае утверждения. Это не окончательный RFC и не свидетельство внедрения, совместимости или эксплуатационной распространённости.
Документ вводит ifUnchangedBy для условий на отдельных объектах и atomic:true для общего commit метода. Первое отвечает на вопрос, сохранились ли выбранные факты. Второе — можно ли применить предложенный набор изменений как одно целое. Сила обоих ответов ограничена их объявленной областью.
Почему состояние типа слишком широкое
В JMAP Core уже есть ifInState. Клиент передаёт строку состояния для целого типа объектов в аккаунте. Изменение любого объекта этого типа может сделать строку устаревшей. Новая почта, действие другого устройства или серверное изменение способны отклонить запись, хотя целевой объект и важные для решения поля остались прежними.
Такой барьер безопасен, но в активных аккаунтах порождает ложные конфликты. Приложения начинают повторять чтение или, хуже, убирают проверку. Более точная модель позволяет защищать именно то, от чего зависит решение.
ifUnchangedBy задаёт карту от ID объекта к PatchObject. Условие выполняется, если применение патча к текущему объекту ничего бы не изменило: каждый указатель уже имеет ожидаемое значение. JSON null утверждает отсутствие свойства. Сравнение использует представление, которое вернул бы Foo/get.
Клиент может проверить только blobId, отдельное право доступа или отсутствие $seen. Несвязанные свойства разрешено игнорировать. Это уменьшает число бесполезных отказов, но одновременно сужает доказательство.
Успех не означает, что весь объект был прежним. Он означает, что выбранные читаемые значения совпали в точке оценки до выполнения изменений метода. Если система аудита сохраняет лишь «версия подтверждена», она добавляет к ответу факты, которых сервер не проверял.
Полная версия существует только как операционная дисциплина
Проект показывает, что серверное поле, обновляемое при любой модификации, может восстановить compare-and-swap всего объекта. Клиент включает это поле в условие, и любое изменение должно привести к несовпадению.
Ключевое слово — «должно». Токен не становится полным оттого, что называется changed, version или generation. Нужно инвентаризировать все каналы: пользовательские методы, администрирование, миграции, восстановление, импорт, исправление повреждений и действия фоновых служб.
Один путь, не двигающий токен, создаёт невидимое изменение. Чем увереннее интерфейс называет проверку «полной версией», тем опаснее эта дыра. Поэтому управление токеном требует отрицательных тестов: измените объект каждым возможным писателем и убедитесь, что прежнее условие больше не проходит.
Условие может ссылаться и на другой объект того же типа, который метод не меняет. Например, обновление файла зависит от его прежнего родителя и имени каталога. Это позволяет включить ограниченный контекст, но не превращает пару полей в полную политику хранения, иерархию или полномочие на распоряжение активом.
Невидимое поле нельзя проверять как гипотезу
Разрешается опираться на серверные и доступные только для чтения свойства: идентификатор содержимого, размер, время изменения. Но клиент обязан иметь право их читать. Если права нет, сервер должен ответить forbidden, не оценивая условие.
Иначе успех или неуспех станет двоичным оракулом. Клиент сможет перебирать варианты скрытого значения. Отсутствие самого значения в ответе не устраняет утечку, если ответ сообщает истинность догадки.
Право чтения, в свою очередь, не даёт права записи. Все условия могут совпасть, а операция — законно завершиться ошибкой разрешения. Техническая запись от имени сервисного аккаунта также не доказывает, что организация делегировала этому сервису решение о коммерческом или юридическом последствии.
В терминах Heng Lu состояние — это свидетельство, а не мандат. Тонкий координационный слой полезен именно потому, что отвечает на узкий технический вопрос и не объявляет себя владельцем решения.
Несовпадение останавливает метод целиком
Все условия оцениваются по состоянию в начале метода, до его собственных созданий, обновлений и удалений. Если хотя бы одно условие не выполнено, сервер возвращает методный stateMismatch, не применяет ни одной мутации и не меняет строку состояния типа.
Он может перечислить ID неудачных объектов, но не обязан раскрывать их значения. Клиент перечитывает данные и решает, что делать дальше. Протокол не подменяет конфликт готовым решением.
Для независимых результатов клиент использует отдельные вызовы. Группировка означает общее судьбоносное условие; разделение допускает наблюдаемый частичный прогресс. Выбор требует знания последствий и принадлежит приложению.
Атомарность защищает переход, а не прошлое
atomic:true решает иной вопрос. В базовом JMAP элементы одного Foo/set могут завершаться независимо. С новой опцией сервер обязан применить все создания, обновления и удаления вместе либо не применить ничего.
Ограничения проверяются на результирующем наборе после всех изменений. Поэтому два уникальных имени можно обменять: каждая последовательная промежуточная операция конфликтует, а совместный итог остаётся уникальным.
Если одна часть не проходит из-за неправильного патча, прав, ограничения или иной SetError, сервер возвращает atomicFailure и ничего не commit-ит. Не перечисленные в необязательных картах объекты тоже не изменились; они лишь не вызвали отказ.
Ложное условие остаётся stateMismatch, даже при запросе атомарности. Так сохраняется разница между неверной исходной картиной и недопустимым общим переходом.
Если сервер не способен применить конкретный метод атомарно, он обязан вернуть cannotApplyAtomically без изменений. Нельзя молча перейти к частичному выполнению. Повтор без atomic допустим только после отдельного решения принять промежуточные состояния.
Такая повторная попытка меняет контракт. Она должна сохранить отклонённый запрос, автора ослабления, допустимые половинчатые состояния и план сверки. Иначе отказ в гарантии исчезнет из истории, а конечный успех будет выглядеть эквивалентным исходному намерению.
Успешный commit не равен внешней сходимости
Атомарный ответ относится к Foo/set отвечающего сервера. Поисковые индексы, уведомления, кэши, рабочие процессы, удалённые реплики и офлайн-клиенты не становятся автоматически частью той же транзакции.
После обмена имён кэш пути может показывать старую связь. После снятия доступа другой сервис может ещё принимать ранее выданный токен. После удаления сообщения локальная копия может оставаться видимой.
Надёжная цепочка разделяет аутентифицированного субъекта, делегирование решения, выбранные условия, ответ метода, чтение после commit-а, поколение у каждого потребителя и пользовательский результат. Первый зелёный ответ не должен свидетельствовать за тех, кого он не наблюдает.
Проверка running code должна ломать предположения. Измените невыбранное поле и убедитесь, что узкое условие остаётся истинным. Измените выбранное поле и подтвердите полное отсутствие мутаций. Попытайтесь проверить скрытое свойство и потребуйте forbidden. Сломайте один элемент атомарного метода и убедитесь, что остальные не появились. После успеха задержите внешних потребителей.
Метка изменения может представлять весь объект только тогда, когда каждый писатель подчиняется её правилу. Протокол даёт точный способ сравнения. Руководство обязано доказать, что сравниваемый символ действительно несёт заявленную историю.
Источники
- https://www.ietf.org/archive/id/draft-ietf-jmap-conditional-00.txt
- https://www.ietf.org/archive/id/draft-ietf-jmap-conditional-00.html
- https://www.ietf.org/archive/id/draft-ietf-jmap-conditional-00.xml
- https://datatracker.ietf.org/doc/draft-ietf-jmap-conditional/
- https://datatracker.ietf.org/doc/draft-ietf-jmap-conditional/history/
- https://datatracker.ietf.org/doc/draft-ietf-jmap-conditional/references/
- https://datatracker.ietf.org/api/v1/doc/document/draft-ietf-jmap-conditional/
- https://www.ietf.org/archive/id/draft-gondwana-jmap-conditional-00.txt
- https://www.ietf.org/archive/id/draft-ietf-jmap-filenode-14.txt
- https://www.rfc-editor.org/rfc/rfc8620.txt
- https://www.rfc-editor.org/rfc/rfc8620.html
- https://www.rfc-editor.org/rfc/rfc8620.json
- https://www.rfc-editor.org/rfc/rfc4918.txt
- https://www.rfc-editor.org/rfc/rfc9110.txt
- https://www.rfc-editor.org/rfc/rfc6902.txt
- https://www.rfc-editor.org/rfc/rfc8621.txt
Обзор для участников
Подробный контекст профиля
Войдите с подходящим уровнем подписки, чтобы открыть полный обзор и примечания к источникам.
Только для Стратегического сообщества
Стратегическое сообщество
Открыто всем читателям. Вступите и войдите, чтобы открыть обзоры профилей.
Вступить в Стратегическое сообществоТолько для Альянса лидеров
Альянс лидеров
Для проверенных владельцев IP-активов и руководителей. Войдите, чтобы открыть обзоры Альянса.
Вступить в Альянс лидеров
