On Jul 8, 2011, at 8:24 PM, Michael Bayer wrote:

> On Jul 8, 2011, at 6:17 PM, Ben Chess wrote:
> 
>> Hmm, bummer.
>> 
>> We're pretty dependent on detached mode.  We eagerload a bunch of
>> things for the purposes of storing them in memcache, and things don't
>> go well when they're not fully traversable.  
> 
> OK so you're caching.    Actually when I worked with caching a lot I found 
> that I was looking for the objects to exist without their related 
> collections, then I'd use the Beaker recipe so that the collections etc. were 
> cached separately as they were lazy loaded.    It allowed the cache to 
> contain individual collections of things more discretely, rather than several 
> large trees tailored towards some specific loading scenario.    The more 
> granular level collections allowed the retrieval over memcached to not spend 
> time loading things that weren't needed for a particular view.

Okay, but let's not get off topic.  How the results are cached has nothing to 
do with the fact that eagerload just isn't reliable.  If I eagerload a few 
objects in, I'd expect that that no further SQL queries will be necessary to 
use those objects.  That's currently not the case.

I experimented a bit with trying to to intelligently recurse down the paths 
defined by the eagers and see if there are any unloaded objects.  I didn't 
quite know what to do in the case of lists.  It felt like I was going to have 
to re-write a lot of things that the populators already do.

My proposal: If there are eagers with paths longer than 1 attribute, always 
re-populate that attribute.  I'd rather have correctness over performance.  Or 
at least have the option.  Here's my patch: http://pastebin.com/j8pakaLP

>> I've been delving into
>> the mapper.py code site you mention.  It's a lot of new code to me,
>> but I agree it doesn't seem trivial.  Also seems like a potential
>> performance hit, but could be something that'd be enabled with an
>> option.
>> 
>> One thing I still don't understand though is why the problem only
>> happens when we first eagerly load the completely unrelated "d_row"?
> 
> its the "a_obj.c_rows" that loads all the C's which then eagerload their A, 
> the A of course is already in the Session but it does the join and populates 
> each C.a_row.

I understand that a_obj.c_rows loads in the Cs.  That doesn't address my 
question.  My test passes if you don't eagerload d_row, and I'm not sure why.

>> 
>> On Thu, Jul 7, 2011 at 6:39 PM, Michael Bayer <mike...@zzzcomputing.com> 
>> wrote:
>>> 
>>> On Jul 7, 2011, at 7:04 PM, Ben Chess wrote:
>>> 
>>>> I've hit a problem where eagerload() fails to load in a relation of a
>>>> relation when lazy='joined' is involved.  It's easiest just to show
>>>> the test.  It fails in 0.7.1, and an equivalent test also fails in
>>>> 0.6.8.
>>>> 
>>>> http://pastebin.com/ruq6SM1z
>>>> 
>>>> Basically, A has relations to B, C, and D.  C's relationship to A is a
>>>> lazy='joined'.
>>>> 
>>>> First load A, eagerloading 'd_row'
>>>> Then reference A.c_row, causing it to load C.
>>>> 
>>>> Then, separately, load C, eagerloading 'a_row.b_row'.
>>>> At this point, I expunge_all() and demonstrate that b_row was not
>>>> attached to C.a_row.
>>>> 
>>>> This does not occur if C's relationship to A is lazy='select'.
>>>> Weirdly, this also does not occur if the initial load of A does not
>>>> eagerload 'd_row'.   I'm not sure why that should affect anything.
>>> 
>>> This will make it pass:
>>> 
>>> assert 'a_row' in a_obj.c_rows[0].__dict__
>>> session.expire_all()
>>> 
>>> c_obj = 
>>> session.query(C).options(eagerload_all('a_row.b_row')).filter_by(id=1).one()
>>> session.expunge_all()
>>> 
>>> assert c_obj.a_row.b_row
>>> 
>>> note after load #1, c_obj is already in the Session, and c_obj.a_row is 
>>> already populated (looking in __dict__ is always the way to see if 
>>> something is already loaded).    This is because of the lazy=False on 
>>> C.a_row.
>>> 
>>> Then what happens in the load, and it occurs on line 2587 of mapper.py in 
>>> the current tip, we get the C object already in the identity map during the 
>>> second load.   We say, OK C do you have any attributes that aren't 
>>> populated which we can pull from this row ?   C says, "nope".   C.a_row is 
>>> already there.   This process currently doesn't descend further into the 
>>> objects attached to C.a_row so the rest of the columns are thrown away.
>>> 
>>> It was actually somewhat of an innovation around 0.5 or so when I actually 
>>> got the thing to populate "unloaded" attributes on objects that were 
>>> otherwise loaded and might even have pending changes, which was a big step 
>>> forward at that time, I didn't take on trying to figure out if eagers could 
>>> keep on going into the graph and find deeper attributes that aren't loaded.
>>> 
>>> If you have an opinion on this, let me know, right now I feel like its in 
>>> an OK place considering the tradeoff of digging way down into a graph which 
>>> may be unnecessary for those rows that were already loaded, many-to-ones 
>>> are usually not an issue since they pull from the identity map.      If the 
>>> issue is you're going for "detached" behavior, I generally don't recommend 
>>> relying heavily on object graphs that are fully traversable in the detached 
>>> state unless you're doing some kind of offline caching.   Of course, if 
>>> there were a patch to that area of code that successfully kept the 
>>> traversal going deeper into already loaded nodes based on the current 
>>> eagers present, I'm open to evaluating it, though it doesn't seem like a 
>>> quick tweak at the moment.
>>> 
>>> Nice test though, if you're interested in helping with tests/patches we're 
>>> always looking for help.
>>> 
>>> 
>>> 
>>> 
>>> 
>>>> 
>>>> --
>>>> You received this message because you are subscribed to the Google Groups 
>>>> "sqlalchemy" group.
>>>> To post to this group, send email to sqlalchemy@googlegroups.com.
>>>> To unsubscribe from this group, send email to 
>>>> sqlalchemy+unsubscr...@googlegroups.com.
>>>> For more options, visit this group at 
>>>> http://groups.google.com/group/sqlalchemy?hl=en.
>>>> 
>>> 
>>> --
>>> You received this message because you are subscribed to the Google Groups 
>>> "sqlalchemy" group.
>>> To post to this group, send email to sqlalchemy@googlegroups.com.
>>> To unsubscribe from this group, send email to 
>>> sqlalchemy+unsubscr...@googlegroups.com.
>>> For more options, visit this group at 
>>> http://groups.google.com/group/sqlalchemy?hl=en.
>>> 
>>> 
>> 
>> -- 
>> You received this message because you are subscribed to the Google Groups 
>> "sqlalchemy" group.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> To unsubscribe from this group, send email to 
>> sqlalchemy+unsubscr...@googlegroups.com.
>> For more options, visit this group at 
>> http://groups.google.com/group/sqlalchemy?hl=en.
>> 
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> To unsubscribe from this group, send email to 
> sqlalchemy+unsubscr...@googlegroups.com.
> For more options, visit this group at 
> http://groups.google.com/group/sqlalchemy?hl=en.
> 

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to