Activerecord Objects in Hashes Aren't Garbage Collected -- a Bug or a Sort of Caching Feature

ActiveRecord objects in hashes aren't garbage collected -- a bug or a sort of caching feature?

I think I know what's going on. Ruby's GC wont free immutable objects (like symbols!). The keys returned by group_by are immutable strings, and so they wont be garbage collected.

UPDATE:

It seems like the problem is not with Rails itself. I tried using group_by alone, and sometimes the objects would not get garbage collected:

oscardelben~/% irb
irb(main):001:0> class Foo
irb(main):002:1> end
=> nil
irb(main):003:0> {"1" => Foo.new, "2" => Foo.new}
=> {"1"=>#<Foo:0x007f9efd8072a0>, "2"=>#<Foo:0x007f9efd807250>}
irb(main):004:0> ObjectSpace.each_object(Foo).count
=> 2
irb(main):005:0> GC.start
=> nil
irb(main):006:0> ObjectSpace.each_object(Foo).count
=> 0
irb(main):007:0> {"1" => Foo.new, "2" => Foo.new}.group_by
=> #<Enumerator: {"1"=>#<Foo:0x007f9efb83d0c8>, "2"=>#<Foo:0x007f9efb83d078>}:group_by>
irb(main):008:0> GC.start
=> nil
irb(main):009:0> ObjectSpace.each_object(Foo).count
=> 2 # Not garbage collected
irb(main):010:0> GC.start
=> nil
irb(main):011:0> ObjectSpace.each_object(Foo).count
=> 0 # Garbage collected

I've digged through the GC internals (which are surprisingly easy to understand), and this seems like a scope issue. Ruby walks through all the objects in the current scope and marks the ones which it thinks are still being used, after that it goes through all the objects in the heap and frees the ones which have not been marked.

In this case I think the hash is still being marked even though it's out of scope. There are many reasons why this may happening. I'll keep investigating.

UPDATE 2:

I've found what's keeping references of objects. To do that I've used the ruby mass gem. It turns out that Active Record relation keeps track of the objects returned.

User.limit(1).group_by(&:name)
GC.start
ObjectSpace.each_object(ActiveRecord::Base).each do |obj|
p Mass.references obj # {"ActiveRecord::Relation#70247565268860"=>["@records"]}
end

Unfortunately, calling reset on the relation didn't seem to help, but hopefully this is enough information for now.

ActiveRecord::Relation issue checking for nil? -- Rails 3.1

use blank?, nil? is true, if it's really nil (single instance of NilClass), but your second example always will return an Array, maybe empty, if there are no results, but an Array nonetheless. blank? checks for empty arrays, empty strings, nil and false values.

If you have problems with blank? not behaving as expected you can check for first.nil?

Elegant way to add to_hash (or to_h) method to Struct?

Try this:

class Struct
old_new = self.method(:new)
def self.new(*args)
obj = old_new.call(*args)
obj.class_exec do
def to_hash
self.members.inject({}) {|h,m| h[m] = self[m]; h}
end
end
return obj
end
end

Ruby Version Display

Chances are, you upgraded your Ruby installation somewhere along the line (or your system package manager did). It's very unlikely that you messed anything up.

Best architecture to extend two classes with the same function

You should implement your function in a module and include it into both classes. This concept is called Mixins. It is used increasingly everywhere in Ruby libraries and is Ruby's answer to multi-inheritance (which is a broader concept, but generally harder to understand with it's edge cases).

module MyModule
def to_report_yaml
self.to_yaml # or whatever
end
end

class ActiveRecord::Base
include MyModule
end

class Array
include MyModule
end

How to refactor this Ruby sanitize hash method to make it more idiomatic?

  • It is inefficient to iterate over the protected keys for each key in the hash as in your solution. Rather, just iterate over the protected keys.

  • It is inefficient to generate the array of protected keys each time the method is called. Define that array outside of the method.

The following is better in these respects:

ProtectedKeys = %w[password confirmation]
def sanitize hash
new_hash = hash.dup
ProtectedKeys.each do |k| [k, k.to_sym].each do |k|
new_hash[k] = "xxxxxxxxx" if new_hash.key?(k) and new_hash[k].present?
end end
new_hash
end


Related Topics



Leave a reply



Submit