Nested TransactionScope

G

Guest

Hello,

I am trying to set up some nested transactions to speed up a group of unit
tests. The unit tests call a common method to build up some test data, which
takes about 30 seconds and then runs a specific test. Each C# unit tests has
a using statement that creates a new new
TransactionScope(TransactionScopeOption.Required, timeSpan) (timeSpan is 600
seconds). Data access uses the Patterns and Practices Enterprise Library 2.0
against a SQL Server 2005 database.

It all works fine but I would like to speed up my total test time and only
create the test data once. I added [ClassInitialize()] and [ClassCleanup()]
methods to run when my unit tests start and end. In the ClassInitialize
method I created a TransactionScope(TransactionScopeOption.Required,
timeSpan) and in the ClassCleanup I added a transactionScope.Dispose(); In
each test, I created a TranactionScop(TransactionScopeOption.RequiresNew,
timeSpan) both with a duration of 5 minutes. Now I am getting a timeout
error.

"Error retrieving data for the query from command with CommandText of xxx:
Timeout expired. The timeout period elapsed prior to completion of the
operation or the server is not responding." (where xxx is a select stored
procedure name)

If I replace RequiresNew with Required in the individual tests, the first
test works and subsequent tests fail, which makes sense because it appears to
join and roll back the outer ambient transaction.

Does anyone know why nesting I would get the timeout at about 30 seconds
with the RequiresNew?

Thanks,

David Morris
 
F

Frans Bouma [C# MVP]

David said:
Hello,

I am trying to set up some nested transactions to speed up a group of
unit tests. The unit tests call a common method to build up some test
data, which takes about 30 seconds and then runs a specific test.
Each C# unit tests has a using statement that creates a new new
TransactionScope(TransactionScopeOption.Required, timeSpan) (timeSpan
is 600 seconds). Data access uses the Patterns and Practices
Enterprise Library 2.0 against a SQL Server 2005 database.

It all works fine but I would like to speed up my total test time and
only create the test data once. I added [ClassInitialize()] and
[ClassCleanup()] methods to run when my unit tests start and end. In
the ClassInitialize method I created a
TransactionScope(TransactionScopeOption.Required, timeSpan) and in
the ClassCleanup I added a transactionScope.Dispose(); In each test,
I created a TranactionScop(TransactionScopeOption.RequiresNew,
timeSpan) both with a duration of 5 minutes. Now I am getting a
timeout error.

"Error retrieving data for the query from command with CommandText of
xxx: Timeout expired. The timeout period elapsed prior to
completion of the operation or the server is not responding." (where
xxx is a select stored procedure name)

If I replace RequiresNew with Required in the individual tests, the
first test works and subsequent tests fail, which makes sense because
it appears to join and roll back the outer ambient transaction.

Does anyone know why nesting I would get the timeout at about 30
seconds with the RequiresNew?

I don't see where you're nesting transactions: nested transactions are
done using nested using statements. For example: it's not said that the
start/end methods of the test fixture are ran in the same
thread/appdomain and neither is it defined in which order the tests are
ran.

Unit tests should be atomic, thus shouldn't rely on order. If you need
test-data for your tests, I'd store it in a second schema in your db
and copy it over inside your db to your schema targeted by the tests,
either with a proc or with a sql statement.

FB

--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website: http://www.llblgen.com
My .NET blog: http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
------------------------------------------------------------------------
 
G

Guest

Frans,

Thank you for responding. I could restructure the tests so that I copy in
test data before running the tests. At this point, I haven't even began on
the application but there is an integration module in the application that
will need to use nested transactions and having this fail now makes me
nervous. I suppose I should create that test now.

David Morris

Frans Bouma said:
David said:
Hello,

I am trying to set up some nested transactions to speed up a group of
unit tests. The unit tests call a common method to build up some test
data, which takes about 30 seconds and then runs a specific test.
Each C# unit tests has a using statement that creates a new new
TransactionScope(TransactionScopeOption.Required, timeSpan) (timeSpan
is 600 seconds). Data access uses the Patterns and Practices
Enterprise Library 2.0 against a SQL Server 2005 database.

It all works fine but I would like to speed up my total test time and
only create the test data once. I added [ClassInitialize()] and
[ClassCleanup()] methods to run when my unit tests start and end. In
the ClassInitialize method I created a
TransactionScope(TransactionScopeOption.Required, timeSpan) and in
the ClassCleanup I added a transactionScope.Dispose(); In each test,
I created a TranactionScop(TransactionScopeOption.RequiresNew,
timeSpan) both with a duration of 5 minutes. Now I am getting a
timeout error.

"Error retrieving data for the query from command with CommandText of
xxx: Timeout expired. The timeout period elapsed prior to
completion of the operation or the server is not responding." (where
xxx is a select stored procedure name)

If I replace RequiresNew with Required in the individual tests, the
first test works and subsequent tests fail, which makes sense because
it appears to join and roll back the outer ambient transaction.

Does anyone know why nesting I would get the timeout at about 30
seconds with the RequiresNew?

I don't see where you're nesting transactions: nested transactions are
done using nested using statements. For example: it's not said that the
start/end methods of the test fixture are ran in the same
thread/appdomain and neither is it defined in which order the tests are
ran.

Unit tests should be atomic, thus shouldn't rely on order. If you need
test-data for your tests, I'd store it in a second schema in your db
and copy it over inside your db to your schema targeted by the tests,
either with a proc or with a sql statement.

FB

--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website: http://www.llblgen.com
My .NET blog: http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
 
G

Guest

I now have a very simple unit test that has nested TransactionScope
statements. I now have code like this:

TimeSpan timeSpan = TimeSpan.FromSeconds(600);
using (TransactionScope transactionScope1, new
TransactionScope(TransactionScopeOption.RequiresNew, timeSpan);
--insert a record using Enterprise library
--retrieve record count
using (TransactionScope transactionScope2, new
TransactionScope(TransactionScopeOption.RequiresNew, timeSpan)
--insert a record using Enterprise library
--retrieve record count
--assert record count = original count + 1
}
--assert record count = original count
}

If I use TransactionScopeOption.Required my second assert fails. If I use
RequiresNew I get the timeout error after about 30 seconds.

Thanks,

David Morris
 
F

Frans Bouma [C# MVP]

David said:
I now have a very simple unit test that has nested TransactionScope
statements. I now have code like this:

TimeSpan timeSpan = TimeSpan.FromSeconds(600);
using (TransactionScope transactionScope1, new
TransactionScope(TransactionScopeOption.RequiresNew, timeSpan);
--insert a record using Enterprise library
--retrieve record count
using (TransactionScope transactionScope2, new
TransactionScope(TransactionScopeOption.RequiresNew, timeSpan)
--insert a record using Enterprise library
--retrieve record count
--assert record count = original count + 1
}
--assert record count = original count
}

If I use TransactionScopeOption.Required my second assert fails. If I
use RequiresNew I get the timeout error after about 30 seconds.

Hmm. it seems the second insert isn't running in the same
transaction, as it apparently can't see what the first statement has
done and the requirednew simply starts a new transaction and this of
course hangs on the record count due to a lock on the row by the first
transaction.

If you pass in the first transactionscope's transaction into the
second using statement, does it work then? (not really logical, but
just for testing)

FB

--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website: http://www.llblgen.com
My .NET blog: http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
------------------------------------------------------------------------
 
G

Guest

Yes, it works if i share the first ambient transaction except the rollback
triggered by the first dispose rolls back both inserts.

If I don't share the transaction the failure is on the inner insert, which
happens before the count. It could be that something that happens in
Enterprise Library 2.0 Data Access Block that contributes to this failure but
I don't know where to begin looking. Version 3.0 added better support for
TransactionScope so that connections are shared to avoid a distributed
transaction but I can't upgrade yet because there were some changes that are
not backward compatable.

--David Morris

Thanks,

David Morris
 
W

WenYuan Wang [MSFT]

Hi David,

What the different between REQUIRED and REQUIRESNEW is that REQUIRESNEW
will always create a new transaction for the scope. I'm afraid it could be
a fault in Enterprise Library 2.0 Data Access Block, but it is really
difficult to trouble shoot such issue.... However, you may consider have
Data Access tracing, so you can see what's really happening. Have a look at
the following document.
http://msdn2.microsoft.com/en-us/library/ms971550.aspx
[Tracing Data Access]

Hope this helps.
Sincerely,
Wen Yuan
Microsoft Online Community Support
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top