Comments 34
Эх, побольше бы таких текстов, чтобы у публики на брейнстормах не было лиц суррикатов, когда произносишь слово «idempotency» вслух.
Одно замечание:
что вы решили обновить свой образ, заменив футболку
Это PATCH
, а не PUT
, и он не идемпотентен (и не безопасен). Если для формирования «образа» кроме футболки я надел на себя что-то еще — то PUT
должен принести замену всему, что на мне надето. Потому что если пока я развлекаюсь с мнимой идемпотентностью смены футболки, с меня стащат штаны — образ после второй смены футболки будет сильно отличаться. В общем, PUT
— это тот же POST
, только идемпотентный. Он обязан принести с собой слепок всего объекта, а если он приносит только некоторые части (футболку) — это PATCH
.
При желании нет никаких проблем сделать PATCH идемпотентным, нужно лишь подобрать соответствующий формат дельты.
Нет, конечно. Вы не сможете придумать такой формат дельты, который учтет все вызванные извне изменения, произошедшие с объектом между двумя вашими вызовами. Или это будет дельта, изоморфная всему объекту, что фактически означает «looks like PATCH
, walks like PATCH
, quacks like PATCH
— let’s call it PUT
».
Почему же?
Если говорить про json api, и оставить за скобками массивы (там и правда есть сложности), то достаточно выкинуть из схемы все неизменяемые и вычисляемые поля, а остальные сделать необязательными.
Да, подобная схема не позволит выражать изменения вида "удалил одну букву из значения некоторого свойства", но я не представляю кому вообще нужны такие подробные изменения.
DELETE идемпотентен
Какой эгоцентризм. Вы же не в транзакции работаете. Сосед мог напостить нового между двумя делитами.
И что? По спецификации DELETE
идемпотентен. Если ключи могут переиспользоваться, DELETE
по ключу просто противоречит спецификации. С переиспользуемыми ключами и PUT
не идемпотентен, представляете себе?
Результат окажется один и тот же независимо от того, постил сосед между делитами или нет. В этом и суть идемпотентного удаления.
Тогда всю статью переписывать надо. Потому что там утверждается, что второе применение не вызывает изменений.
Не вызывает изменений если идёт сразу после первого.
Выражаясь языком математики, f(f(x)) = f(x)
Это неверное определение, неясно откуда вы его взяли. Выражаясь языком математики, `f(g(f(x))) ≡ f(x), ∀g ∈ F(x) → F(x)`
Я-то своё определение взял из учебника математики, а вы свою чушь откуда взяли?
Господи, вы бы с реальностью и экспериментом как-нибудь соотносили ваши учебники.
Мы про определение в вебе? Поздравляю, по вашему определению и любой PATCH
идемпотентен. Про математику — надо смотреть как минимум определение на афинных полугруппах (потому что непрерывных параметров в вебе не бывает, печаль).
Эрудиция — прекрасная штука, но интеллект, как известно, не заменяет.
Определение в вебе соответствует математическому.
Поздравляю, по вашему определению и любой PATCH идемпотентен.
Неа, не любой. К примеру, стандартный diff для текстовых файлов неидемпотентен.
Про математику — надо смотреть как минимум определение на афинных полугруппах (потому что непрерывных параметров в вебе не бывает, печаль).
А где в формуле f(f(x)) = f(x) хотя бы намёк на непрерывный параметр?
стандартный diff для текстовых файлов
Стандартный кто? PATCH
в вебе — описан в RFC 5789. У стандартного diff, как минимум, два аргумента, он вообще из другой оперы.
Вы 55 минут назад сказали глупость, и теперь извиваетесь ужом, приводя нерелевантные примеры из курсов кройки и шитья (и математики), вместо того, чтобы просто согласиться с тем, что ошиблись.
PATCH в вебе — описан в RFC 5789
Формат патча в RFC 5789 не описан, он определяется реализацией (а также Content-Type), и может быть любым. В том числе тем, что используется в утилитах diff и patch.
У стандартного diff, как минимум, два аргумента, он вообще из другой оперы.
Очевидно, я говорил не об утилите, а о формате, в котором она выводит различия. Если бы вы сосредоточились на попытках меня понять, а не оскорбить -= вы бы это поняли.
Если бы вы сосредоточились на попытках меня понять, а не оскорбить
Вы себе льстите, я не пытаюсь никого никогда оскорбить, за меня природа отлично справляется.
Формат патча в RFC 5789 не описан
Очередной выверт ужа на сковородке, хотя это ж хабр, да, чего я ждал. В спецификации напрямую написано то, что PATCH
не идемпотентен (и также написано, что его можно сделать идемпотентным, да, хотя конкретная имплементация всегда облажается потому, что полная синхронизация сервера и клиента требует таких мощностей, транзакций и отсутствия моральных ценностей, что ее можно считать пренебрежимым частным случаем). Хоть сто велосипедов нагородите поверх, спецификацию это не изменит (но людей запутает, да). Все эти етаги, нонсы, диффы — поверх патча — уничтожают сам смысл существования такового запроса.
Ну и чтобы вам понятнее было. Когда я говорил «по вашему определению и любой PATCH
идемпотентен» — мне надо было сказать «по вашему определению гигантское количество патч-запросов, которые все адекватные люди (и консорциум) назовут неидемпотентными — оказываются идемпотентными».
Но я отвык от людей, отстаивающих любое свое слово любой ценой, поэтому не был готов к завороту про диффы. Мог и догадаться предвидеть, да.
А где в формуле f(f(x)) = f(x) хотя бы намёк на непрерывный параметр?
Да, а вот тут погорячился я.
Про методы GET, POST, PUT, DELETE довольно странные мысли. Любой из методов можно сделать идемпотентным или не идемпотентным. Метод - это лишь вариант интерфейса.
Не так давно я работал с API банка, у которого вообще все было сделано через GET, причем параметры через url. И добавление заказов, и удаление, и запрос статуса... все-все-все :)
Можно, но тут речь про спецификацию, а не про то, как её можно извратить.
Видимо, да. Но об этом остается только догадываться. Если бы я не был немного в теме, я бы не понял того, что речь о спецификации этих методов. Этого не понятно ни по названию, ни по содержанию статьи. Ну и не помешало бы разъяснение, что это лишь рекомендации, которые соблюдаются далеко не всегда. Я бы даже сказал, что редко соблюдаются...
Важная часть, которую надо бы вынести большими буквами для новичков в начало статьи - методы PUT и DELETE ДОЛЖНЫ быть идемпотентный в соответствии с RFC, но в реальной жизни всегда проверяйте, что нашкодили программисты, потому что никто этого не гарантирует. По сути это все го лишь слова для разграничения разных методов, а что они делают зависит от кода, и GET с POST могут использоваться для удаления записей.
И, кстати, да, по RFC идемпотентными являются в явном виде только PUT и DELETE, GET не обязан быть таковым.
Метод без побочных эффектов всегда идемпотентен.
Неа, не любой [
PATCH
идемпотентен]. К примеру, стандартный diff для текстовых файлов неидемпотентен.
ваши слова?
Метод без побочных эффектов всегда идемпотентен.
тоже ваши?
Простейший силлогизм: diff
имеет побочные эффекты. Внимание, вопрос: какие именно?
Применение патча формата diff к файлу, очевидно, меняет этот самый файл. Это и есть побочный эффект с точки зрения стандарта HTTP.
Изменение сущности на которую указывает идентификатор ресурса при вызове небезопасного метода — побочный эффект с точки зрения стандарта HTTP?
Я сдаюсь. Искренне завидую, если вам удается зарабатывать деньги не астрологией и не финансовыми махинациями.
А что не так-то? Основной результат запроса - это ответ сервера. Всё остальное - побочные эффекты.
И, если что, "метод без побочных эффектов всегда идемпотентен" написано прямо в стандарте:
A sequence that never has side effects is idempotent, by definition
-- https://datatracker.ietf.org/doc/html/rfc2616#section-9.1.2
(одиночный запрос является тривиальной разновидностью последовательности запросов, если вы решите ещё и тут придраться)
О, пошло в ход вырывание слов из контекста. Полная цитата, а не те три слова, которые вам выгодны, звучит так:
A sequence that never has side effects is idempotent, by definition (provided that no concurrent operations are being executed on the same set of resources).
Когда для человека веб — однопоточная неконкурентная среда — и не такие аберрации сознания, конечно, происходят. На всякий случай уточню: я понял, что вы имеете в виду одиночный запрос (по определению неконкурентный), и что для вас
Основной результат запроса - это ответ сервера. Всё остальное - побочные эффекты.
Попробую в последний (клянусь) раз: запрос, который по идентификатору ресурса меняет значение числового поля этого ресурса на модуль его значения. Функция взятия модуля — идемпотентна (в вашей этой математике, если я правильно помню). Условию `f(f(x)) ≡ f(x)` удовлетворяет. А запрос, гад, неидемпотентен. Потому что если между двумя такими патчами прилетит запрос, меняющий это значение на его квадратный корень, — результаты окажутся разными.
Ссылку на то, что изменение ресурса по идентификатору не является по спецификации сайд-эффектом мне искать лень, да и вряд ли ее модно вообще найти, настолько это всем очевидно.
Теперь сходите перечитайте мой первый комментарий, про который вы вежливо (и абсолютно неверно) сказали «чушь», и поблагодарите за разъяснения, да вот хотя бы за то, что я потратил чуть не час, разжевывая прописные истины человеку, который не желает (не умеет?) слушать и понимать очевидные подавляющему большинству вещи.
Могу инвайт выслать — сможете мне еще раз поднасрать в карму с другого аккаунта.
А правильный ответ: идемпотентность зависит от того какими кривыми руками спроектировано АПИ. Я так понимаю тут речь про канонический REST. В реале довольно часто встречается следующее: [POST] "/object-type/:id" для обновления объекта, а вот если вызвать без параметра в урле, то будет создан новый объект.
Идемпотентность: искусство не менять мир дважды