views:

470

answers:

8

I have a VB6 application accessing a single table on a MSSQL2000 server via ADO. I'm using read-only access (adOpenStatic, adLockReadOnly) There are other applications in the network which do make changes to the table.

For some reason I'm getting errors about my application being chosen as a deadlock victim.

I'm really confused: Why can there be a deadlock when I'm just reading from a single table? I'd expect timeouts, because of the writing of the other applications, but not a deadlock...

Can someone shed some light on this?

UPDATE: 2009-06-15 I'm still interested in a solution to this problem. So I'm providing some more information:

  • It makes no difference if I choose adOpenForwardOnly or adOpenStatic
  • It makes no difference if the cursor position is client or server.
A: 

Wouldn't it be something like this?

Other Application: Write to table (acquire write lock on table)

Your Application: Read from table (acquire read lock on table, can't due to write lock).

Martin Clarke
In my experience that would only block my application and eventually produce a timeout
DR
Not used it myself, but apparently trace flag 1204 on SQL server will help you determine where the deadlock is happening. See http://msdn.microsoft.com/en-us/library/ms178104.aspx
Martin Clarke
A: 

It depends on both application's behavior. your app can surely wait on the other to release resources.

Harnod
Yes that would be the normal behaviour I observed in many other applications: blocking and perhaps a timeout.
DR
A: 

Reads can still incur locks, in order for the DB to ensure that a write isnt done in the middle of a non-atmic read. In other words the read lock ensures that you get an accurate consistent snapshot of whatever data you are slecting.

Visage
A: 

A deadlock refers to a condition when two or more processes are waiting for each other to release a resource, or more than two processes are waiting for resources in a circular chain. Sure you can create a deadlock with read-only access because the read will NOT wait.

There is a nice explanation about the deadlock conditions at the wikipedia

Luixv
"Sure you can create a deadlock with read-only access because the read will NOT wait" - that doesn't make sense to me.
Mitch Wheat
A: 

Do you get the same behaviour with adOpenForwardOnly ?

You might want to check that your SQL Server statistics are up to date. Or you could get your DBA to rebuild all indexes. Many locking problems are due to out of date statistics/indexes.

Mitch Wheat
I can't reproduce the problem at will, I have to wait and see...
DR
It makes no difference :(
DR
...and you've checked that all indexes/statistics are up to date?
Mitch Wheat
+1  A: 

It is possible for a single SELECT statement to deadlock against a single UPDATE or DELETE statement due to the presence of a non-clustered index, consider the follwing scenario:

The reader (your app) first obtains a shared lock on the non-clustered index in order to perform a lookup, and then attempts to obtain a shared lock on the page contianing the data in order to return the data itself.

The writer (other app) first obtains an exlusive lock on the database page containing the data, and then attempts to obtain an exclusive lock on the index in order to update the index.

You can find more information on this (and other) type of deadlock in the Microsoft KB article Q169960 (http://support.microsoft.com/kb/q169960/)

Also you might want to take a look on Google on how to obtain deadlock trace information (trace flag 1222) - this will report on exactly what SQL statements are conflicting over what objects whenever a deadlock occurrs. This is a fairly decent looking article - http://blogs.msdn.com/bartd/archive/2006/09/09/747119.aspx

Kragen
As far as I understand trace flags are global, i.e. they effect every connection. As the problem only occurs on the production server I have to wait for a time frame where I can do that without risking any problems.
DR
Yes they are global, however there is very little performance impact of the extra logging.IMO its a good idea to leave this flag enabled on production servers so that you are sure to capture deadlock information whenever they occur (deadlocks are difficult to reproduce), I'm not a DBA however...
Kragen
+1  A: 

I think there are a number of possibilities in the answers already provided here. Since you only take shared locks, the deadlock can't be due to lock escalation, and must simply be acquiring locks that are incompatible with those acquired in another process, and acquiring those locks in a different order...

Your shared locks are incompatible with another process taking exclusive locks. The scenario might run something like this...

  1. You take shared lock on resource A
  2. Other process takes exclusive lock on resource B
  3. Other process tries to take exclusive lock on resource A, and blocks waiting for you to release your shared lock on A.
  4. You try to take shared lock on resource B, and would block waiting for the other process to release its exclusive lock on B, except that you're now in a deadlock situation, which is identified by the server and it chooses a process to kill.

N.B. deadlocks can have more players than just 2. Sometimes there's a whole chain of interwoven activity that results in a deadlock, but the principle is the same.

Often, if multiple applications access the same database, there is a DBA that manages all access via stored procedures, so he can ensure resources are always locked in the same order. If you're not in that situation, and the other applications use ad-hoc SQL statements you'd have to inspect their code to find out if they might conflict with your app in the way I've described. That doesn't sound like fun.

A pragmatic solution might be to catch the error when your transaction is killed as a deadlock victim, and simply re-try the transaction several times. Depending on how much activity the other apps are generating, you might achieve acceptable results this way.

Martin