Caching, ActiveRecord, and disappearing methods

I was very surprised to see that my model is missing some of its attributes after first request in development mode.

The error occurred while evaluating nil.include?

With the backtrace ending with:

/var/lib/gems/1.8/gems/activerecord-2.3.2/lib/active_record/attribute_methods.rb:142:in 'create_time_zone_conversion_attribute?'

Such a magic… ;-) Here is the recipe. Place in your controller:

def index
  @posts = Rails.cache.fetch(:all_posts) { Post.all }
end

Go to your view page and press refresh.

The reason is that ActiveRecord stores some information in so called class inheritable attributes. These are stored as your model class variable (Post in that case). Let’s see…

>> before_refresh = Post
>> before_refresh.object_id
=> 70091079977500
>> before_refresh.inheritable_attributes
=> {:skip_time_zone_conversion_for_attributes=>[], :record_timestamps=>true, :reject_new_nested_attributes_procs=>{}, :default_scoping=>[], :scopes=>{:scoped=>#<Proc:0x00007f7eb4450c10@/var/lib/gems/1.8/gems/activerecord-2.3.2/lib/active_record/named_scope.rb:87>}}
>> before_refresh.inheritable_attributes.object_id
=> 70091079977380

>> reload!

>> Post.object_id
=> 70091079300300 # different!
>> before_refresh.inheritable_attributes
=> {} # different!
>> before_refresh.inheritable_attributes.object_id
=> 70091099674080 # different! but similar ;-)

It looks like ActiveRecord clears inheritable_attributes before reload… But why? I do not know… I am still learning ;-)

Anyway, here are some other reasons why you should not cache your models but described with the problem of requiring models.

If you still want to cache your models (why not?), here is the tip for disabling caching in your development mode (not so easy). You can set memcached as caching store and give it invalid port number. Queries will be missed all the time without any exception raised while writing to cache.

Edit:
Michał Kwiatkowski found better solution.

Posted by Kacper Bielecki Mon, 29 Jun 2009 16:28:00 GMT


acts_as_list - null scope attribute value

Some time ago I ran into performance problems connected with acts_as_list.

My acts_as_list in EmailAddress model was defined as follows:

acts_as_list :scope => :user

Unfortunately, most email addresses in database had user_id set to null.

Acts_as_list takes all records from table which has null scope attribute and creates one huge list from all of them… Ups… In my situation it means almost

SELECT * FROM email_addresses
query on each new EmailAddress creation what causes very low performance.

Here is my fix. It makes possible to write:

acts_as_list :scope => :user, :ignore_nil => true

After doing so, newly created EmailAddress will not be added to any list unless user is set to non null.

In my opinion original behaviour is quite odd as acts_as_list supports listable records which are not on any list…

Posted by Kacper Bielecki Tue, 21 Apr 2009 18:49:00 GMT