views:

873

answers:

6

In this asp.net I'm cleaning up it's possible for deadlocks to occur. I want to make sure that the code deals with them properly, so I'm trying to write NUnit tests which trigger a deadlock.....

The DAO is split by entity. Each entity has a set of tests which are surrounded by Startup() and Teardown() methods which create a transactionscope and then roll it back after the tests are complete. This works great for everything else, but is completely useless for deadlocks.

How can I setup and run a "deadlock" test using TransactionScope and SQL2000 (ie MSDTC is involved) that can be reliably reproduced? More detail: I know there is a situation whereby if two users call two functions with different, specific, data values then a deadlock can result. How can I simulate this within NUNIT - and make the deadlock always happen?

And yes, I did start with the "Why don't you stop the deadlocks happening in the first place" plan of action, but I have no control over the code where the deadlocks can occur - I just call the functions and they can deadlock.

A: 

What if one of your tests in the middle of your transaction just does a "wait" for like 5 minutes? Or, you simply write a test that starts a transaction, creates a new record, and then updates that record without committing. Then, start a new transaction in and try to read that record that was created and is currently being updated. You'll get a deadlock there.

Nick DeVore
Creating two transactions in the same thread can't produce a deadlock using TransactionScope. If I create one txn, then create another without committing the first, the second one just becomes nested inside the first.....
Dave R
A: 

What if you manually locked a table and ALWAYS left it locked? Then, any action you took against that table would produce a deadlock situation?

Nick DeVore
"Manually" creating a transaction using MSDTC isn't simple. I can begin a transaction in Query Analyzer, sure, but that isn't a Distributed transaction and all that happens is that it locks-out the MSDTC transaction from doing any database work until I "Rollback" in Query Analyzer.
Dave R
A: 

Coming at this blind, but is it possible to in your TestSetup method to actually create a SQLConnection to your database? Then, using that, you could just issue either a command to lock the table or take some action to lock a record or page? That way it would be outside of any other transactions you've got going on? It seems like this would be an option you've already considered though. What am I missing about your situation?

Nick DeVore
I think a pre-requisite for creating a deadlock is going to be two separate processes both issuing transactions. Creating "artificial" locks within one process doesn't force a deadlock - it forces a "wait" for the resource to become free. I was hoping for a way to simulate this in NUNIT.
Dave R
I don't see a way to do it outside of forcing it manually. Now, that could mean you force it manually inside of NUnit, but you're still forcing it manually. You would simply have to create the circumstance that generates the deadlock scenario.
Nick DeVore
+2  A: 

If your deadlock results in an exception being thrown, you want to use a Mock Object to emulate the exception being thrown.

The basic idea is that you tell your Mock Object framework (I like TypeMock) to throw an exception instead, something like this:

MockObject mo = MockManager.MockObject(typeof(MyDeadlockException));
mock.ExpectAndThrow("MyMethod", (MyDeadlockException)mo.Object);

The idea is basically the same for other mocking frameworks.

Josh Kodroff
I like this idea. I have an idea of Mock frameworks, but haven't ever used one. I am assuming that an "ExpectAndThrow" method would actually cause the exception to be thrown, so my code could catch it. I'll look into this further.
Dave R
That's exactly what it does. I haven't used it, but if you've got budgetary concerns, Rhino.Mocks is free: http://ayende.com/projects/rhino-mocks.aspx
Josh Kodroff
A: 

For unit testing you probably want to avoid actually using a database. How do you know you have a deadlock. You should be testing the condition that tells you there is a deadlock and creating that in your test.

A mock is an ideal whay to mimic that if you call a service and it returns an error. Simply have the mock return the error you are expecting. If you are waiting for a timeout or something then the same thing applies.

In general a unit test should only run on the code being tested and not rely on any other code or components. That said - databases are essentially another component and you are probably doing some sort of functional tests using nunit to drive them.

In that case you really have to create a deadlock situation but locking a record, or table and then calling a component that tries to use the same record and handling the response.

Brody
"In that case you really have to create a deadlock situation but locking a record, or table and then calling a component that tries to use the same record and handling the response."That describes blocking, not deadlocks.
Pittsburgh DBA
I know we can get deadlocks because the unhandled erorrs log of the current version of the live app has them :)I understand UnitTests shouldn't normally test a DB, but I have setup a UnitTestDB specifically for testing which doesn't change state.
Dave R
+1  A: 

Most of these solutions involve multiple threads. Here is one that does not.

Close these Loopholes - Reproduce Database Errors

The author is Alex Kuznetsov.

Pittsburgh DBA
I'm not sure how using a custom deadlocker class and a test which calls the deadlocker class can help me test for deadlocks in a specific call to my existing asp.net app? Interesting article though.
Dave R