I get the following sql exception :
Transaction was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction. Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.
I don't have any transactions in any stored procedures, I do the transcation from .net and I always call them with using .
Have you guys met this before?
A transaction is a transaction, no matter where started. Whether in c# or the RDBMS.
Your using issues BEGIN TRANSCATION effectively.
MSDN (for SQL Server 2000 but still valid) recommends you retry automatically when a deadlock is detected, Rather than write code here, there are many results on Google for you to peruse.
When using the transaction you need to be careful as by default it sets the isolation level to serialisable. When the connection is released back into the pool it will still have that level set. This can seriously harm concurrency.
Related
I have a .net web page in which two stored procs are called.
Both of these have begin-commit transaction in sql server.Also I am calling the second proc multiple times depending on some if conditions.
I want to wrap this whole process under a single transaction. I have looked around and found Sqltransaction and TransactionScope clases in C# will help me in this situation.
But I have never used them, always use transaction in sql server, and so do not know if the transactions in .Net will have problems as both my stored procs have their own Begin-commit transaction in Sql Server.
If they do conflict is there a way to get them to work under a single transaction?
Yes, it is possible to call (e.g. existing or legacy) Stored Procs from .Net which use manual BEGIN TRAN, COMMIT TRAN / ROLLBACKs under a .Net TransactionScope, or if you manage the transaction from a SqlTransaction. (Although to state the obvious, if you can avoid using multiple transaction technologies, do so).
For the 'happy case' scenario, what will happen is that the ##TRANCOUNT will be increased when the SPROC transactions are called (just as per nested transactions in SqlServer). Transactions are only committed when ##TRANCOUNT hits zero after the outermost commit on the connection. i.e. inner commits will simply decrease ##TRANCOUNT. Note however that the same is not true for ROLLBACKS - unless you are using SAVEPOINTS, any rollback will rollback the entire transaction. You'll need to be very careful of matching up ##TRANCOUNTs.
You sound a bit undecided about TransactionScope vs SqlTransaction. TransactionScope is more versatile, in that it can span both single phase and distributed transactions (using DTC). However, if you only need to coordinate the transaction on a single connection, same database, then SqlTransaction would also be fine.
I've read about TransactionScope and this article, but I still don't understand 2 things:
When SqlCommand.ExecuteNonQuery is executed it doesn't really executed until scope.Complete() is invoked? If it's true, so where all the operations that were executed within the scope are remain and wait for scope.Complete() or scope.Rollback()?
When TransactionScope is instantiated how it prevents from SqlCommand.ExecuteNonQuery to be executed and wait for scope.Complete() or scope.Rollback()? Does it creates some "place" and SqlCommand somehow knows about it and puts the instructions in there?
[1] When SqlCommand.ExecuteNonQuery is executed it doesn't really executed until scope.Complete() is invoked?
No this is not correct. Your command is executed on the line where you call ExecuteNonQuery. It is, however, interesting to know where all the changes are stored. The changes do not go directly to the affected table(s) on the server side, rather the changes are stored in a temporary place (again on a server side), which leads to an answer on your second question
[2] When TransactionScope is instantiated how it prevents from SqlCommand.ExecuteNonQuery to be executed and wait for scope.Complete() or scope.Rollback()?
It does not prevent as such, the action is executed, but because the result of the action is stored in a temporary location you must either merge these changes with the main table(s) - scope.Commit() or discard these changes - scope.Rollback() (or whatever is used to discard the changes in the specific database data provider)
TransactionScope hides a lot of stuff under the covers.
When you create a TransactionScope, everything you do inside it, is in the context of database transaction(s). So the SQL statements will be executed immediately, but their effects will be inside a transaction so that other processes will not be aware of them having occurred until the transaction commits.
If you are only working with a single database, then a transaction is opened against that and committed, or rolled back according to whether you .Complete() or not.
Also if an exception occurs within the context of the TransactionScope then the Transaction is rolled back.
If you are working with multiple databases, a transaction is created in each one, and Microsoft Distributed Transaction Coordinator (MSDTC) manages the overall transaction. When you .Complete() MSDTC will then instruct each individual transaction to commit.
Note MSDTC is not limited to databases - see here for more info.
A transaction scope doesn't prevent the code from executing, it prevents the transaction been committed. So in the case of a SqlCommand.ExecuteNonQuery, because it is inside a transaction, the SqlCommand looks at the transaction co-ordinator and sees that is was called inside a transaction, so when it connects through to SQL Server, the transaction is maintained, so SQL Writes the data to the database but it is not available to be read unless the transaction is committed (or someone does a "dirty read"). If complete is never called then when the TransactionScope is disposed it is rolled back, and SQL can undo the insert (or whatever else it did).
Anything that uses transactions (such as other database technologies) need to implement their .Net code to support transactions.
Transactions are essentially used in databases, but theoretically, other code could be made to support it.
But, to answer your question, the code is not stopped until the Complete() method is called, it is run, but within a transaction.
This might help your questions you can enable tracing for the MSDTC to see transaction
Please see link for detailed information http://support.microsoft.com/kb/899115
Hope this helps..
Is it possible to retrieve the current SQL Transaction in c# when connection was closed?
sqlTransaction.Save("savePoint");
sqlConnection.Close() // im purposely closing it to test
if (sqlConnection.State == ConnectionState.Close)
{
sqlConnection.Open():
// is it possible to resume the sql transaction when I re-open
// the sql connection? how?
}
SqlTransaction.Save does not 'save' the transaction, instead it creates a transaction savepoint, which is something completely different:
Creates a savepoint in the transaction
that can be used to roll back a part
of the transaction, and specifies the
savepoint name.
A savepoint can be used before the transaction is committed to partially rollback some of the work done by the transaction. A typical example would be an attempt to do an update that may fail, so you create a savepoint before doing the update and in case of failure you rollback to the savepoint, thus preserving all the work done prior to the savepoint.
See Exception handling and nested transactions for an example of how to use savepoints.
Now back to your question, is there a way for a connection to start a connection, close, and when re-open, pick up the same transaction? Technically there is, by using the (now deprecated) sp_getbindtoken and sp_bindsession. But this is just a curiosity, there is absolutely no valid scenario for you to attempt to 'reuse' a transaction across two different sessions (two re-opens of a connection).
No, SQL Server will rollback any uncommitted transactions when the connection is terminated.
This seems to be a misunderstanding of a database transaction. Transactions are all-or-nothing conversations with the database. If you close the line of communication with the database by closing the connection, the conversation is over and the changes are not committed (the "nothing" part of "all-or-nothing").
No, I don't think you can do this.
Consider the following code which does not rollback the transaction if an exception is caught.
transaction = connection.BeginTransaction();
command.Transaction = transaction;
try {
// interact with database here
catch {}
finally {
connection.Close();
}
What are the consequences of this and is it necessary to rollback the transaction?
The best is to generate your transaction inside a using block like this:
using( /*code to create the transaction you want )
{
//perform your transaction here
transaction.Commit();
}
If you code fails before the call to commit, it will automatically be rolled back as the using block is exited.
It will leave an open transaction on the database, which could potential block other queries.
Taken from here:
Consider the following general
guidelines when you use transactions
so that you can avoid causing
deadlocks:
Always access tables in the same order
across transactions in your
application. The likelihood of a
deadlock increases when you access
tables in a different order each time
you access them.
Keep transactions as
short as possible. Do not make
blocking or long-running calls from a
transaction. Keep the duration of the
transactions short. One approach is to
run transactions close to the data
source. For example, run a transaction
from a stored procedure instead of
running the transaction from a
different computer.
Choose a level of
isolation that balances concurrency
and data integrity. The highest
isolation level, serializable, reduces
concurrency and provides the highest
level of data integrity. The lowest
isolation level, read uncommitted,
gives the opposite result.
I'm using PowerShell transactions; which create a CommittableTransaction with an IsolationLevel of Serializable. The problem is that when I am executing a Transaction in this context all SELECTs are blocked on the tables affected by the transaction on any connection besides the one executing the transaction. I can perform gets from within the transaction but not anywhere else. This includes SSMS and other cmdlets executions. Is this expected behavior? Seems like I'm missing something...
PS Script:
Start-Transaction
Add-Something -UseTransaction
Get-Something #hangs here until timeout
Add-Something -UseTransaction
Undo-Transaction
Serializable transactions will block any updates on the ranges scanned under this isolation. By itself the serialization isolation level does not block reads. If you find that reads are blocked, something else must be at play and it depends on what you do in those scripts.
Sounds as if your database has ALLOW_SNAPSHOT_ISOLATION=OFF. This setting controls the concurrency mechanism used by the database:
ALLOW_SNAPSHOT_ISOLATION=OFF: This is the traditional mode of SQL Server, with lock based concurrency. This mode may lead to locking problems.
ALLOW_SNAPSHOT_ISOLATION=ON: This is avaliable since SQL Server 2005, and uses MVCC, pretty similar to what Oracle or Postgresql do. This is better for concurrency as readers do not block writers and writers do not block readers.
Note that this two modes do not behave in the same way, so you must code your transactions for assuming one mode or the other.