Как получить постоянное использование памяти при переносе приложения Rails из mongoid (MongoDB) в ActiveRecord (Postgres)? - PullRequest
6 голосов
/ 30 мая 2019

Недавно я начал консультироваться и помогать в разработке приложения на Rails, которое использовало MongoDB (с Mongoid в качестве клиента БД) для хранения всех экземпляров своей модели.

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

Итак, мы сейчас находимся в процессе переноса как таблиц, так и данных из MongoDB (с Mongoid в качестве объект-сопоставителя) в Postgres (с ActiveRecord в качестве объект-сопоставителя).Поскольку мы должны убедиться, что в базе данных Mongo нет неправильных ненормализованных данных, мы должны запустить эти миграции данных внутри Rails-land, чтобы убедиться, что проверки, обратные вызовы и проверки работоспособности выполняются.

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

Поскольку мы переносим экземпляры модели один за другим, мы надеемся найти способ убедиться, что использование памяти может оставаться (почти) постоянным.

Вещи, которые в настоящее время приходят кпомните, что это может быть вызвано тем, что: (a) ActiveRecord или Mongoid хранят ссылки на экземпляры объектов, которые мы уже импортировали, и (б) миграция выполняется в одной транзакции БД, поэтому Postgres занимает все больше и больше памяти, пока она не будет завершена?

Итак, мой вопрос:

  • Какова вероятная причина такого линейного использования памяти?
  • Как мы можем уменьшить его?
  • Существуют ли способы заставить Mongoid и / или ActiveRecord отказаться от старых ссылок?
  • Должны ли мы попытаться вызвать Ruby GC вручную?
  • Существуют ли способы разделения миграции данных на несколько транзакций БД, и это поможет?

Эти миграции данных имеют следующий формат:

class MigrateSomeThing < ActiveRecord::Migration[5.2]
  def up
    Mongodb::ModelName.all.each do |old_thing| # Mongoid's #.all.each works with batches, see /5883080/poisk-zapisei-mongodb-v-paketnom-rezhime-s-ispolzovaniem-adaptera-mongoid-ruby 
      create_thing(old_thing, Postgres::ModelName.new)
    end
    raise "Not all rows could be imported" if MongoDB::ModelName.count != Postgres::ModelName.count
  end

  def down
    Postgres::ModelName.delete_all
  end

  def create_thing(old_thing, new_thing)
    attrs = old_thing.attributes
    # ... maybe alter the attributes slightly to fit Postgres depending on the thing.
    new_thing.attributes = attrs
    new_thing.save!
  end

end

1 Ответ

3 голосов
/ 31 мая 2019

Я предлагаю сузить потребление памяти для чтения или записи (или, иначе говоря, Mongoid vs AR), выполнив все чтения, но не создавая / записывая модель и не наблюдая, продолжает ли расти использование памяти.

Mongoid выполняет поиск в пакетах по умолчанию, в отличие от AR, где это необходимо запрашивать через find_in_batches.

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

  1. Отключить неявную транзакцию для рассматриваемой миграции (https://apidock.com/rails/ActiveRecord/Migration):

    disable_ddl_transaction!

  2. Создание данных с помощью прямых вставок, полностью обходя создание экземпляров модели (это также ускорит процесс). Самый простой способ - через SQL ( Rails ActiveRecord: получение идентификатора необработанной вставки ), для этого также есть библиотеки ( Массовая вставка записей в таблицу Active Record ).

...