Как прокачать belongs_to чтобы работал в два раза быстрее (database_validations gem)

habr.png

В данной статье, я покажу почему нужно использовать db_belongs_to из database_validations гема вместо привычного нам belongs_to.

Я уверен, что большинство из вас знакомо с belongs_to из ORM ActiveRecord. Но знаете ли вы, что инициализация связи с помощь belongs_to в вашей модели также добавляет валидацию на существование связи. Это происходит потому, что belongs_to имеет опцию optional: false по умолчанию.

Таким образом, каждый раз когда вы сохраняете новый объект или обновляете существующий, вы выполняете дополнительный SQL SELECT запрос на каждую из ваших связей.

Пример

class User < ActiveRecord::Base
  belongs_to :company
  belongs_to :country
end

user = User.first
user.update(some_field: 'something')
# В первую очередь сделает два дополнительных запроса SELECT, чтобы узнать, что связи `company` и `country` существуют

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

user.update(...)
user.company.destroy!
=> команда будет выполнена без ошибок, таким образом, в базе данных будет храниться пользователь без связи с компанией, что нарушает целостность нашей базы данных (если изначально не было задумано обратное)

Чтобы решить данную проблему, вы можете добавить соответствующее ограничение внешнего ключа (foreign key constraint) в вашу базу данных. Имея данное ограничение, вы всегда будете уверены, что данная связь существует.

Что насчет производительности? Зачем нам делать SELECT запросы к базе данных, если мы уже уверены, что целостность данных будет соблюдена (с использованием ограничения внешнего ключа)?

Ответ прост — не нужно. Но, чтобы это стало возможным, нам нужно решить проблему обработки исключения ActiveRecord::InvalidForeignKey, которое мы получаем, когда пытаемся сохранить отсутствующую в базе данных связь. Это необходимо, чтобы иметь такое же поведение, как с belongs_to, чтобы errors включал такие же ошибки.

Чтобы не писать всю обработку самостоятельно, вам пригодится database_validations гем. Данный гем уже включал в себя validates_db_uniqueness_of, который понравился людям (пост на хабре) и теперь имеет db_belongs_to, который очень легко внедрить в ваш проект. db_belongs_to улучшает производительность и гарантирует целостность данных.

Метод делает несколько вещей за вас:


  • Проверяет существование правильного ограничения внешнего ключа в базе данных во время запуска приложения;
  • Парсит исключения базы данных и предоставляет соответствующие ошибки для errors вашего объекта;
  • Исключает необходимость исполнения лишних SQL запросов к базе данных;

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

Любые отзывы приветствуются! Признательны за любой вклад в проект!

© Habrahabr.ru