On Jan 9, 2014, at 7:17 PM, Weikai Xie <xiewei...@gmail.com> wrote:

> Hi, folks,
> 
> I observed that it seems all ORM objects  in a session will expire 
> immediately if there is a failure in session.flush(). I was wondering what's 
> the rationale behind this behavior.
> 
> Following is an artificial web application code to illustrate the idea:
> 
> def PUT():
>        ....
>       # session and engine are all created with default parameter
> 
>         user = session.query(UserProfile).first()
>        
>         # deliberately tries to insert a Login record which has the same 
> primary key as an existing one such that the later session.flush() will fail
>         try:
>            duplicated_login_record = Login(id=0, username="username:weikai")
>            session.add(duplicated_login_record)
>            session.flush()   
>         except IntegrityError:
>             session.rollback() 
> 
>         print user.id   
>         ...
> 
> By turning on logging, I noticed the "print user.id" line always result in a 
> new SELECT being issued to database, which is an indicator that somehow user 
> object got expired after the failure in session.flush().
> 
> For some other good reason which I don't want to elaborate here, we really 
> don't want to have an extra SELECT going to DB in the processing of one HTTP 
> request. Therefore, I was just wondering why is this behavior and whether it 
> can be turned off. 

The short answer is that this behavior can’t be disabled across the board, 
unless you are using SAVEPOINTs, which you probably should here.  The flush() 
process does a rollback() immediately on the inner transaction when an 
exception occurs, and once the transaction is rolled back all data is expired; 
as the objects are proxies for the state of the database, and the transaction 
has been rolled back, the state of the objects is invalid.

I do notice that you’re catching an IntegrityError.    The typical pattern when 
writing code that wants to catch these and then continue is to run the 
individual set of questionable operations within a SAVEPOINT, that is a 
begin_nested()/commit() block.  Recent versions of SQLAlchemy have the behavior 
such that when a flush() exception rolls back the SAVEPOINT, objects that were 
not modified within the SAVEPOINT are not expired; only those objects that 
changed within the save point’s scope do.

There’s also an option called _enable_transaction_accounting which if set to 
False will disable *all* rollback/commit accounting, basically reverts to 
pre-0.5 behavior.  Using this flag, you’re giving up a lot of the Session’s 
functionality and as this mode is not really supported I’m not sure what other 
useful features you might be giving up.



> 
>  
>         
> 
>  
> 
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to sqlalchemy+unsubscr...@googlegroups.com.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> Visit this group at http://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/groups/opt_out.

Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

Reply via email to