This project is archived and is in readonly mode.

#6179 new
fearless_fool

Heisenberg principal in ActiveRecord::Relation: observing relation changes it

Reported by fearless_fool | December 16th, 2010 @ 05:45 AM

I have isolated a case where calling ActiveRecord::Relation#all (or evidently anything operation that forces evaluation) changes its state to that it subsequently produces incorrect results. A self-contained test example is attached. Observed in Ruby 1.9.2 / Rails 3.0.0

The general synopsis:

A RefThing table has two fks: obj1_id and obj2_id. The following method correctly create a RefThing and returns 1:

def works(obj1, obj2)
  relation1 = RefThing.where(:obj1_id => obj1.id)
  relation1.where(:obj2_id => obj2.id).create
  relation1.size
end

The following method returns zero instead, even though it correctly creates a RefThing. Note the ONLY difference is the call to relation1.all before the create:

def broke(obj1, obj2)
  relation1 = RefThing.where(:obj1_id => obj1.id)
  relation1.all    # forces evaluation of relation1
  relation1.where(:obj2_id => obj2.id).create
  relation1.size
end

[As an aside: it's not just that relation1.size is returning 0 -- it's simply empty, and attempts to use it for accessing fields from RefThing are futile. And yes, it took me a long time to isolate this from a much larger code body! :)]

Comments and changes to this ticket

  • fearless_fool

    fearless_fool December 17th, 2010 @ 07:53 PM

    • Assigned user set to “Aaron Patterson”

    Assigning this to Aaron, since he appears to be the relationship maven. If I'm violating lighthouse etiquette by assigning the ticket, please excuse me - I'm not sure of the process.

  • Samuel Kadolph

    Samuel Kadolph December 17th, 2010 @ 08:15 PM

    • Assigned user cleared.
    • Tag cleared.

    For future reference, misusing a physics term isn't helpful.

    This is simply because the results of the relation are being cached and currently models created with create are not added into the relation's cache.

    ruby-1.9.2-p0 > r = Widget.scoped
     => [#<Widget id: 1>]
    ruby-1.9.2-p0 > r
     => [#<Widget id: 1>]
    ruby-1.9.2-p0 > r.create
     => #<Widget id: 2>
    ruby-1.9.2-p0 > r
     => [#<Widget id: 1>]
    

    Calling reload on the relation proves this.

    ruby-1.9.2-p0 > r.reload
     => [#<Widget id: 1,>, #<Widget id: 2>]
    
  • fearless_fool

    fearless_fool December 17th, 2010 @ 10:38 PM

    @SK:

    The Heisenberg reference was intended to enliven your day, even if not totally accurate. But -- absent any documentation about how relations are cached -- one would agree that having a relation change its state merely by observing it is kind of astonishing.

    So, to make sure I understand, what was happening in the OP was that in the working example, relation1 was NOT being cached because nothing had forced its evaluation. In the broken example, relation1 was getting cached in its empty state and not getting reloaded after the create, so it appeared to be empty.

    I can live with that if it's documented somewhere. If not, please (please) point me to the appropriate section on relations and I'd be happy to contribute to improving them.

    • ff

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>

People watching this ticket

Attachments

Pages