- PVSM.RU - https://www.pvsm.ru -

Настоящая валидация на уникальность

Каждый рубист, поработавший с Ruby On Rails [1] знаком с ORM [2] ActiveRecord [3]. Обсудим одну из предложенных из коробки валидаций, а именно, валидации на уникальность, и почему database_validations [4] gem спасет консистенцию вашей базы данных.

Допустим, у вас есть модель пользователей с уникальностью на поле email, т.е.

class User < ApplicationRecord
  validates :email, uniqueness: true
end

Вы, возможно, уже знаете, что данная валидация выполняет следующий запрос

SELECT 1 FROM users WHERE email = $1

каждый раз, когда мы пытаемся сохранить запись в базу данных.

У данного подхода, есть несколько недостатков:

Во-первых, исполнение дополнительного запроса, и в случае, если в модели инициализировано несколько валидаций на уникальность, запрос будет выполнен на каждую из них. Это не эффективно, а также требует наличие индексов, если мы хотим, чтобы данные запросы исполнялись быстро.

Во-вторых, данное решение не гарантирует уникальность из-за возможной гонки за данными [5]. Несколько конкурентных операций могут одновременно узнать об отсутствии конкретной записи, в следствии чего, сохранить одни и те же данные.

Конечно, редкие случаи с гонкой данных возможно разрешить добавив ограничение на уникальность на уровне базы данных. Но в данном случае, вы не получите ошибку валидации, запрос к БД просто упадет и вся транзакция откатится.

В этой ситуации поможет gem database_validations [4], который предоставляет совместимость между ограничениями в базе данных и валидациями.

Ознакомившись с документацией [6] и бенчмарками [7], можно прийти к выводу, что данный gem ускорит процесс сохранения записей в базу данных минимум в два раза.

Благодаря поддержке таких баз данных, как PostgreSQL, SQLite, MySQL и обратной совместимости с validates_uniqueness_of, процесс замены на validates_db_uniqueness_of занимает считанные минуты.

Удобный matcher для RSpec также присутствует из коробки:

specify do
  expect(described_class)
    .to validate_db_uniqueness_of(:field)
    .with_message('duplicate')
    .with_where('(some_field IS NULL)')
    .scoped_to(:another_field)
    .with_index(:unique_index)
end

При переходе на новую валидацию, вам необходимо иметь ограничения на уникальность в базе данных, но если их еще нет, gem об этом укажет во время запуска приложения.

Гем протестирован на приложении с 100+ валидациями на уникальность среди 50+ моделей.

Используйте гем и делитесь мнением. Любой вклад в дальнейшее развитие приветствуется!

Автор: djezzzl

Источник [8]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/ruby/300453

Ссылки в тексте:

[1] Ruby On Rails: https://rubyonrails.org/

[2] ORM: https://ru.wikipedia.org/wiki/ORM

[3] ActiveRecord: https://github.com/rails/rails/tree/master/activerecord

[4] database_validations: https://github.com/toptal/database_validations

[5] гонки за данными: https://ru.wikipedia.org/wiki/%D0%A1%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B5_%D0%B3%D0%BE%D0%BD%D0%BA%D0%B8

[6] документацией: https://github.com/toptal/database_validations#databasevalidations

[7] бенчмарками: https://github.com/toptal/database_validations#benchmark-code

[8] Источник: https://habr.com/post/431298/?utm_campaign=431298