Deadlocks: Share/Insert Lock Deadlocks, With a Twist

Content rating: intermediate/tips.  We'll do an intro deadlocking blog sometime soon.

I had a bear of a deadlocking issue last week, and reached out to my SQL Tweeps for help. Jonathan Kehayias (blog, SQLSarg on twitter) responded with email, repros, and solutions.  I was trying to think of a funny way to say thank you, or thumbs up, in a MidnightDBA way…but I couldn't come up with anything better than "Thanks, Jonathan….that's one full moon up for you!"  And that's just lame, and a little bit surreal in the Yo Gabba Gabba way, so we'll leave it at, thanks SQLSarg!

Through profiler traces capturing deadlock graphs, I was able to narrow the deadlock down to a very, very specific piece of code – a stored procedure called UpdateOrder was deadlocking on an insert to the [Order] table.  The deadlock graph (images forthcoming) showed Owner Mode Range S-S, and Request Mode Range I-N locks on the primary key for that table.

Do a search for "Range I-N" in books online, and you'll get the article on Key-Range Locking. The first thing that leaps out at you is that these are associated with the serializable isolation level.  Now, my database uses the default READ COMMITTED isolation level, and the stored procedure itself wasn't specifically set for Serializable, so I was very confused.  The solution to the first mystery: this SP is called as part of a BizTalk process, and BizTalk not only applies Serializable to all the code it calls, but it encapsulates it in a transaction.  That'll be important a little bit later.

In the meantime, we're stuck with Serializable. That increases contention, but it shouldn't cause a deadlock by itself, so now we take a closer look at the code.  This is somewhat simplified:

UPDATE  @tmp   
SET     inOrder = 1   
FROM    @tmp tp    
INNER JOIN [Order] op ON op.OrderID = @BookingNumber 
                          
             
INSERT  INTO [Order]   
                ( PolicyName ,   
                  Text             
                )   
                SELECT DISTINCT   
                        @BookingNumber ,   
                        tp.[Message]   
                FROM    @tmp tp   
                WHERE   tp.inOrder= 0    
 

The RANGE I-N lock clearly comes from the INSERT statement, but what about the RANGE S-S? If we look closely, we'll see that the UPDATE statement doesn an inner join on the [Order] table, which is to say, we're SELECTing from [Order] via that join.  When we run two or more processes running this stored procedure, both procedures get shared locks for the JOIN [Order] statement, and they request insert locks (Range I-N) for the insert statement. Each proces is in one container transaction, so it can't let go of the shared lock before it also gets the insert lock.  Boom, deadlock.

The three suggested solutions, in order of preference, are

  1. Change the isolation level from serializable to Read Commited.  I can't do that, since BizTalk is enforcing Serializable. Next option….
  2. Move any pre-Insert SELECT (in my case, the update with the inner join) outside of the transaction that is performing the INSERT.  This doesn't apply to me, also courtesy of BizTalk. Remember that encapsulating transaction we can't get out of? Yeah. So what we gotta do is….
  3. Use an UPDLOCK hint.  To quote Jonathan, the UPDLOCK hint will "increase the lock type to an update lock from a shared lock which blocks the other executions of the stored procedure until the transaction commits". 

UPDATE  @tmp   
SET     inOrder = 1   
FROM    @tmp tp    
INNER JOIN [Order] op WITH (UPDLOCK) ON op.OrderID = @BookingNumber 
                          

Tah-dah! No more deadlocks.  The update lock prevents any other process from getting their mitts on [Order] long enough to finish this update and the insert.

-Jen McCown

http://www.MidnightDBA.com

 

 

 

Need Speakers & Sponsors for Dallas SQL Saturday!

We’re holding our first SQL Saturday event in Dallas on May 22, 2010. 

I'm in charge of getting this great event sponsored, so if your company (or one you know) is interested in sponsoring, shoot me an email at Jen@JenniferMcCown.com and I'll get you the details double-quick. We're expecting over 400 attendees (and I wonder if we're underestimating), so it's a great opportunity for some excellent IT exposure.  Of your company. And product. Not you. You know what I mean.

Sean is in charge of speakers, and he's sent out the official call: If you're going to be in Dallas, or would like to be, on May 22, then send an abstract directly to Sean at KO@KenpoSecrets.com by Feb 15 and we’ll get you going.

On a personal note, I've never been involved in planning something this big, and this full of win. There's a lot of work that goes into it, but it's also really cool to see a big event start to come together on paper.  And believe me, kids…SQL Saturday #35 is alreay chock full of win!  I'm really excited…this will bring Dallas a lot of free training, networking, and swag.  It'll be great for the user group, too: we're really ramping up for 2010 overall, with some cool NTSSUG events in addition to SQL Saturday.  Hint: The January user group meeting should be wicked cool…

More details to come, so watch this space, the N Texas User Group webpage, and and the official SQL Saturday #35 page at SQLSaturday.com!  Your homework:

  1. Get me in touch with your company for SQL Saturday sponsorship info – Jen@JenniferMcCown.com
  2. Get your abstract together and send it to Sean – KO@KenpoSecrets.com
  3. Register for SQL Saturday #35
  4. RSVP and mark your calendar for the January North Texas User Group meeting (it's on on January 21).

Bonne beurre, 

Jen McCown

http://www.MidnightDBA.com

Find Deprecated SQL Code Using Profiler

I stumbled across something really cool when I was reading through the BOL entry on sp_trace_setevent*, which gives a list of all events and columns available to trace.  This is, by the way, an extremely useful entry to have bookmarked.

There are two events I hadn't really noticed before: number 39, Deprecated, and  125, Deprecation Announcement.  "Deprecated" clearly tells you when a deprecated (that means phased out, kids) feature is used on the instance.  And Deprecation Announcement "Occurs when you use a feature that will be removed from a future version of SQL Server."

That is SO insanely useful, I might throw a deprecation party. We'd all have to drink TAB and wear really old clothes, or something.  (Get it? Because they've gone out of style? Ha! I made a funny!)  

When you begin to look at upgrading SQL Server, this is a great way to start to track down code that will break after the upgrade.  Set up a small server side trace, with just the Deprecated, Deprecation Announcement, and Existing Connections events, and let it run for a while – maybe a day, or a week, depending on how fast the trace file grows.  Then start your lists of code to update.

* By the way, you use sp_trace_setevent when you set up a server side trace.  Definitely see my SQLServerPedia article on server side trace, and the accompanying video on MidnightDBA.com.

-Jen McCown

http://www.MidnighDBA.com