Рассмотрим сайт электронной коммерции, где Алиса и Боб редактируют списки продуктов. Алиса улучшает описания, а Боб обновляет цены. Они начинают редактировать Acme Wonder Widget одновременно. Боб заканчивает первым и сохраняет товар по новой цене. Алисе требуется немного больше времени, чтобы обновить описание, и когда она заканчивает, она сохраняет продукт со своим новым описанием. К сожалению, она также перезаписывает цену старой ценой, которая не была предназначена.
По моему опыту, эти проблемы чрезвычайно распространены в веб-приложениях. Некоторое программное обеспечение (например, программное обеспечение Wiki) действительно имеет защиту от этого - обычно второе сохранение завершается неудачно с «страница была обновлена во время редактирования». Но большинство веб-сайтов не имеют этой защиты.
Стоит отметить, что методы контроллера сами по себе являются потокобезопасными. Обычно они используют транзакции базы данных, что делает их безопасными в том смысле, что если Алиса и Боб попытаются сохранить в один и тот же момент, это не приведет к повреждению. Состояние гонки возникает из-за того, что Алиса или Боб имеют устаревшие данные в своем браузере.
Как мы можем предотвратить такие условия гонки? В частности, я хотел бы знать:
- Какие методы можно использовать? например, отслеживание времени последнего изменения. Каковы плюсы и минусы каждого.
- Что такое полезный пользовательский опыт?
- В какие рамки встроена эта защита?
источник
Ответы:
Вам необходимо «прочитать ваши записи», что означает, что перед тем, как записать изменение, вам необходимо снова прочитать запись и проверить, были ли внесены какие-либо изменения в нее с момента последнего чтения. Вы можете сделать это поле за полем (мелкозернистый) или на основе отметки времени (крупнозернистый). Пока вы делаете эту проверку, вам нужна эксклюзивная блокировка записи. Если не было внесено никаких изменений, вы можете записать свои изменения и снять блокировку. Если за это время запись изменилась, вы отменяете транзакцию, снимаете блокировку и уведомляете пользователя.
источник
Я видел 2 основных способа:
Добавьте отметку времени последнего обновления страницы, которую вы редактируете, в скрытом вводе. При фиксации отметка времени сверяется с текущей и, если они не совпадают, обновляется кем-то еще и возвращает ошибку.
Pro: несколько пользователей могут редактировать разные части страницы. Страница с ошибкой может привести к странице сравнения, где второй пользователь может объединить свои изменения на новой странице.
con: иногда большие усилия теряются при больших одновременных изменениях.
Когда пользователь начинает редактировать страницу, блокирует ее в течение разумного промежутка времени, когда другой пользователь затем пытается редактировать, он получает страницу с ошибкой и должен ждать, пока не истечет время блокировки или не произойдет фиксация первым пользователем.
Pro: редактировать усилия не теряются.
против: недобросовестный пользователь может заблокировать страницу на неопределенный срок. Страница с блокировкой с истекшим сроком может все еще быть в состоянии зафиксировать, если иначе не решено (используя технику 1)
источник
Используйте оптимистичный контроль параллелизма .
Добавьте столбец versionNumber или versionTimestamp к рассматриваемой таблице (целое число самое безопасное).
Пользователь 1 читает запись:
Пользователь 2 читает запись:
Пользователь 1 сохраняет запись, это увеличивает версию:
Пользователь 2 пытается сохранить прочитанную запись:
Hibernate / JPA может сделать это автоматически с
@Version
аннотациейВам нужно где-то поддерживать состояние прочитанной записи, обычно в сеансе (это безопаснее, чем в скрытой переменной формы).
источник
SQLAlchemy
не указывает на оптимистичные офлайновые блокировки, и я не могу найти ее в документации. У вас была более полезная ссылка или вы просто указали людям на SQLAlchemy в целом?Некоторые системы объектно-реляционного сопоставления (ORM) будут определять, какие поля объекта изменились с момента загрузки из базы данных, и будут создавать оператор обновления SQL, чтобы устанавливать только эти значения. ActiveRecord для Ruby on Rails является одним из таких ORM.
Чистый эффект заключается в том, что поля, которые пользователь не изменил, не включены в команду UPDATE, отправленную в базу данных. Люди, которые обновляют различные поля одновременно, не перезаписывают изменения друг друга.
В зависимости от того, какой язык программирования вы используете, изучите доступные ORM и посмотрите, обновит ли какой-либо из них только столбцы в базе данных, помеченные как «грязные» в вашем приложении.
источник