При работе с огромными массивами объектов ActiveRecord будьте очень осторожны ... При обработке этих объектов в цикле, если на каждой итерации вы загружаете связанные с ними объекты с помощью has_many, Active_ecy ActiveRecord и т. Д. - использование памяти значительно увеличивается, каждый объект, принадлежащий массиву, растет ...
Нам очень помог следующий метод ( упрощенный пример ):
students.each do |student|
cloned_student = student.clone
...
cloned_student.books.detect {...}
ca_teachers = cloned_student.teachers.detect {|teacher| teacher.address.state == 'CA'}
ca_teachers.blah_blah
...
# Not sure if the following is necessary, but we have it just in case...
cloned_student = nil
end
В приведенном выше коде "cloned_student" - это объект, который растет, но, поскольку он "обнуляется" в конце каждой итерации, это не проблема для огромного количества студентов. Если бы мы не делали «клонирование», переменная цикла «student» выросла бы, но поскольку она принадлежит массиву - используемая им память никогда не освобождается, пока существует объект массива.
Работает и другой подход:
students.each do |student|
loop_student = Student.find(student.id) # just re-find the record into local variable.
...
loop_student.books.detect {...}
ca_teachers = loop_student.teachers.detect {|teacher| teacher.address.state == 'CA'}
ca_teachers.blah_blah
...
end
В нашей производственной среде у нас был фоновый процесс, который не удалось завершить один раз, потому что 8 ГБ оперативной памяти было недостаточно для него. После этого небольшого изменения он использует менее 1 ГБ для обработки того же объема данных ...