Два сотрудника нажали «Сохранить»: как не потерять правки
Чтобы два сотрудника не перезаписали правки друг друга, сохраняйте запись условно: вместе с изменением передавайте версию, на основе которой открыли форму. Сервер должен проверить условие в защищённом пути записи. Если данные уже изменились, интерфейс сохраняет введённый текст и предлагает сравнение, а не молча объявляет победителем последнего нажавшего кнопку.
Оба исправления выглядят правильными
Учебная карточка содержит адрес доставки и контактное примечание. Первый сотрудник меняет адрес, второй — примечание. Оба открыли исходное состояние. Если каждый отправляет полную карточку, второй может вернуть старый адрес вместе со своим новым текстом. Человек не намеревался отменять чужую правку, но результат получился именно таким.
Проверка «сотрудник имеет право редактировать» не решает этот конфликт. Оба действительно могут иметь нужные полномочия. Вопрос другой: актуальна ли прочитанная основа изменения? При разработке CRM это нужно принять отдельным сценарием, особенно для длинных форм, которые остаются открытыми во время работы других людей.
Три состояния нужны и серверу, и экрану сравнения
Сохраняйте связь с исходной версией формы. Для разбора нужны прежняя прочитанная запись, локальные предложения сотрудника и актуальные данные сервера. По двум последним снимкам не всегда понятно, что пользователь сознательно изменил, а что просто отправил обратно как часть старой формы.
В условных HTTP-запросах описана защита от потерянного обновления. Один из механизмов — валидатор ETag и условие If-Match. Это вариант протокола, а не обязательное имя поля в любой внутренней системе. ETag не требуется делать последовательным номером или хешем определённого формата.
Учебная последовательность сохранений
Обозначения v-A и v-B в таблице вымышлены и означают разные версии, не готовые значения HTTP-заголовков. Первое принятое изменение делает прочитанную вторым сотрудником основу устаревшей.
| Действие | Прочитанная основа | Текущее состояние сервера | Решение | Что остаётся у человека | Следующий шаг |
|---|---|---|---|---|---|
| Оба открыли карточку | v-A | v-A | Редактирование разрешено по правам | Каждый работает со своим черновиком | Сохранить с условием прочитанной основы |
| Первый сохраняет новый адрес | v-A | v-A | Принять и получить v-B | Подтверждённый результат первого | Показать фактически сохранённую карточку |
| Второй сохраняет примечание и старый адрес | v-A | v-B | Отклонить устаревшее условие | Не уничтожать локальное примечание | Показать изменившийся адрес и предложенное примечание |
| Второй сверил различия | Основа обновлена после согласования | v-B либо более новая | Новая условная попытка | Согласованное предложение | При новом конфликте повторить осмысленное сравнение |
Условие и запись нельзя разделять окном гонки
Схема «прочитать версию, проверить, затем когда-нибудь сохранить» оставляет возможность другого изменения между действиями. Реализация должна обеспечить проверку и запись подходящим согласованным способом. Не принимайте демонстрацию, где конфликт ловит только клиентская проверка до отправки: данные могут измениться сразу после неё.
В MDN If-Match описано сильное сравнение валидатора; при невыполненном условии возвращается 412 Precondition Failed. Но один заголовок в запросе не подтверждает защиту, если обработчик игнорирует его или проверяет другой ресурс. Конкретную серверную реализацию нужно испытать на конкурентном сценарии.
Что показать после конфликта
Скажите, что запись уже изменилась, и сохраните предложение пользователя для разбора. Покажите значимые различия с понятными подписями: что было при открытии, что изменилось на сервере и что предлагается сейчас. Простое сообщение «ошибка сохранения» не помогает принять решение, а автоматическая перезагрузка может уничтожить несохранённый текст.
Не разрешайте проблему скрытой повторной отправкой прежнего тела с новым валидатором. Такой обход формально использует актуальную версию, но всё равно может затереть изменение первого сотрудника. Новая основа имеет смысл только после подготовки нового согласованного предложения. При повторном конфликте процесс должен оставаться понятным.
Если выбран ручной отказ от своих правок, дайте увидеть последствия. Если допускается принудительная замена, определите полномочия и запись решения отдельно. У обычной кнопки повторной попытки не должно быть скрытого значения «перезаписать всё независимо от остальных». Права и актуальность версии проверяются разными условиями.
Почему разные поля не всегда можно слить автоматически
Адрес и примечание в простом примере могут выглядеть независимыми. Но в реальном процессе изменение одного поля влияет на другое: адрес меняет доступные интервалы доставки, а выбранная услуга — допустимую стоимость. Совмещение двух формально разных полей может дать недопустимую комбинацию. Выберите правила слияния по смыслу сущности, не только по совпадению имён.
В документации EF Core разбор конфликта выделен в самостоятельную логику выбора значений. Там же описаны разные способы контроля конкуренции и ограничения длительных транзакций. Для формы, открытой человеком, нельзя механически держать блокировку базы всё время его размышлений как бесплатное универсальное решение.
Проверить все пути записи
Интеграция, импорт или фоновая обработка могут менять ту же карточку. Если только веб-форма поддерживает версию, другой путь способен обойти защиту и сделать её неполной. Для интеграций через API согласуйте обязательность условия и поведение устаревшей команды, не обещая одинаковые заголовки всем сторонним системам.
- Создайте учебную карточку и откройте её двумя редакторами. Сохраните их исходные снимки и предложения.
- Выполните две конкурирующие попытки, включая изменение разных и одного поля. Сверьте фактическую запись с объявленной политикой.
- Проверьте, что отклонённое сохранение не уничтожило локальный текст и не объявило ложный успех.
- Измените карточку ещё раз до согласованной повторной попытки. Новый конфликт тоже должен обрабатываться без тихой перезаписи.
- Повторите применимые случаи через импорт и другие разрешённые источники записи.
- Проверьте отказ от своего черновика и отдельно разрешённые исключения. Каждое решение должно быть осознанным и объяснимым.
Для внутреннего веб-приложения результатом приёмки станет не наличие поля version, а сохранность двух намерений: принятое изменение остаётся на сервере, отклонённое — доступно человеку для решения. После этого можно обсуждать удобное сравнение и частичное слияние, не скрывая конфликт под обычной кнопкой сохранения.
Вопросы и ответы
Что такое оптимистическая блокировка?
Это подход, при котором при сохранении проверяют, не изменилась ли прочитанная основа. Конфликт требует отдельного решения, а не автоматического разрешения последней записи.
Как If-Match и ETag помогают от потерянного обновления?
Клиент передаёт условие, связанное с полученной версией, а сервер применяет изменение только при его выполнении. Важно обеспечить корректную проверку в пути записи и понятный ответ интерфейса на конфликт.
Чем оптимистическая блокировка отличается от пессимистической?
Оптимистический подход выявляет конфликт при попытке изменения, а пессимистический может удерживать блокировку на время операции. Выбор зависит от процесса; длительная пользовательская форма не делает удержание транзакции универсально подходящим вариантом.