Между проверкой и сохранением есть промежуток
Представим вымышленный каталог, где код карточки должен быть уникальным. Запрос А проверяет код К-17 и не находит его. До сохранения запроса А запрос Б делает такую же проверку и тоже не находит код. Затем оба пытаются добавить запись. Если на уровне базы нет соответствующего ограничения, предварительные проверки не запрещают появление двух одинаковых кодов. Для этого не требуется ошибка в каждой отдельной проверке: на момент выполнения каждая могла корректно видеть отсутствие записи. Проблема возникает из-за последовательности событий при параллельной работе.
Ограничение базы действует на итоговую запись
Проверка уникальности в модели Active Record сама по себе не создаёт уникального индекса в базе данных. Такой индекс или соответствующее ограничение задают отдельно в схеме. Тогда база контролирует допустимость записи независимо от того, успели ли два запроса пройти предварительную проверку. При конфликте один из запросов не сможет сохранить нарушающий правило дубль. Приложение должно корректно обработать эту ситуацию. Проверка модели при этом остаётся полезной для понятной обратной связи в обычном сценарии, но не подменяет окончательную защиту целостности данных.
Правило должно совпадать на обоих уровнях
Важно определить, что именно считается уникальным. Код может быть уникален во всём каталоге или только внутри одной организации. Во втором случае ограничение должно учитывать соответствующее сочетание полей. Также нужно согласовать правила сравнения значений, если различия регистра имеют значение. Нельзя добавить произвольный уникальный индекс и считать бизнес-задачу решённой. Перед изменением существующей базы отдельно проверяют уже накопленные данные и разбирают возможные дубли. Обработка конфликта должна сообщать пользователю понятный результат, а не показывать успешное сохранение после фактического отказа базы.
Попробуйте на практике
Разложите параллельное сохранение на бумажной временной линии. Запуск Rails, установка базы и работа с реальными данными для упражнения не нужны.
- Создайте две дорожки для запросов А и Б. На каждой разместите события проверки кода К-17 и последующего сохранения. Предположите, что до начала обоих запросов такого кода в каталоге нет.
- Расположите обе проверки раньше обоих сохранений. Объясните, почему каждая проверка может сообщить об отсутствии кода и почему этого недостаточно для запрета дубля.
- Добавьте условие уникальности к моменту записи в базу. Покажите вариант, в котором А сохраняется первым, а попытка Б создать такой же код отклоняется.
- Запишите ожидаемую реакцию приложения для Б: корректно объяснить конфликт и предложить подходящее исправление. Не отмечайте обе операции успешными и не считайте повтор проверки заменой ограничения.
Как проверить результат. На линии времени виден промежуток между проверкой и записью. Правило уникальности действует при сохранении, а конфликт получает отдельный исход. Пользовательский результат соответствует фактическому состоянию базы.
Частые вопросы
Если приложение пока используют немногие, ограничение не нужно?
Небольшая аудитория не устраняет возможность параллельных запросов. Если уникальность является обязательным свойством данных, её стоит обеспечивать соответствующим правилом базы, а не предположением о редкости совпадения.
Уникальный индекс заменяет все проверки модели?
Нет. Он обеспечивает конкретное правило целостности. Другие требования, понятные сообщения и согласование бизнес-логики остаются отдельными задачами. Важно использовать каждый механизм для своей части проверки.
Самостоятельный разбор темы. Содержание конкретной обучающей программы здесь не представлено.