интенсивно использует память

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

Задача очень проста и analytics_eligbile? метод всегда возвращает false, учитывая, где данные находятся сейчас, поэтому в основном не вызывается ни один из сложных кодов. У меня есть около 200 сообщений в моей выборке данных в разработке. Пост has_one analytics_facet.

Независимо от внутренней логики / бизнеса здесь, единственное, что делает эта задача, это вызывает analytics_elptable? метод 200 раз каждые 2 минуты. В течение 4 часов мое использование физической памяти составляет 110 МБ, а виртуальной памяти - 200 МБ. Просто для того, чтобы сделать что-то такое простое! Я даже не могу себе представить, сколько памяти это будет съесть, если он будет выполнять реальную аналитику на 10000 сообщений с реальными производственными данными !! Конечно, он может не работать каждые 2 минуты, больше как каждые 30, но я не думаю, что он будет летать.

Это работает ruby ​​1.9.7, rails 2.3.5 на Ubuntu 10.x 64 бит. Мой ноутбук имеет 4 ГБ памяти, двухъядерный процессор.

Действительно ли рельсы такие плохие или я что-то не так делаю?

 Delayed::Worker.logger.info('RAM USAGE Job Start: ' + `pmap #{Process.pid} | tail -1`[10,40].strip)
Post.not_expired.each do |p|
    if p.analytics_eligible?
        #this method is never called
        Post.find_for_analytics_update(p.id).update_analytics
    end
end
Delayed::Worker.logger.info('RAM USAGE Job End: ' + `pmap #{Process.pid} | tail -1`[10,40].strip)

Delayed::Job.enqueue PeriodicAnalyticsJob.new(), 0, 2.minutes.from_now

Post Model

def analytics_eligible?
        vf = self.analytics_facet
        if self.total_ratings > 0 && vf.nil?
            return true
        elsif !vf.nil? && vf.last_update_tv > 0
            ratio = self.total_ratings / vf.last_update_tv
            if (ratio - 1) >= Constants::FACET_UPDATE_ELIGIBILITY_DELTA
                return true
            end
        end
        return false
    end
9
задан badnaam 18 August 2010 в 05:40
поделиться

4 ответа

ActiveRecord довольно требователен к памяти — будьте очень осторожны при выборе и помните, что Ruby автоматически возвращает последний оператор в блоке в качестве возвращаемого значения, что может означать, что вы возвращаете массив записей, которые сохраняются. в результате где-то и, следовательно, не имеют права на сбор мусора.

Кроме того, когда вы вызываете "Post.not_expired.each", вы загружаете все ваши сообщения с сроком действия not_expired в оперативную память. Лучшим решением является find_in_batches, которое загружает только X записей в ОЗУ за раз.

Исправить это можно так просто:

def do_analytics
  Post.not_expired.find_in_batches(:batch_size => 100) do |batch|
    batch.each do |post|
      if post.analytics_eligible?
        #this method is never called
        Post.find_for_analytics_update(post.id).update_analytics
      end
    end
  end
  GC.start
end

do_analytics

Здесь происходит несколько вещей. Во-первых, все это ограничено функцией, чтобы предотвратить коллизии переменных со ссылками из итераторов блоков. Затем find_in_batches извлекает объекты batch_size из БД за раз, и до тех пор, пока вы не создаете ссылки на них, после каждой итерации вы получаете право на сборку мусора, что снижает общее использование памяти. Наконец, мы вызываем GC.start в конце метода; это заставляет GC запускать развертку (чего вы не хотели бы делать в приложении реального времени, но, поскольку это фоновое задание, это нормально, если для его выполнения требуется дополнительные 300 мс). Он также имеет очень явное преимущество, если возвращает nil, что означает, что результатом метода является nil, а это означает, что мы не можем случайно удержать экземпляры AR, возвращенные из средства поиска. .

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

При всем при этом, на мой взгляд, это проблема cron (периодическая повторяющаяся работа), а не проблема DJ. У вас может быть одноразовый анализатор аналитики, который запускает вашу аналитику каждые X минут с помощью script/runner, вызываемого cron, который очень аккуратно очищает любые потенциальные утечки памяти или неправильное использование за прогон (поскольку весь процесс заканчивается в конце)

19
ответ дан 4 December 2019 в 08:14
поделиться

Это факт, что Ruby потребляет (и утекает) память. Не знаю, сможете ли вы что-то с этим поделать, но по крайней мере я рекомендую вам взглянуть на Ruby Enterprise Edition .

REE - это порт с открытым исходным кодом, который, помимо прочего, обещает «на 33% меньше памяти». Я использую REE с Passenger в производстве уже почти два года, и я очень доволен.

0
ответ дан 4 December 2019 в 08:14
поделиться

Если вы испытываете проблемы с памятью, одним из решений является использование другой технологии фоновой обработки, например resque. Это обработка BG, используемая github.

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

Как?

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

Более подробную техническую информацию вы можете найти в README.

1
ответ дан 4 December 2019 в 08:14
поделиться

Пакетная загрузка данных и агрессивное использование сборщика мусора, как предложил Крис Хилд, дадут вам действительно большие выгоды, но люди часто упускают из виду еще одну область — это то, в какие фреймворки они загружаются.

Загрузка стека Rails по умолчанию даст вам ActionController, ActionMailer, ActiveRecord и ActiveResource вместе. Если вы создаете веб-приложение, возможно, вы используете не все из них, но, вероятно, используете большинство из них.

Когда вы создаете фоновое задание, вы можете избежать загрузки вещей, которые вам не нужны, создав для этого пользовательскую среду:

# config/environments/production_bg.rb

config.frameworks -= [ :action_controller, :active_resource, :action_mailer ]

# (Also include config directives from production.rb that apply)

Каждая из этих платформ будет просто ждать письма, которое никогда не будет отправлено. отправлено, или контроллер, который никогда не будет вызван. Их просто нет смысла загружать. Отредактируйте файл database.yml, настройте фоновое задание для запуска в среде production_bg, и у вас будет гораздо более чистый лист для начала.

Еще одна вещь, которую вы можете сделать, это использовать ActiveRecord напрямую, вообще не загружая Rails. Это может быть все, что вам нужно для этой конкретной операции. Я также обнаружил, что использование облегченного ORM, такого как Sequel, делает вашу фоновую работу очень легкой, если вы в основном выполняете вызовы SQL для реорганизации записей или удаления старых данных. Если вам нужен доступ к вашим моделям и их методам, вам нужно будет использовать ActiveRecord. Однако иногда стоит повторно реализовать простую логику на чистом SQL из соображений производительности и эффективности.

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

В конце концов, если для запуска чего-то важного требуется 100 МБ памяти, но вы можете сократить его до 10 МБ за три недели работы, я не понимаю, зачем вам беспокоиться. 90 МБ памяти стоят максимум около 60 долларов в год у управляемого поставщика, что обычно намного дешевле, чем ваше время.

Ruby on Rails придерживается философии большей заботы о вашей продуктивности и времени, чем об использовании памяти. Если вы хотите подстричь его, посадить на диету, вы можете это сделать, но для этого потребуется немного усилий.

6
ответ дан 4 December 2019 в 08:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: