These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: Bizarre Transaction / Rollback Behavior 1.2 ODP.net Oracle10
PostPosted: Fri Nov 16, 2007 5:34 pm 
Newbie

Joined: Thu Oct 02, 2003 2:38 pm
Posts: 7
Location: Charlotte, NC
I have been testing forcing a rollback, after noticing some odd data left behind after debugging.


    Why would *not* closing/Disposing the session here end up committing a transaction that I have explicitly rolled back?

    Eventually, (in the End_Request) the following will close the session, but how would this cause a commit of work that had been previously rolled back?
    session.Flush();
    session.Close();

This commits the work:
Code:
            ISession session = NHibernateHttpModule.CurrentSession;
            using (ITransaction tx = session.BeginTransaction())
            {
                try
                {
                    // Do some work
                    session.Save(..);
                    tx.Rollback();
                }
                catch (Exception e)
                {
                    tx.Rollback();
                    Console.Write(e);
                    throw;
                }
            }



This appropriately rolls back the work (added using around the session) :
Code:
            using(ISession session = NHibernateHttpModule.CurrentSession)
            using (ITransaction tx = session.BeginTransaction())
            {
                try
                {
                    // Do some work
                    session.Save(..);
                    tx.Rollback();
                }
                catch (Exception e)
                {
                    tx.Rollback();
                    Console.Write(e);
                    throw;
                }
            }



    Top
     Profile  
     
     Post subject:
    PostPosted: Fri Nov 16, 2007 6:06 pm 
    Hibernate Team
    Hibernate Team

    Joined: Tue Jun 13, 2006 11:29 pm
    Posts: 315
    Location: Calgary, Alberta, Canada
    Once you rollback a transaction, you should immediately discard the session:
    http://www.hibernate.org/hib_docs/nhibe ... ion-commit

    It is very likely that you are still using the session after you rolled back the transaction. At that point, you are not inside a transaction anymore; that explains the garbage data that got left behind.

    For a web app, you should rollback or commit a transaction exactly once for each request. Create a thread-local variable and call it something like IsTransactionAborted and default it to false. If you need to rollback, set this to true. In End_Request, check this variable and commit or rollback accordingly.

    _________________
    Karl Chu


    Top
     Profile  
     
     Post subject:
    PostPosted: Sat Nov 17, 2007 12:25 pm 
    Newbie

    Joined: Thu Oct 02, 2003 2:38 pm
    Posts: 7
    Location: Charlotte, NC
    karlchu wrote:
    Once you rollback a transaction, you should immediately discard the session:
    http://www.hibernate.org/hib_docs/nhibe ... ion-commit

    It is very likely that you are still using the session after you rolled back the transaction. At that point, you are not inside a transaction anymore; that explains the garbage data that got left behind.

    For a web app, you should rollback or commit a transaction exactly once for each request. Create a thread-local variable and call it something like IsTransactionAborted and default it to false. If you need to rollback, set this to true. In End_Request, check this variable and commit or rollback accordingly.


    Thanks karlchu,

    What you are saying must be the case. I just find it odd that NHibernate does not do such a thing internally -- mark the session as un-commitable.

    In my test, once I rolled back the transaction, it was immediately disposed, then the session was flushed and finally closed. What you are saying is that since the transaction was disposed, the session flush operation required a transaction and since there was none, it assumed an auto-commit mode.

    It just seems NHibernate would want to prevent this within the same session. Most people would not expect flushing and closing a session which was already rolled back, to actually perform a commit.

    Maybe the real problem is with rollback? Shouldn't the rollback clear the session of pending statements to be executed?

    Something like this:
    Code:

    // some work
    tx.Rollback(); // internally clear any pending changes to the associated session
    tx.Dispose();
    session.Flush(); // nothing to flush
    session.Close();



    Top
     Profile  
     
     Post subject:
    PostPosted: Sat Nov 17, 2007 2:17 pm 
    Hibernate Team
    Hibernate Team

    Joined: Tue Jun 13, 2006 11:29 pm
    Posts: 315
    Location: Calgary, Alberta, Canada
    One usually does not need to call Flush() if he uses transaction. Flush() is useful inside a transaction when you want some changes to propagate to the database immediately (to force a trigger, for example) so that you can do a load on some other things that depends on the trigger having been fired. If I have to boil it down to a rule, it is this:
    Quote:
    Do not call Flush() outside of a transaction; unless you are not using transactions

    Here is a very typical block of code that you will see in many applications:
    Code:
    ISession s = OpenSession();
    ITransaction tx = s.BeginTransaction();
    try
    {
        Thing thing = s.Get<Thing>(thingId);
        // Change some property about the thing
        s.Save(new OtherThing());
        tx.Commit();
    }
    catch (Exception)
    {
        tx.Rollback();
        // Do whatever else you need to do
    }
    finally
    {
        // s.Flush();
        s.Close();
    }

    Note that the call to Flush() is commented out. This is perfectly acceptable. Consider the call to Flush() is now uncommented. Here is a possible sequence of events:
    1. Session is opened and transaction begun
    2. You loaded "thing" and made some changes to its properties. At this point, nothing is pushed to the database yet
    3. You created an "OtherThing" and saved it, but the save failed for some reason and an exception is thrown
    4. You arrived at the catch-block and the transaction is rolledback
    5. The finally-block is run and the session is flushed... uh-oh
    6. NHibernate sees that the "thing" has changed state, so it pushes the change to the database
    7. You close the session, and the "thing" is changed forever
    Ironically, had you called Flush() after you changed the "thing" (which is inside the transaction), the change would not have persisted.

    A much better and concise way to write the equivalent of the above code block is as follows:
    Code:
    try {
        using (ISession s = OpenSession())
        using (ITransaction tx = s.BeginTransaction()) {
            Thing thing = s.Get<Thing>(thingId);
            // Change some property about the thing
            s.Save(new OtherThing());
            tx.Commit();
        }
    }
    catch (Exception ex) {
        // Do what you need to do
    }

    If an exception is thrown inside the using blocks, the transaction is automatically rolled back and session is automatically closed. With a web app, you need to manage the session and the transaction carefully so that you either commit or roll back the transaction; and do not call Flush() afterwards.

    _________________
    Karl Chu


    Top
     Profile  
     
     Post subject:
    PostPosted: Tue Nov 20, 2007 10:16 am 
    Newbie

    Joined: Thu Oct 02, 2003 2:38 pm
    Posts: 7
    Location: Charlotte, NC
    Karl,

    Thanks for the thorough explanation! I'm sure others will find it very helpful.

    Sincerely,
    -Marc


    Top
     Profile  
     
    Display posts from previous:  Sort by  
    Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

    All times are UTC - 5 hours [ DST ]


    You cannot post new topics in this forum
    You cannot reply to topics in this forum
    You cannot edit your posts in this forum
    You cannot delete your posts in this forum

    Search for:
    © Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.