On Mar 8, 2012, at 4:45 PM, zz elle wrote:

> Hi everyone,
> 
> I join an application dev team in order to solve 2 main problems with SA on 
> an application that use SA the wrong way.
> 
> 
> Indeed the dev team did not understood that sessions are "ad-hoc" (as said by 
> Mike if i remember well). 
> So they get DetachedInstanceError sometimes on lazy loads.
> First some detached errors were solved using joinedload(_all) in SA queries.
> Secondly they discover that scoped_session solve every errors, indeed 
> scoped_session provide session alive for the life of a thread so SA objects 
> never get detached from their session ... 
> Except when you get an SA object with some query in a thread T1, start an 
> asynchronous thread T2 with the object as argument and T1 ends (so T1 session 
> will be destroyed by python gc) ...
> And you get new detached errors.

when you transfer an object to a new thread, you do this:

def im_receiving_an_object_from_another_thread(obj):
   obj = my_threads_session.merge(obj)


that is, use merge() to transfer the state of the object to the session that's 
local to the thread.


> 
> The application i am currently speaking is about more >100k code lines, so i 
> can not change the way the application use SA.
> Is there an elegant way to catch silently DetachedInstanceError and merge the 
> associated object to an alive session ?

OK, guess you knew about merge().   erm, might be tricky.   merge() returns a 
new object in the first place, as the given object might still be related to 
the session from the other thread, and you'd get some nasty concurrency-related 
issues.     So even if there were some event that could link to that new 
session, the calling code would need to get a hold of the newly merged object 
as the thing it should be dealing with.

I'd maybe give a second look to actually fixing the code, unfortunately.    
Whenever you see objects being passed to another thread would be where you want 
to do the merge.  You can even make a decorator that does it, like :

@merge_objects("a", "b", "c")
def run_some_code(a, b, c):

@merge_objects() is given the string names of the arguments it should be 
merging, pulls them from the argument list (probably using inspect() to 
reconcile the names in merge_objects() with how they should be received/passed 
to the function) and passes merged versions to the function.    


> 
> One elegant way i think of to solve the problem would be to wrap SA object 
> into a higher level object which handles session troubles (detached case, 
> thread/scope session).

that would be very involved.  The object graph is a nasty beast.

> Currently i implement some PoC by overloading the query_cls but i was only 
> able to proxify SA objects "directly" returned by the query.
> I mean in the following query User SA object would be wrapped but not SA 
> objects listed in user.roles.
> session.query(User).option(joinedload(User.roles)).all()
> Is it the good class to overload ?
> Is it better to overload lazyloader (if it's possible) ?

OK maybe overriding query() you could trick it into using the "correct" 
session, still, its quite sloppy that the calling code would be dealing with a 
mix of objects pointing to two different sessions.    A more explicit approach 
like the decorator might lead to clearer results in the end.  Or maybe not.   
You'd need to experiment quite a bit.


-- 
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