Skip to main content

Что такое ignored_columns

ignored_columns — это макрос (class method) в ActiveRecord::Base, который позволяет указать модели Rails, какие колонки из соответствующей таблицы в базе данных следует полностью игнорировать.

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

Если вы добавляете колонку в ignored_columns, Rails будет вести себя так, будто этой колонки в базе данных не существует.

  • Он не будет создавать для нее атрибуты в модели.
  • Он не будет пытаться читать из нее данные при SELECT запросах.
  • Он не будет пытаться записать в нее данные при INSERT или UPDATE.

Зачем это нужно? Проблема нулевого простоя (Zero-Downtime Deployment)

Основное предназначение ignored_columns — обеспечение безопасных миграций по удалению колонок из базы данных в продакшн-среде без остановки приложения.

Представьте себе типичный процесс развертывания:

  1. Выкатывается новый код.
  2. Запускаются миграции базы данных.

Теперь рассмотрим, что произойдет, если вы просто удалите колонку без ignored_columns:

Сценарий сбоя:

  1. Деплой кода: Вы удалили использование колонки users.legacy_data из всего кода и выкатили этот код на продакшн-серверы.
  2. Состояние до миграции: Новый код уже работает, но миграция на удаление колонки legacy_data еще не запущена. Колонна все еще существует в БД.
  3. Кэш Rails: Когда сервер с новым кодом запускается, Rails смотрит на схему таблицы users и видит колонку legacy_data. Он кэширует эту информацию и создает для модели User соответствующий атрибут.
  4. Запуск миграции: Вы запускаете миграцию remove_column :users, :legacy_data. База данных удаляет колонку.
  5. КРАХ! Следующий запрос, который приходит на ваш сервер (который все еще работает со старым кэшем схемы), пытается выполнить что-то вроде User.find(1). ActiveRecord генерирует SQL SELECT "users".* FROM "users" WHERE "users"."id" = 1. База данных пытается выполнить этот запрос, но не находит колонку legacy_data и возвращает ошибку ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column "legacy_data" does not exist.
  6. Результат: Ваше приложение падает с ошибкой 500 до тех пор, пока все серверные процессы не будут перезапущены, чтобы они обновили свой кэш схемы уже без удаленной колонки. Это и есть простой (downtime).

Как ignored_columns решает эту проблему

ignored_columns позволяет разорвать этот порочный круг, разделив процесс на три безопасных шага.

Шаг 1: Деплой с игнорированием колонки

Сначала вы готовите код к тому, что колонка скоро исчезнет.

  1. Не удаляйте колонку! Просто зайдите в модель и добавьте ignored_columns.

    # app/models/user.rb
    class User < ApplicationRecord
    # ...
    self.ignored_columns = [:legacy_data]
    # ...
    end
  2. Создайте Pull Request и выкатите этот код в продакшн.

Что теперь происходит? Все ваши серверы теперь работают с кодом, который полностью игнорирует колонку legacy_data. Даже если она есть в базе, Rails ее не видит, не читает и не пишет в нее. Приложение полностью готово к ее физическому удалению.

Шаг 2: Деплой с миграцией на удаление

Теперь, когда код готов, можно безопасно удалять колонку.

  1. Создайте миграцию.

    rails g migration RemoveLegacyDataFromUsers
  2. Добавьте в нее код удаления колонки.

    # db/migrate/20250825140000_remove_legacy_data_from_users.rb
    class RemoveLegacyDataFromUsers < ActiveRecord::Migration[7.1]
    def change
    remove_column :users, :legacy_data, :string
    end
    end
  3. Создайте Pull Request и выкатите его, запустив миграцию.

Что теперь происходит? Миграция выполняется и удаляет колонку из базы данных. Ваши серверы, которые уже работают с кодом из Шага 1, даже не замечают этого, потому что они и так игнорировали эту колонку. Никаких ошибок, никакого простоя.

Шаг 3: Деплой с очисткой кода

Колонка удалена, но в коде остался "технический долг" — строка ignored_columns. Ее нужно убрать.

  1. Удалите ignored_columns из модели.

    # app/models/user.rb
    class User < ApplicationRecord
    # ...
    # self.ignored_columns = [:legacy_data] # <- Убираем эту строку
    # ...
    end
  2. Создайте Pull Request и выкатите этот код.

Теперь ваш код и схема базы данных снова находятся в идеальной синхронизации.

Важные моменты и лучшие практики

  • Несколько колонок: Если нужно игнорировать несколько колонок, передайте массив символов:

    self.ignored_columns = [:legacy_data, :old_preference, :temp_field]
  • Безопасное добавление: Если в модели (или в одном из ее concerns) уже может быть определен ignored_columns, безопаснее добавлять к существующему списку, а не перезаписывать его:

    self.ignored_columns += [:new_column_to_ignore]
  • Инструменты: Гем strong_migrations автоматизирует проверку на подобные опасные миграции и не даст вам удалить колонку без предварительного игнорирования, что очень помогает в командной работе.

Итог: ignored_columns — это критически важный инструмент для управления схемой базы данных в работающем приложении. Он позволяет вам безопасно, в несколько этапов, удалять колонки, полностью исключая риск падения приложения и обеспечивая тот самый "zero-downtime".