query cache breaks ActiveRecord's reload() or is this the desired behavior?

I've noticed that because of the query_cache, if you write the value
of an attribute on an ActiveRecord object, the object will continue to
have that "dirty" value even if you ".reload" the object or try and
fetch it again from the DB. Here's an example in a test case. You
could stick this in QueryCacheExpiryTest:

  def test_write_attribute

    # With our without the query cache shouldn't this behave the same?
    task = Task.find(1)
    starting = task.starting
    task.starting = Time.now
    task.reload
    assert_equal starting, task.starting

    # But this assert will fail
    Task.cache do
      task = Task.find(1)
      task.starting = Time.now
      task.reload
      assert_equal starting, task.starting
    end
  end

Should the query cache get cleared when "write_attribute" is called?
I've started working on a patch, couldn't it be as simple as this:

    def write_attribute_with_clears_cache(attr_name, value)
      if self.class.query_cache
        self.class.query_cache.clear_query_cache
      end
      write_attribute_without_clears_cache(attr_name, value)
    end
    alias_method_chain :write_attribute, :clears_cache

As something added to Base inside query_cache.rb?

The query cache stores the returned attributes in a class variable
(Task.query_cache.instance_variable_get(:@query_cache). Modifying an
attribute modifies that same hash, which is why #reload doesn't change
anything.

Please open up a ticket with the failing test patch. I don't think
modifying #write_attribute is the way to go though.

Please open up a ticket with the failing test patch. I don't think
modifying #write_attribute is the way to go though.

Er, nevermind: http://dev.rubyonrails.org/changeset/7238

Had a little Dr. House moment :slight_smile:

Thanks for checking this out man.

However now the casting is f'd up. I haven't looked at it thoroughly
enough yet, but I do know if you add this test to QueryCacheTest it
doesn't work:

  def test_type_cast
    Task.cache do
      assert Task.count.is_a?(Integer)
    end
  end

NoMethodError: undefined method `to_i' for ["2"]:Array
    ./test/../../../../config/../vendor/rails/activerecord/lib/
active_record/calculations.rb:263:in `type_cast_calculated_value'
    ./test/../../../../config/../vendor/rails/activerecord/lib/
active_record/calculations.rb:213:in `execute_simple_calculation'
    ./test/../../../../config/../vendor/rails/activerecord/lib/
active_record/calculations.rb:121:in `calculate'
    ./test/../../../../config/../vendor/rails/activerecord/lib/
active_record/calculations.rb:117:in `calculate'
    ./test/../../../../config/../vendor/rails/activerecord/lib/
active_record/calculations.rb:45:in `count'
    ./test/query_cache_test.rb:81:in `test_type_cast'