Home All Groups Group Topic Archive Search About
Author
22 Apr 2007 4:34 PM
David Morris
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

Author
23 Apr 2007 7:37 AM
Frans Bouma [C# MVP]
David Morris wrote:

Show quote
> 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#)
------------------------------------------------------------------------
Author
23 Apr 2007 1:14 PM
David Morris
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

Show quote
"Frans Bouma [C# MVP]" wrote:

> David Morris wrote:
>
> > 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#)
> ------------------------------------------------------------------------
>
Author
23 Apr 2007 3:16 PM
David Morris
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
Author
24 Apr 2007 7:33 AM
Frans Bouma [C# MVP]
David Morris wrote:

Show quote
> 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#)
------------------------------------------------------------------------
Author
24 Apr 2007 1:10 PM
David Morris
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

Show quote
"Frans Bouma [C# MVP]" wrote:

> David Morris wrote:
>
> > 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#)
> ------------------------------------------------------------------------
>
Author
25 Apr 2007 12:49 PM
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

AddThis Social Bookmark Button