When trying to debug something I found code that was effectively doing the following:
Creating a TransactionScope
Creating a Transaction (in this case an nHibernate tx, but not really important)
Creating a second transaction (in this case a standard ADO.Net Tx)
Committing the second transaction
Calling Complete() on the Transaction scope
Disposing the Transaction Scope.
Now - Creating a transaction and not committing is probably a bad idea anyway - especially when having (and that was the bug fix).
However when testing this - I tried various combinations of the above (committing all transactions, some transactions, no transactions (i.e. only TScope) committing the First, but not second, adding other transactions etc) and in all thesting I found that the following to be true:
Only when I failed to commit the first transaction AND the transaction scope became distributed, the Dispose of the TScope would fail with:
System.InvalidOperationException : The operation is not valid for the current state of the enlistment.
I am now curious and would like to know why this is the case?
I suspect the problem you see is covered by one of these: https://nhibernate.jira.com/issues/?jql=project%20%3D%2010000%20AND%20labels%20%3D%20TransactionScope
I'm not entirely sure what happens but I've seen similar behaviour, e.g. if NH enlists in the ambient transaction, and the transaction later becomes distributed, calling TransactionScope.Complete() might hang for 20 seconds and then fail.
NH will try to enlist in a TransactionScope even if you don't use an NH transaction. In this case, NH will flush changes during the ambient transaction's Prepare() phase. It will do this on the db connection, but that has also enlisted in the transaction and will get its own Prepare() call. Unfortunately I haven't been able to figure out the exact problem, but I suspect what happens is that in some circumstances the db connections Prepare() will be called before NHibernate's Prepare(). The latter will try to continue to use the db connection, and it appears this causes some sort of deadlock.
Using a NH transaction and committing this before completing the transaction scope will make NH flush its changes before the underlying DB connection enters the prepare-phase.
Related
I have a 2 DbContext's that I call both of them in one form as you can see here:
dbcontext1.add(object);
dbcontext1.save();
dbcontext2.add(object);
dbcontext2.save();
So I have a question: if some problems happen and my record saved by DbContext1 but not in DbContext2, I need to rollback my transaction.
I searched and I found two methods: using BeginTransaction and TransactionScope. I am confused about which one I should use and what the differences between them are?
I found something like this but i don;'t know how can i roll back this :
using (TransactionScope scope = new TransactionScope())
{
using (EFEntities1 dc = new EFEntities1())
{
dc.USERS.Add(user);
dc.SaveChanges();
}
using (EFEntities2 dc = new EFEntities2())
{
dc.USERS.Add(user);
dc.SaveChanges();
}
scope.complete();
}
Best regards
I am confused about which one I should use and what the differences
between them are?
The difference is that TransactionScope behaves like a regular, lightweight and local transaction as long as you use it with a single database connection (SQL Server 2005) or with multiple connections on the same database (SQL Server 2008 and up). If the two or more connections are used within the same transaction scope or more than one databases are accessed (again, depending on SQL Server version), then it is promoted to a distributed transaction (so registered with MSDTC). In your case, it will be promoted.
I found something like this but i don;'t know how can i roll back this.
As long as you don't call Complete, the transaction scope is rolled back as soon as the scope ends (e.g. dispose, the using ends, etc).
If an exception is thrown by either of the db contexts, then in your code Complete will not be called and the transaction is going to be rolled back.
From MSDN:
If no exception occurs within the transaction scope (that is, between
the initialization of the TransactionScope object and the calling of
its Dispose method), then the transaction in which the scope
participates is allowed to proceed. If an exception does occur within
the transaction scope, the transaction in which it participates will
be rolled back.
This is also the reason why a Rollback method does not exist.
You might also want to look into what isolation level to use with it. The default is Serializable.
I have been using NHibernate for a while and came across the code below that uses a Transaction scope.
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
// do work
}
}
}
I generally do everyting without wrapping the code to a TransactionScope Am I doing something wrong or am I just missing out some beautiful functionality ?
The usage is: transactions. Whether that is benefit is more complex. There are more direct ways of achieving transactions - ADO.NET transactions. These are a little awkward to work with (you need to remember to set the transaction on every command), but are very efficient.
Transaction scope has the advantage of an ambient transaction; this makes it easier to work with. However, it works in a different way. In particular, transaction-scope supports multiple resource transactions - which can mean multiple databases etc. This is typically done via DTC, but DTC has overheads - it is more expensive (and requires specific firewall configuration, etc). In many single-database cases it can short-cut and use the LTM instead of full DTC, but this is still more expensive than ADO.NET transactions... just not as expensive as DTC.
A powerful feature, but make sure you intend to use it before you do ;p
If you are not using any TransactionScope explicitly, every statement you execute on the database will run in a separate transaction.
With a TransactionScope you can bundle multiple statements into a big transaction and undo everything as a block.
This is necessary, when updating multiple tables in multiple statements but performing virtually one big thing, that has to work or not be done at all.
You are missing out on some beautiful functionality: with the transaction scope in place, the code with the transaction scope will participate in ambient transaction if invoked from inside a piece of code running in its own transaction scope. Without transaction scope, your code will have its own transaction (from the deepest nested block) which could fail without failing the outer transaction.
In addition, anything inside your // do work block would have easier time participating in your transaction if you put a transaction scope outside it. The code would be able to break your transaction without having to propagate the error code up the chain or throwing an exception, which could potentially be ignored by code in the middle.
Note: Don't forget to call scope.Complete() on the transaction scope before the end of its using block.
Are you using transactions at all. If you aren't, then you should be.
I personally don't use TransactionScope in any of my NHibernate, but then again I don't need it. In a web environment with a single database, it's not really necessary. my unit of work is a web request. I open my connection on BeginRequest and close it on EndRequest. I use a generic repository that will start a transation if none is present and defined a TransactionAttribute that decorates controller actions so that all table updates are performed in a single transaction.
TransactionScope is just Microsoft's generic way to put all transactional aware resources into a single transaction. This may be multiple databases, a transactional file system, .... Things to worry about in these scenarios is that the transaction most likely will be promoted to the DTC to coordinate all the updates.
Also, I don't know if it's still an issue, but older versions of NHibernate used to have a memory leak associated with using TransactionScope.
Here's a link with everything you need to know about TransactionScopes http://www.codeproject.com/Articles/690136/All-About-TransactionScope
Since I have a "DB util" class with a DataSet QueryDB(string spName, DBInputParams inputParams) method which I use for all my calls to the database, I would like to reuse this method in order to support transacted calls.
So, at the end I will have a SqlDataAdapter.Fill within a SqlTransaction. Will this be a bad practice? Because rarely I see usage of DataAdapter.Fill within a transaction and more often ExecuteReader(). Is there any catch?
Edit1: The thing is that inside my transaction is often needed to retrieve also some data (e.g auto-IDs)... that's why I would like to get it as DataSet.
Edit2: Strange is when I use this approach in a for loop (10000) from 2 different processes, I get "Transaction (Process ID 55) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction." . Is this the right behaviour?
Edit3: (answer for Edit2) I was using IDENT_CURRENT('XTable') which was the source of the error. After I went back to SCOPE_IDENTITY(), everything has been solved.
It is not a bad practice. One thing to remember is that all statements will use an implicit transaction that they will automatically commit when the statement ends. That is a SELECT (as in the SELECT used by Fill) will always use a transaction, the question is whether it will have to start it on itself or it will use the existing one.
Is there any difference between the number, type and duration of locks acquired by a SELECT in an implicit transaction vs. an explicit transaction? Under the default transaction model (READ COMMITTED isolation) NO, there is none. The behavior is identical and indistinguishable. Under other isolation levels (repeatable read, serializable) there is a difference, but that is the necessary difference for the desired higher isolation level to occur and using an explicit transaction is the only way to achieve this desired isolation level, when necessary.
In addition if the SELECT has to read the effects of a transaction that is pending (not yet committed), as in your example (read back the generated IDs) then there is no other way. The SELECT must be part of the transaction that generated the IDs, otherwise it will not be able to see those uncommitted IDs!
A note of caution though. I believe you have at your disposal a great tool that can make all this transaction handling much easier: the System.Transactions. All ADO.Net code is system transaction aware and will automatically enroll any connection and command into the pending transaction, if you simply declare a TransactionScope. That is if function Foo declares a TransactionScope and then calls function Bar, if Bar does any ADO.Net operatio, it will automatically be part of the transaction declared in Foo, even if Bar does nothing explicitly. The TransactionScope is hooked into the thread context and all ADO.Net call called by Bar will check for this context automatically, and use it. Note that I really mean any ADO.Net call, including Oracle provider ones. Alas though there is a warning: using new TransactionScope() Considered Harmful: the default constructor of TransactionScope will create a serializable transaction, which is overkill. You have to use the constructor that takes a TransactionOptions object and change the behavior to ReadCommitted. A second gotcha with TransactionScope is that you have to be very careful how you manage connections: if you open more than one connection under a scope then they will be enrolled in a distributed transaction, which is slow and requires MSDTC to be configured, and leads to all sort of hard to debug errors. But overall I fell that the benefits of using TransactionScope outweight the problems, and the resulted code is always more elegant than passing around IDbTransaction explicitly.
It is a bad practice because while the transaction is open, records/pages/tables that you make changes to are locked for the duration of the transaction. The fill just makes the whole process keep those resources locked longer. Depending on your sql settings, this could block other accesses to those resources.
That said, if it is necessary, it is necessary, just realize the penalty for doing it.
Greetings
I stumbled onto a problem today that seems sort of impossible to me, but its happening...I'm calling some database code in c# that looks something like this:
using(var tran = MyDataLayer.Transaction())
{
MyDataLayer.ExecSproc(new SprocTheFirst(arg1, arg2));
MyDataLayer.CallSomethingThatEventuallyDoesLinqToSql(arg1, argEtc);
tran.Commit();
}
I've simplified this a bit for posting, but whats going on is MyDataLayer.Transaction() makes a TransactionScope with the IsolationLevel set to Snapshot and TransactionScopeOption set to Required. This code gets called hundreds of times a day, and almost always works perfectly. However after reviewing some data I discovered there are a handful of records created by "SprocTheFirst" but no corresponding data from "CallSomethingThatEventuallyDoesLinqToSql". The only way that records should exist in the tables I'm looking at is from SprocTheFirst, and its only ever called in this one function, so if its called and succeeded then I would expect CallSomethingThatEventuallyDoesLinqToSql would get called and succeed because its all in the same TransactionScope. Its theoretically possible that some other dev mucked around in the DB, but I don't think they have. We also log all exceptions, and I can find nothing unusual happening around the time that the records from SprocTheFirst were created.
So, is it possible that a transaction, or more properly a declarative TransactionScope, with Snapshot isolation level can fail somehow and only partially commit?
We have spotted the same issue. I have recreated it here - https://github.com/DavidBetteridge/MSMQStressTest
For us we see the issue when reading from the queue rather than writing to it. Our solution was to change the isolation level of the first read in the subscriber to be serialised.
no, but snapshot isolation level isn't the same as serializable.
snapshoted rows are stored in the tempdb until the row commits.
so some other transaction can read the old data just fine.
at least that's how i understood your problem. if not please provide more info like a grapf of the timeline or something similar.
Can you verify that CallSomethingThatEventuallyDoesLinqToSQL is using the same Connection as the first call? Does the second call read data that the first filed into the db... and if it is unable to "see" that data would cause the second to skip a few steps and not do it's job?
Just because you have it wrapped in a .NET transaction doesn't mean the data as seen in the db is the same between connections. You could for instance have connections to two different databases and want to rollback both if one failed, or file data to a DB and post a message to MSMQ... if MSMQ operation failed it would roll back the DB operation too. .NET transaction would take care of this multi-technology feature for you.
I do remember a problem in early versions of ADO.NET (maybe 3.0) where the pooled connection code would allocate a new db connection rather than use the current one when a .NET level TransactionScope was used. I believe it was fully implemented with 3.5 (I may have my versions wrong.. might be 3.5 and 3.5.1). It could also be caused by the MyDataLayer and how it allocates connections.
Use SQL Profiler to trace these operations and make sure the work is being done on the same spid.
It sounds like your connection may not be enlisted in the transaction. When do you create your connectiion object? If it occurs before the TransactionScope then it will not be enlisted in the transaction.
I’m running into problems using MSDTC and Oracle. It’s a .net application and I’m using the TransactionScope class to control the transactions.
The problem is that, sometimes, if the transaction is rolled back (scope.Dispose is called without having called scope.Complete), it stays in “Aborting” state for a long time, not releasing the locked records. Even though the transactions stays in “Aborting” state, when Dispose is called to abort the transaction, it returns immediately, so the thread doesn’t get stuck.
Does anyone know what could cause the transaction to behave like this and keep the locks after abort has been called?
Thanks
There are known issues around the use of distributed transactions when using the Microsoft Data Provider for Oracle.
If you are using it, try switching to the ODP.NET provider, which should fix your transaction problems.