On May 10, 2011, at 12:48 PM, Luper Rouch wrote:

> I often run into problems when working with SA's events.
> MapperExtension's methods are called in the middle of a flush, and so
> there are things to take care of. For example:
> 
>  * if you add new objects to the session, or do an update during an
> event, you'll have to call session.flush() a second time for them to
> be actually inserted or updated (and you can't call flush() within the
> event since you're in the middle of a flush). There is a workaround
> for this issue, you use a SessionExtension that looks for dirty
> objects in its after_flush_postexec() method, and call flush() a
> second time if there is any [1].
> 
>  * if an error occurs during an event (e.g. a database timeout), you
> can't handle it within the event with a rollback or else you'll get an
> "InvalidRequestError: The transaction is inactive due to a rollback in
> a subtransaction. Issue rollback() to cancel the transaction." error
> on the next usage of the transaction [2].
> 
> While the first problem just involves a quick hack (which you still
> have to figure out), the second is more annoying and involves coupling
> (you have to know that the particular table you are flushing triggers
> events and needs special error handling).
> 
> I think it would make sense to add new events triggered *outside* of
> the flush mechanism. For example the order of operations for an update
> would look something like this :
> 
> outer_before_update()
> [begin]
> before_update()
> [emit SQL update]
> after_update()
> [commit]
> outer_after_update()
> 
> This way you would code normally in events, as you do in the rest of
> your code, without taking care of special rules.
> 
> Maybe MapperExtension is not the right place to add these kind of
> events (as I see it, it's more a tool to extend the API than a day to
> day tool to work with events), and something like MapperEvents should
> be created instead ?

You should be using SessionExtension for most of these use cases.   As the docs 
state you can't change the flush plan inside of before_update()/before_insert() 
kinds of events, you do that in before_flush().   There's a number of examples 
in examples/ and on the wiki which illustrate this usage.   Also you should 
probably be moving up to 0.7 where the events system has been vastly improved.  
In particular there is an ample space for us to add new functionality to 
listen() that will allow the "check the dirty list" part of session event 
handling, and similar, to be automated by SQLAlchemy itself.

The session doesn't support the "try something, it fails, rollback, keep doing 
the flush" kind of pattern.   There's info in the FAQ regarding the rationale 
here.  You'll have to approach that in a different way, typically using 
begin_nested() to work with SAVEPOINT and working outside the scope of flush(). 
   This is something that should be dealt with at the point of integration with 
your app and not within the Session itself (or at least, a custom Session 
subclass).   A "database timeout" though is outside the scope of all of that - 
if you lose your connection, your transaction is gone.  You need to run the 
whole operation again from the start.  Typically its better to prevent timeouts 
from the start (depends on why you think you'd be getting timeouts) or to take 
an optimistic approach (i.e. failures are rare, they happen, the app recovers 
on the next try).    

The purpose of events is to provide hooks into points inside of SQLAlchemy's 
internal processes that would otherwise be impossible.   Adding events outside 
of its internal processes just to suit various use cases begins to turn it into 
a framework, which is not what SQLAlchemy is.


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

Reply via email to