-->
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.  [ 8 posts ] 
Author Message
 Post subject: IsolationLevel SQL server
PostPosted: Mon May 21, 2007 6:22 am 
Newbie

Joined: Mon Jul 18, 2005 9:04 am
Posts: 11
Hibernate version: 1.0.4
Name and version of the database you are using: SQL Server 2005

Hi,

I understand that Locking isn't working for SQL server 2005 and 1.0.4, so I was trying t work around it by setting the isolationlevel to RepeatableRead.
Basically, the effect that I want to achieve is that I want to "lock" an object from when it is read untill it is saved so that an seperate session cannot update this at the same time, therefore to avoid stale object exceptions.

I have the following test code:

Code:
        static void Main(string[] args)
        {
            Thread t = new Thread(new ThreadStart(SecondThread));

            ISession session = SessionFactory.Instance.GetSession("TestConsole 1");
            Console.WriteLine("Start transaction");
            session.BeginTransaction(IsolationLevel.RepeatableRead);
            MyObject b = (MyObject)session.Load(typeof(MyObject), 12);

            t.Start();

            Console.WriteLine("Sleeping ...");
            Thread.Sleep(2000);
            Console.WriteLine("Finished Sleeping");

            b.MyProperty--;
            session.SaveOrUpdate(b);
            Console.WriteLine("Commiting transaction 1");
            try
            {
                session.Transaction.Commit();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.WriteLine("Commited transaction 1");


            if (!t.Join(60000))
            {
                t.Interrupt();
                t.Join();
            }

            Console.WriteLine("End");
            Console.ReadLine();
        }

        static void SecondThread()
        {
            Console.WriteLine("Start transaction 2");
            ISession session = SessionFactory.Instance.GetSession("TestConsole 2");
            session.BeginTransaction(IsolationLevel.RepeatableRead);
            MyObjectb = (MyObject)session.Load(typeof(MyObject), 12);
            b.MyProperty++;
            session.SaveOrUpdate(b);
            Console.WriteLine("Commiting transaction 2");
            try
            {
                session.Transaction.Commit();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.WriteLine("Commited transaction 2");
        }


So basically, I try to emulate the followng scenario:

- Thread A is loading an object (id 12) because it needs to do some updating.
- Thread B is planning to do the same
- I want thread B to wait till thread A is finished.

I thought the transaction Isolation level would do this as it would hold a read and write locks until the end of the transaction.

However, when I try this, I get an NHiberante.ADOException because it has created a deadlock.
"Transaction (Process ID 51) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction."

Does anybody have any suggestions?

Thanks,

Dries.


Top
 Profile  
 
 Post subject: Re: IsolationLevel SQL server
PostPosted: Mon May 21, 2007 8:12 am 
Senior
Senior

Joined: Thu Feb 09, 2006 1:30 pm
Posts: 172
driesie wrote:
Hibernate version: 1.0.4
I understand that Locking isn't working for SQL server 2005 and 1.0.4, so I was trying t work around it by setting the isolationlevel to RepeatableRead.
Basically, the effect that I want to achieve is that I want to "lock" an object from when it is read untill it is saved so that an seperate session cannot update this at the same time, therefore to avoid stale object exceptions.


I'm a little confused. You are trying to prevent stale object exceptions? I have to assume there are not many users for you system. Not allowing one user to search for an object while another is searching for the same objects doesn't seem very scalable. If there aren't many users, then the chance for a stale object exception seems very low.

That being said, what is the isolation level on your connection? You can set the isolation across the connection for each session, so if your connection is also repeatable read I could see that you could never read that object again until the connection is closed. Perhaps you should try closing the session after committing the transaction to see if that affects your results.


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 21, 2007 9:09 am 
Newbie

Joined: Mon Jul 18, 2005 9:04 am
Posts: 11
Thanks for your response.

The problem isn't really searching. The problem is one particular case where we know multiple users could be updating at the same time. So what we like to achieve is that the updates get queued. So user A reads MyObject with id 12, because it will do an update to it. At the same time user B wants to do the same thing, so I would like for B to wait reading untill the transaction for A has completely finished.
I hope this makes sense ;)

The transaction isolation leven for the connections are left at the default, only in this specific case do we want to change it to repeatable read.

The connection will get closed immediately after the transaction has finished, our framework takes care of this, but I didn't add it in the example. I changed the example to do this, but we get the same results.


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 21, 2007 12:08 pm 
Senior
Senior

Joined: Thu Feb 09, 2006 1:30 pm
Posts: 172
driesie wrote:
The problem isn't really searching. The problem is one particular case where we know multiple users could be updating at the same time. So what we like to achieve is that the updates get queued. So user A reads MyObject with id 12, because it will do an update to it. At the same time user B wants to do the same thing, so I would like for B to wait reading untill the transaction for A has completely finished.
I hope this makes sense ;)


What type of application is this? What architecture are you using? Is this a web application or a windows forms application? When do you create new sessions?

If you create a new session, then load the object then apply changes and flush, this will not do you any good.

If you are using a session to load the object, expose that object to the user and then save the values then I can see where this architecture comes from, but you could expose yourself to a lot of issues in locks and deadlocks.

If you are using the first solution, then I would say to handle the stale objects yourself. At the time when the user goes to save check the version yourself, or catch for an exception, explain what happened to the user in a clear way and then refresh the object for them to see if they still need to make their changes.

More details on the architecture may help give another recommendation.


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 21, 2007 12:48 pm 
Newbie

Joined: Mon Jul 18, 2005 9:04 am
Posts: 11
The architecture is quite complicated, but in short: a windows service with 2 different web servers connecting to via remoting, and specific transactions coming in via a message queue (IBM MQ)

A "chain of commands" infrastructure creates a session for "single units of work" including converting our domain objects into DTO object and visa versa (i.e load some data, load data then update properties from DTO then save ...).

In general, we are happy with the default concurrency handling (i.e. dealing with stale object exceptions), but there is one specific scenario (when saving a specific object on a specific occasion after updating one specific property of that object) which we must deal with differently by waiting to load the object in case somebody else is updating it (the other thread will be updating different properties, we know that for sure), and returning a "message to the user" isn't an option nor necessary as we know the 2 users will be updating different things.

Basically, for now, we can get away with doing the locks ourselfs using the "CreateSQLQuery" functionality. In the above example, instead of:

Code:
MyObject b = (MyObject)session.Load(typeof(MyObject), 12);


We have something like this (in reality in a different command in the chain, but for ease of example):
Code:
MyObject b = (MyObject)session.CreateSQLQuery(
                    "SELECT {MyObject.*} FROM MyObject{MyObject} WITH(UPDLOCK, ROWLOCK) WHERE MyObject.id=12",
                    "MyObject", typeof(MyObject)).UniqueResult();


This works, but seems such an ugly hack as we're embedding raw SQL.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 13, 2008 3:29 am 
Newbie

Joined: Thu Oct 06, 2005 5:26 am
Posts: 17
FYI I've been looking at a deadlocking problem using Hibernate against MS SQL server. Basically, we have lots of threads performing inserts, deletes and updates. With enough threads we will get a deadlock. We found this is due to row locking not being performed. We had clustered indexes and in order to tell SQL server to use row locking we needed to specify WITH (ROWLOCK) in the statements

As a test I altered the Hibernate source to insert the "WITH (ROWLOCK)" into the insert, delete and update statements. We no longer get any deadlocks.

I'm investigating whether there are other solutions as creating our own SQL dialect in Hibernate will introduce other issues around the loading of the correct Hibernate classes.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 13, 2008 6:28 am 
Senior
Senior

Joined: Thu Feb 09, 2006 1:30 pm
Posts: 172
homeworkjunkie wrote:
I'm investigating whether there are other solutions as creating our own SQL dialect in Hibernate will introduce other issues around the loading of the correct Hibernate classes.


If you're using Sql Server 2005, the easiest fix would be to use Row versioning.

Alternatively, there may be a way to control lock escalation besides just lock hints. I'm not actually sure about this one, but I could believe that a setting would exist.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 17, 2008 6:22 am 
Newbie

Joined: Thu Oct 06, 2005 5:26 am
Posts: 17
The problem with row versioning is that if the data is changed in the database then you get the following errors:

"[SQL Error: 3960, SQLState: S0002] Snapshot isolation transaction aborted due to update conflict. You cannot use snapshot isolation to access table '<table>' directly or indirectly in database '<db>' to update, delete, or insert the row that has been modified or deleted by another transaction. Retry the transaction or change the isolation level for the update/delete statement."

Using explicit row locking, via clustered indexes and the ROWLOCK locking hint, no errors or deadlocks occur.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 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.