You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TLDR:
Мой личный опыт работы с Optimistic concurrency control не позволил мне понять идею из книги, что проверять ревизию разделяемого ресурса необходимо не только при изменении заказа, но и при создании, т.к. операция создания не может быть идемпотентной.
Я привык, что "разделяемый ресурс" это конкретный заказ и версия требуется при изменении заказа, например:
val orderToUpdate = getOrderById(id)
val updated = orderToUpdate.changeSomeState()
// throws OptimisticLockException
saveOrder(updated, orderToUpdate.version)
Однако если нам требуется предотвратить создание одного и того же заказа дважды, то разделяемым ресурсом должен быть не конкретный заказ, а коллекция / массив всех заказов.
Имейл
--->
Подскажите, в чем заключается суть оптимистической блокировки в случае создания заказа? Ведь заказа еще не существует…
Я понимаю вариант с пессимистичной блокировкой / lock’ом, когда клиент выделяет себе эксклюзивное право на создание заказов.
Прикладываю скриншот - какой смысл обращаться к orderState.version, если заказа еще в принципе не существует?
Мне понятно, зачем OCC при изменении сущности, но ведь тут идет создание. Или в данном случае речь о том, что версионируется целая коллекция заказов, что-то типа serializable уровня изоляции? (если кто-то создал свой заказ между моим чтением и записью в коллекцию заказов, то моя запись закончится конфликтом)
<---
Добрый день, Илья.
Рад, что книга оказалась полезной; я вложил в неё очень много времени и сил.
В случае с созданием заказа проблема, которую мы решаем — не допустить создания повторного заказа по ошибке. Рассмотрим два сценария:
Клиент заказывает два такси из точки А в точку Б — допустим, ему нужно перевезти 8 человек
Клиент сделал заказ из точки А в точку Б и зашёл в лифт; у него пропал интернет и он пытается сделать заказ снова, полагая, что его первый запрос не прошёл (так как он не видел ответа).
Проблема с операцией создания заказа в том, что она нативно неидемпотентна. Оба кейса (1) и (2) вполне возможны; мы не можем технически отличить настоящее создание заказа от ошибочного. Единственное, что мы можем гарантировать — это что клиент ВИДЕЛ, что у него уже есть один заказ, когда он создавал второй.
Именно эту проблему мы и решаем, включая ревизию заказа / версию эндпойнта в запрос — что клиент получил и просмотрел самое свежее состояние разделяемого ресурса. Для этого как раз нужно ВСЕГДА запрашивать актуальное состояние, ДАЖЕ если клиент уверен, что никакого заказа у него ПОКА нет. (На самом деле, если есть строгая проверка ревизии на сервере, то необязательно — просто UX будет более удобным, без показа лишних ошибок.)
--->
Кажется я все понял - получается, что разделяемый ресурс в Вашем примере это коллекция всех заказов такси конкретного клиента, а не один конкретный заказ. Клиенту при создании заказа необходимо указать последнюю версию этого разделяемого ресурса, которую он «видел». Т.е. при создании первого заказа надо отправить версию, которая по смыслу значит «видел, что заказов нет». А при создании второго заказа версию «видел, что уже есть один заказ».
Полагаю, что максимально широкий скоуп разделяемого ресурса это все состояние системы целиком (но тогда никакой параллелизм не будет возможен, обычно в этом нет смысла), а минимальный - это «коллекция всех заказов конкретного клиента», как раз из-за того, что создание не может быть идемпотентным.
<---
Да, примерно так. Ниже в той же главе есть примерно такое же пояснение:
«NB. Выбор ресурса, версию которого мы требуем передать для получения доступа, очень важен. Если в нашем примере мы заведём глобальную версию всей системы, которая изменяется при поступлении любого заказа, то, очевидно, у пользователя будут околонулевые шансы успешно разместить заказ.»
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Переношу сюда обсуждение из имейла.
TLDR:
Мой личный опыт работы с Optimistic concurrency control не позволил мне понять идею из книги, что проверять ревизию разделяемого ресурса необходимо не только при изменении заказа, но и при создании, т.к. операция создания не может быть идемпотентной.
Я привык, что "разделяемый ресурс" это конкретный заказ и версия требуется при изменении заказа, например:
Однако если нам требуется предотвратить создание одного и того же заказа дважды, то разделяемым ресурсом должен быть не конкретный заказ, а коллекция / массив всех заказов.
Имейл
--->
Подскажите, в чем заключается суть оптимистической блокировки в случае создания заказа? Ведь заказа еще не существует…
Я понимаю вариант с пессимистичной блокировкой / lock’ом, когда клиент выделяет себе эксклюзивное право на создание заказов.
Прикладываю скриншот - какой смысл обращаться к orderState.version, если заказа еще в принципе не существует?
Мне понятно, зачем OCC при изменении сущности, но ведь тут идет создание. Или в данном случае речь о том, что версионируется целая коллекция заказов, что-то типа serializable уровня изоляции? (если кто-то создал свой заказ между моим чтением и записью в коллекцию заказов, то моя запись закончится конфликтом)
<---
Добрый день, Илья.
Рад, что книга оказалась полезной; я вложил в неё очень много времени и сил.
В случае с созданием заказа проблема, которую мы решаем — не допустить создания повторного заказа по ошибке. Рассмотрим два сценария:
Проблема с операцией создания заказа в том, что она нативно неидемпотентна. Оба кейса (1) и (2) вполне возможны; мы не можем технически отличить настоящее создание заказа от ошибочного. Единственное, что мы можем гарантировать — это что клиент ВИДЕЛ, что у него уже есть один заказ, когда он создавал второй.
Именно эту проблему мы и решаем, включая ревизию заказа / версию эндпойнта в запрос — что клиент получил и просмотрел самое свежее состояние разделяемого ресурса. Для этого как раз нужно ВСЕГДА запрашивать актуальное состояние, ДАЖЕ если клиент уверен, что никакого заказа у него ПОКА нет. (На самом деле, если есть строгая проверка ревизии на сервере, то необязательно — просто UX будет более удобным, без показа лишних ошибок.)
--->
Кажется я все понял - получается, что разделяемый ресурс в Вашем примере это коллекция всех заказов такси конкретного клиента, а не один конкретный заказ. Клиенту при создании заказа необходимо указать последнюю версию этого разделяемого ресурса, которую он «видел». Т.е. при создании первого заказа надо отправить версию, которая по смыслу значит «видел, что заказов нет». А при создании второго заказа версию «видел, что уже есть один заказ».
Полагаю, что максимально широкий скоуп разделяемого ресурса это все состояние системы целиком (но тогда никакой параллелизм не будет возможен, обычно в этом нет смысла), а минимальный - это «коллекция всех заказов конкретного клиента», как раз из-за того, что создание не может быть идемпотентным.
<---
Да, примерно так. Ниже в той же главе есть примерно такое же пояснение:
«NB. Выбор ресурса, версию которого мы требуем передать для получения доступа, очень важен. Если в нашем примере мы заведём глобальную версию всей системы, которая изменяется при поступлении любого заказа, то, очевидно, у пользователя будут околонулевые шансы успешно разместить заказ.»
Beta Was this translation helpful? Give feedback.
All reactions