This project is archived and is in readonly mode.

#2655 ✓stale
Matthew Beale

Rails.cache freezes all objects passed to it

Reported by Matthew Beale | May 15th, 2009 @ 11:35 PM | in 3.x

A changeset and a file:

http://github.com/rails/rails/commit/5de340e79f1d11973b7c7bbec82f32...

http://github.com/rails/rails/blob/2e69db18ce2815c25eee64fc2978e7f6...

Local cache uses MemoryStore, and MemoryStore freezes everything it stores. Local cache is used on all caches, so any object written to any store will be frozen:

(rdb:43) Rails.cache
#<ActiveSupport::Cache::MemCacheStore:0xb70a46e8 @data=<MemCache: 1 servers, 1 buckets, ns: "greatspace", ro: false>, @addresses=["localhost"], @middleware=#<Class:0xb7051a9c>, @thread_local_key=:active_support_cache_mem_cache_store_local_cache>
(rdb:43) my_obj.frozen?
false
(rdb:43) Rails.cache.write("myobjs_#{my_obj.id}", my_obj, :expires_in => 1.day)
false
(rdb:43) my_obj.frozen?
true

This is a pretty significant change in behavior and really hard to track down. I had a method changing an instance variable telling me the object was frozen in prod. Is it intentional?

I'm going to work up a test. For a fix MemoryStore could just be dropped from local_cache in favor of something less intrusive. Or objects could be duped going into the cache? I haven't come up with anything great.

@josh peek you've touched a bunch of this code. Thoughts?

Comments and changes to this ticket

  • soulware

    soulware May 23rd, 2009 @ 01:12 AM

    Hi,

    this does not even seem to be a simple change in behavior for Rails 2.3

    The following code in a controller action -
    puts "1st pass: #{Rails.cache.fetch("key"){Item.find(1)}.frozen?}"
    puts "2nd pass: #{Rails.cache.fetch("key"){Item.find(1)}.frozen?}"

    Gives this -
    1st pass: true
    2nd pass: false

    The first time something is pulled from the per-request local cache it is frozen. Any further times it is pulled in that request it is not frozen.

    I do not believe this is intentional behavior as the freeze does not seem consistent.

  • soulware

    soulware May 23rd, 2009 @ 01:18 AM

    Try again with formatting...

    The following code in a controller action -

    puts "1st pass: #{Rails.cache.fetch("key"){Item.find(1)}.frozen?}"
    puts "2nd pass: #{Rails.cache.fetch("key"){Item.find(1)}.frozen?}"

    Gives this -

    1st pass: true
    2nd pass: false

  • soulware

    soulware May 23rd, 2009 @ 01:19 AM

    And again...

    The following code in a controller action -

    puts "1st pass: #{Rails.cache.fetch("key"){Item.find(1)}.frozen?}" 
    puts "2nd pass: #{Rails.cache.fetch("key"){Item.find(1)}.frozen?}"
    

    Gives this -

    1st pass: true 
    2nd pass: false
    
  • Guoliang Cao

    Guoliang Cao May 26th, 2009 @ 09:20 PM

    We introduced Memcached to our project in previous release and upgraded to Rails 2.3.2 in current release. "Can't modify frozen object" is causing headache now. Is freezing object intended behavior? Can we let the application to worry about whether it is appropriate to modify cached object? Please let us know. Thank you.

  • soulware

    soulware June 3rd, 2009 @ 06:55 PM

    Looks like this was fixed - see #2302

  • Matthew Beale

    Matthew Beale June 3rd, 2009 @ 07:07 PM

    #2303 is close, but that's testing things read from the cache are not frozen, the issue here is things being written are frozen.

    That's a really helpful changeset for seeing how I might make a test for this though, cool.

  • soulware

    soulware June 3rd, 2009 @ 09:32 PM

    I think the expected behavior is for everything in the cache to be frozen. The fix from #2302 ensures that things pulled from the cached are duped so the client is given an unfrozen version, ensuring the version in the cache remains unchanged.

  • Timur Vafin

    Timur Vafin June 30th, 2009 @ 07:45 PM

    These issues still here, here are two patches:
    * #2860 - fixup issue when Cache.fetch return frozen object first time * #2859 - fixup cache issue fo AR:B object

  • Timur Vafin

    Timur Vafin June 30th, 2009 @ 07:45 PM

    With formatting again.

    These issues still here, here are two patches:

    • #2860 - fixup issue when Cache.fetch return frozen object first time
    • #2859 - fixup cache issue fo AR:B object
  • Anton Rogov

    Anton Rogov September 11th, 2009 @ 08:41 AM

    those patches seem to be missing in 2.3.4

  • Matthew Williams

    Matthew Williams September 24th, 2009 @ 08:01 PM

    +1 for what Anton said.

    Still occurring in 2.3.4

  • Timur Vafin

    Timur Vafin September 25th, 2009 @ 10:52 AM

    Yep guys,
    could anybody push rails team for that?

    Thanks,
    Timur

  • Bart Zonneveld

    Bart Zonneveld October 13th, 2009 @ 04:29 PM

    Confirmed, still occuring in 2.3.4, so a big +1 from me too.

  • Roland Moriz

    Roland Moriz December 23rd, 2009 @ 12:13 AM

    not fixed on 2.3.5, too. +1

  • Jeremy Kemper

    Jeremy Kemper May 4th, 2010 @ 06:48 PM

    • Milestone changed from 2.x to 3.x
  • scottwb

    scottwb August 7th, 2010 @ 09:37 PM

    • Importance changed from “” to “”

    Still not fixes on 2.3.8...

    I forked the rails repository and made a topic branch that cherry-picked in the two related commits that were made back in July 2009 to address this (but never got merged back into 2-3-stable), plus I added one more change to dup the MemoryStore-cached object on the way back out too, to make it behave more like MemCacheStore. (Rationale for this explained here: http://sleeplesscoding.blogspot.com/2010/08/rails-23-activesupportc...)

    I believe this would address everyone's problems on this thread. If there's a 2.3.9 in our future, I'd love to see this topic branch incorporated. I submitted a pull request for this.

  • Ralph Haygood

    Ralph Haygood August 18th, 2010 @ 10:30 PM

    I too would be happy if this misfeature went away, without my having to hack it out myself.

  • ehud (at playedonline)

    ehud (at playedonline) October 26th, 2010 @ 09:46 AM

    This is also relevant to Rails 3.x, and seems like a very odd change in the cache's behavior.
    We use memcache to store ActiveRecord objects, updating their attributes and only save them to the db in certain intervals. I can't see how freezing all objects read from cache makes sense for a lot of use cases. For the time being we're monkey patching Cache.rb to not freeze the returned value. Is there a downside to that anyone can think of?

  • Jeff Kreeftmeijer

    Jeff Kreeftmeijer November 1st, 2010 @ 05:06 PM

    • Tag cleared.

    Automatic cleanup of spam.

  • Santiago Pastorino

    Santiago Pastorino February 2nd, 2011 @ 04:20 PM

    • State changed from “new” to “open”

    This issue has been automatically marked as stale because it has not been commented on for at least three months.

    The resources of the Rails core team are limited, and so we are asking for your help. If you can still reproduce this error on the 3-0-stable branch or on master, please reply with all of the information you have about it and add "[state:open]" to your comment. This will reopen the ticket for review. Likewise, if you feel that this is a very important feature for Rails to include, please reply with your explanation so we can consider it.

    Thank you for all your contributions, and we hope you will understand this step to focus our efforts where they are most helpful.

  • Santiago Pastorino

    Santiago Pastorino February 2nd, 2011 @ 04:20 PM

    • State changed from “open” to “stale”
  • Michael Kintzer

    Michael Kintzer February 2nd, 2011 @ 11:49 PM

    Seems to be still an issue with Rails 3.0.3. Using memcache store or redis store. This code in ActiveSupport::Cache

          # Get the value stored in the cache.

      def value
        if @value
          val = compressed? ? Marshal.load(Zlib::Inflate.inflate(@value)) : @value
          unless val.frozen?
            val.freeze rescue nil
          end
          val
        end
      end</code>
    
    
    
    
    Not sure I understand the rationale for freezing cache objects on reads. If the caller wants to update the return from the cache, they can modify the object to change the cache_key, thereby implicitly invalidating the previous cache result, but freezing the return val prevents this, or makes it much more difficult
  • Xavier Noria

    Xavier Noria February 16th, 2011 @ 10:05 AM

    • Assigned user set to “Xavier Noria”

    I was caught by this one today.

    I have two around filters: the first one configured in ApplicationController yields to be able to inject a tracking code for Google Analytics later. A second around filter, down in some particular controller, caches an action response.

    Since writing freezes the body, the tracking code can't be injected.

    I see no reason why the write method has to mess with the frozen flag at all, it is your business whether that object can be later modified.

    Unless someone can present a good rationale for this, I'll change this behavior as discussed in this ticket.

  • mdrozdziel

    mdrozdziel March 22nd, 2011 @ 12:49 PM

    Is this patched in any official release of Rails? If not, can you provide any kind of monkey-patch temporary fix, or something? Thanks in advance.

  • mdrozdziel
  • John Berry

    John Berry April 5th, 2011 @ 01:27 AM

    Just a +1 for Xavier's comment and approach above. This is causing us major headaches during an upgrade from Rails 2.1 and we'd prefer to be in control of whether the cached objects are frozen or not.

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Pages