Длительная до смерти миграция / find_each - PullRequest
4 голосов
/ 03 мая 2011

Запуск Rails 3 с PostgreSQL,

У меня есть миграция, обновление миллионов маленьких записей.

Record.find_each do |r|
  r.doing_incredibly_complex_stuff
  r.save!
  puts "#{r.id} updated"
end

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

Итак, могу ли я запустить этот find_each вне транзакции - хотя это довольно безопасно - сэкономив много времени и памяти "commit"?

своего рода ActiveRecord :: Base.without_transaction do ...; end Я думаю: -)

ИЛИ: Я ошибаюсь, миграции не заключены в транзакции, и время, которое я вижу, - это только применение операторов обновления SQL?

РЕДАКТИРОВАТЬ: кажется, что нет никакой связи с транзакциями, вот трассировка стека, которую я получил, как только я прервал миграцию, когда все было напечатано на экране, а объем оперативной памяти уменьшился с 500 МБ до ~ 30 МБ:

IRB::Abort: abort then interrupt!!
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activesupport-3.0.4/lib/active_support/whiny_nil.rb:46:in `call'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activesupport-3.0.4/lib/active_support/whiny_nil.rb:46:in `method_missing'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/connection_adapters/postgresql_adapter.rb:978:in `flatten'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/connection_adapters/postgresql_adapter.rb:978:in `block in select'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/connection_adapters/postgresql_adapter.rb:977:in `map'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/connection_adapters/postgresql_adapter.rb:977:in `select'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/connection_adapters/abstract/database_statements.rb:7:in `select_all'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/connection_adapters/abstract/query_cache.rb:56:in `select_all'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/base.rb:467:in `find_by_sql'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/relation.rb:64:in `to_a'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/relation.rb:356:in `inspect'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/railties-3.0.4/lib/rails/commands/console.rb:44:in `start'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/railties-3.0.4/lib/rails/commands/console.rb:8:in `start'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/railties-3.0.4/lib/rails/commands.rb:23:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'

РЕДАКТИРОВАТЬ (2): Wow. Оказалось, что это было очень долго, поскольку find_each возвращает все элементы после итерации.

Я пытался:

Record.tap do |record_class|
  record_class.find_each do |r|
    r.doing_incredibly_complex_stuff
    r.save!
    puts "#{r.id} updated"
  end
end
=> Record(id: integer, ...)

Так что он вернул консоль мгновенно, как и ожидалось. :)

Но тогда я все еще вижу странное поведение: RAM не освобождается. Вместо этого, как только я вышел из термина, RAM все еще падает ...

Может, мое решение с краном не удовлетворяет? Это все еще массовый отбор? Как мне избежать массового выбора после find_each?

Спасибо!

Ответы [ 2 ]

2 голосов
/ 03 мая 2011

Ни ActiveRecord::Migration, ни find_each не делают ничего, чтобы обернуть ваш код в транзакцию базы данных.r.save! будет заключен в отдельную транзакцию, которая охватывает любые каскадные эффекты сохранения.

Как и в комментариях выше, использование update_all или необработанного execute будет быстрее для массового обновления.У меня нет возможности сказать, подходит ли это для того, что вы делаете.Кроме того, если у вас проблемы с памятью, вы можете настроить размер пакета на find_each и посмотреть, будет ли он иметь эффект.Если нет, возможно, вы где-то держитесь за эти объекты.

0 голосов
/ 30 ноября 2011

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

end ; nil
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...