У меня есть следующий сценарий:
- Пользователь делает запрос GET
/projects/1
и получает ETag . - Пользователь делает запрос PUT
/projects/1
с ETag с шага # 1. - Пользователь делает еще один запрос PUT
/projects/1
с ETag с шага # 1.
Как правило, второй запрос PUT получит ответ 412, поскольку ETag теперь устарел - первый запрос PUT изменил ресурс, поэтому ETag больше не совпадает.
Но что, если два запроса PUT отправляются одновременно (или ровно один за другим)? Первый запрос PUT не успевает обработать и обновить ресурс до прибытия PUT # 2, что приводит к тому, что PUT # 2 перезаписывает PUT # 1. Весь смысл оптимистической блокировки в том, чтобы этого не произошло ...
rest
language-agnostic
concurrency
http
maximedupre
источник
источник
Ответы:
Механизм ETag определяет только протокол связи для оптимистической блокировки. Служба приложения несет ответственность за реализацию механизма обнаружения одновременных обновлений для обеспечения оптимистической блокировки.
В типичном приложении, которое использует базу данных, вы обычно делаете это, открывая транзакцию при обработке запроса PUT. Обычно вы читаете существующее состояние базы данных внутри этой транзакции (чтобы получить блокировку чтения), проверяете действительность Etag и перезаписываете данные (таким образом, что это приведет к конфликту записи при любой несовместимой параллельной транзакции), затем совершить. Если вы правильно настроили транзакцию, то один из коммитов должен завершиться неудачей, потому что они оба будут пытаться одновременно обновлять одни и те же данные. Затем вы сможете использовать этот сбой транзакции для возврата 412 или повторного запроса, если это имеет смысл для приложения.
источник
AND ETag = ...
в предложение вашегоUPDATE
оператораWHERE
, а затем проверив счетчик обновленных строк. (Или с помощью более жесткого уровня изоляции транзакций, но я не очень рекомендую это делать.)Вы должны выполнить следующую пару атомарно:
Другие называют это транзакцией, но, по сути, атомарное выполнение этих двух операций - это то, что не позволяет одной перезаписать другую из-за времени; без этого у вас есть состояние гонки, как вы заметили.
Это по-прежнему считается оптимистической блокировкой, если вы посмотрите на общую картину: сам ресурс не заблокирован начальным чтением (GET) каким-либо пользователем или любым пользователем, который просматривает данные, с намерением обновить или нет.
Некоторое атомарное поведение необходимо, но это происходит в рамках одного запроса (PUT), а не попытки удержать блокировку для нескольких сетевых взаимодействий; это оптимистическая блокировка: объект не блокируется GET, но все же может быть безопасно обновлен PUT.
Есть также много способов добиться атомарного выполнения этих двух операций - блокировка ресурса не единственная возможность; Например, может быть достаточно легкой блокировки потока или объекта, и это зависит от архитектуры вашего приложения и контекста выполнения.
источник
Разработчик приложения должен проверить E-Tag и предоставить эту логику. Это не волшебство, что веб-сервер делает для вас, потому что он знает только, как рассчитать
E-Tag
заголовки для статического контента. Итак, давайте возьмем ваш сценарий выше и разберем, как должно происходить взаимодействие.Сервер получает запрос, определяет E-Tag для этой версии записи, возвращая ее с фактическим содержимым.
Поскольку у клиента теперь есть значение E-Tag, оно может включать это в
PUT
запрос:На этом этапе ваше приложение должно сделать следующее:
Отправьте ответ об успехе.
Если другой запрос приходит и пытается выполнить
PUT
аналогичный запрос выше, во второй раз, когда ваш серверный код оценивает его, вы обязаны предоставить сообщение об ошибке.В случае ошибки отправьте ответ об ошибке.
Это код, который вы действительно должны написать. Фактически, E-Tag может быть любым текстом (в пределах, определенных в спецификации HTTP). Это не должно быть число. Это также может быть хеш-значением.
источник
В качестве дополнения к другим ответам я опубликую одну из лучших цитат в документации ZeroMQ, которая точно описывает основную проблему:
источник