ActiveRecord association :reset

Is it just me, or is the use of reset() on one-to-many and many-to-many ActiveRecord associations quite unpopular? I actually have never used it (maybe never needed to use it) and have never seen it used yet.

I was looking into performance issues in one of our projects, and noticed some bit of association loading within a loop. I realized it is actually quite deceiving if we don’t remember some things.

Consider the following scenario:

persons = Person.find(:all)
persons.each do |person|
  books = persons.books
  # ... And then say a bunch of rendering goes here.
end

Whatever the reason, let us say there are on the average 50 books associated to each person, and we need a list of all books associated to each of 50 persons in a single go. That could grow a pretty large data set.

Sure, the books for each person record are assigned to a variable within the scope of the each block and would normally be garbage collected as soon as the loop ends an iteration, but we might be ignoring that these books have been stored for the :books association of each person record. Each person record then is still referenced in the persons array, so there will be no garbage collection happening there either.

In goes the reset() function. The reset() function on associations simply clears the association data @target and updates the boolean tracker @loaded to put things back how they are before association data is loaded.

persons = Person.find(:all)
persons.each do |person|
  persons.books
  # ... And then say a bunch of rendering goes here.
  person.books.reset
end

This did not help the particular area I was looking at as we are dealing with a harmlessly small collection there. Large index pages and sitemaps should employ memory-efficient tricks too, like combinations of pre-loading record IDs and paginated fetching, so this might not exactly be useful knowledge. It really is something to watch out for, though, and it could contribute much in memory optimization.

Tags: , ,

Leave a comment