What happens to an uncommitted transaction when the connection is closed? - sql

Are they rolled back immediately?
Are they rolled back after some period of time?
Are they left in an uncommitted state?
Is the behavior the same if connection pooling is used and the connections are simply reset?

It can stay open while connection pooling applies. Example: command timeout can leave locks and TXN because the client sends as "abort".
2 solutions:
Test in the client, literally:
IF ##TRANCOUNT <> 0 ROLLBACK TRAN
Use SET XACT_ABORT ON to ensured a TXN is cleaned up: Question 1 and Question 2
I always use SET XACT_ABORT ON.
From this SQL Team blog:
Note that with connection pooling,
simply closing the connection without
a rollback will only return the
connection to the pool and the
transaction will remain open until
later reused or removed from the pool.
This can result in locks begin held
unnecessary and cause other timeouts
and rolling block
From MSDN, section "Transaction Support" (my bold)
When a connection is closed, it is
released back into the pool and into
the appropriate subdivision based on
its transaction context. Therefore,
you can close the connection without
generating an error, even though a
distributed transaction is still
pending. This allows you to commit or
abort the distributed transaction at a
later time.

Uncommitted changes are not visible outside of the connection, so the time of the rollback is irrelevant. So yes, the transaction is eventually rolled back.

The server will rollback immedeatly any uncommited transaction when a session is closed.
The ADO pool is reponsible for clearing any uncommited transaction before returning a transaction to the pool. If you dispose a connection with pending transactions, it will rollback.
Transactions can be started by the client using the ADO API (SqlConnection.BeginTransaction) or by executing an BEGIN TRANSACTION statement. The TDS protocol between client and server has special tokens informing the client when a transaction was started/commited like this so ADO knows that the connection has pending transactions even if they are started in T-SQL code.

Related

Sql Server 2014 sp_reset_connection behavior inside transaction

What is the behavior of sp_reset_connection when it is run inside a transaction? I am looking for clarification so I can decide if I need to restructure my code, or if I can leave it as is.
I have a c# application that uses an ORM(Petapoco) that doesn't seem to respect the ambient transaction(System.Transactions.TransactionScope). I can see the transaction beginning in Sql Profiler, but before every call in the transaction the ORM is sending a sp_reset_connection call. I am also seeing the Transaction Commit event in Sql Profiler when my ambient transaction is completed.
I have seen other questions on StackOverflow that suggest the Transaction Isolation scope is reset when sp_reset_connection is called (see: What does “exec sp_reset_connection” mean in Sql Server Profiler?), but I can't find any info on what happens specifically when sp_reset_connection is called inside of a transaction.
The sp_reset_connection stored procedure is not well documented since it's an internal stored procedure and never called directly. Be aware that it may change or be removed in the future without notice. That being said, it is useful to know a little about the internals for troubleshooting and correlating activity trace data.
The purpose of the proc is to restore connection environment state to that of a newly opened connection to support connection pooling. The client API specifies whether a connection is to be reset by setting either the RESETCONNECTION or RESETCONNECTIONSKIPTRAN bits of the TDS protocol status field when a request is issued on a newly reused connection from the pool. SQL Server internally invokes the sp_reset_connection RPC you see in a trace when either of those flags are set.
The TDS protocol documentation desribes these flags so we can infer this is what sp_reset_connection does under the covers. Below is the an excerpt from the documentation:
RESETCONNECTION (Introduced in TDS 7.1) (From client to server) Reset
this connection before processing event. Only set for event types
Batch, RPC, or Transaction Manager request. If clients want to set
this bit, it MUST be part of the first packet of the message. This
signals the server to clean up the environment state of the connection
back to the default environment setting, effectively simulating a
logout and a subsequent login, and provides server support for
connection pooling. This bit SHOULD be ignored if it is set in a
packet that is not the first packet of the message.
This status bit MUST NOT be set in conjunction with the
RESETCONNECTIONSKIPTRAN bit. Distributed transactions and isolation
levels will not be reset. 0x10
RESETCONNECTIONSKIPTRAN (Introduced in TDS 7.3) (From client to
server) Reset the connection before processing event but do not modify
the transaction state (the state will remain the same before and after
the reset). The transaction in the session can be a local transaction
that is started from the session or it can be a distributed
transaction in which the session is enlisted. This status bit MUST NOT
be set in conjunction with the RESETCONNECTION bit. Otherwise
identical to RESETCONNECTION.
The low-level client API (SqlClient here) indirectly controls the behavior of sp_reset_connection by setting those flags as needed. When a connection is reused outside a TransactionScope, the RESETCONNECTION flag is set as can be seen in this network packet trace (Wireshark):
Below is a trace of the first request issued on a reused connection from within the scope of a TransactionScope using block:
As you can see, the reset within TransactionScope will not rollback the transaction. Consequently, even if your ORM follows an open/execute/close pattern for data access, your outer TransactionScope transaction context will be honored. The transction will be committed as long as you invoke TransactionScope.Complete before exiting the transaction scope. You can include a TM Commit Tran completed event to your Profiler trace to see the commit when the TransactionScope exits.
BTW, Profiler/SQL trace is deprecated. Use Extended Events instead going forward. Also, as suggested in the other answer, be sure to specify the desired transaction isolation level (e.g. read committed) when you use TransactionScope. The default is serializable which can have side effects like unnecessary blocking and deadlocks. I suggest you use serializable only if you specifically need that strict level of isolation.

nhibernate RollbackTransaction and why to dispose of session after rollback?

I am working on a system using nhibernate, and I see a lot of the following two lines in exception catch blocks:
session.Flush();
session.RollbackTransaction();
I am very confused by this logic, and it looks like unnecessary work to flush changes, then use transaction rollback practices.
I wanted to setup an argument for removing these flush calls and relying on just the RollbackTransaction method, but I came across this question. Next I read more into the documentation linked, and read the following nugget of information:
If you rollback the transaction you should immediately close and discard the current session to ensure that NHibernate's internal state is consistent.
What does this mean? we currently pair our Session life time with our web request's begin and end operations, so I am worried that the reason we are calling flush THEN rollback is to keep the session in a valid state.
Any ideas?
NHibernate does object tracking via the session, and all the changes you do the entities are stored there, when you do the flush those changes are written to the db. If you get an exception while doing so the session state is not consistent with the database state, so if you do a rollback at this stage it will rollback db transaction but session values will not be rolled back.
As per design once that happens the session should not be used further in reliable manner (even Session.Clear() will not help)
If you use the session per request and if you get an error the best approach is to display an error to the user and ask to retry the operation. Other option is to create a brand new session and use it for data fetching purposes to display errors.
This Flush before Rollback is very likely a trick for working around a bug of the application caused by re-using the session after the rollback.
As you have found by yourself, the session must not be used after a rollback. The application is doing that mistake as per your comment.
Without the Flush before the Rollback, the session still consider the changes as pending, and will commit them at next Flush, defeating the Rollback purpose. Doing that Flush before the rollback causes pending changes to be flushed, then rollback-ed, helping avoiding the session to flush them later.
But the session is still not in a consistent state, so by continuing to use it, the application stays at risk. The session cache still holds the changes that were attempted then rollback-ed. The session does just no more consider them as pending changes awaiting a flush. If the application later usages of the session access those entities, their state will still be the modified one from the rollback-ed transaction, although they will not be considered dirty.

Force a Transaction to commit from another connection?

We have a very difficult to track down bug in one of our software solutions that sometimes leaves an open transaction. We have this application in production at a number of sites (read: 70+), and we've only seen this issue twice so far this year at separate locations.
The issue we're having is a transaction that's being left open from the constant connection to the SQL Server. Using dbcc opentran shows that there is a single transaction left open. In today's case, it was open from 9:30 AM before we realized it at 1:00 PM. Closing the program with the connection will result in the transaction being closed/cancelled, and all the data from the day thus far lost.
Using dbcc opentran it responds with the name of the open transaction was user_transaction. Trying to close it with commit tran user_transaction gives an error of The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION..
I can understand you would almost never want to force the transaction to be committed without the prior connection's knowledge, but is there any way to do so? In this case, we closed the program and we lost a half of day's business worth of data.
Thanks.
If it were possible for a transaction to be committed by another conenction, then it's not a transaction. If this were possible, it would open the door to way more issues than it would solve.
See this link: http://ask.sqlservercentral.com/questions/3865/forcing-a-transaction-to-commit.html

How can have a SQL connection that is sleeping a pending transaction?

I'm facing some locks at our DB server created by our application. What I don't understand is how a process that is Sleeping is having an Open Transaction (that process 71 is the one creating the Lock).
As far as I know when a process finishes it closes all the opened transactions. Is that rigth?
Thanks in advance mates.
As far as I know when a process
finishes it closes all the opened
transactions. Is that right?
No. If you explicitly open a transaction you must explicitly commit or rollback. Until that time the transaction remains open so it is perfectly possible for a connection to be idle (not currently processing any task) but still have an uncommitted transaction.
Many people expect that an error will automatically roll back a transaction but this is not the case unless you have
set xact_abort on
As far as I know when a process
finishes it closes all the opened
transactions. Is that rigth?
Yes. But it is not guaranteed and you should not rely on it. You must explicitly close the connection.

What does sp_reset_connection do?

sp_reset_connection seems to be called by SQL Server connection pooling, to ensure that connections reused from the pool have most of their settings reset. Does anyone know exactly what it does and doesn't do though?
eg I see from this post that it doesn't reset the transaction isolation level
Data access API's layers like ODBC, OLE-DB and SqlClient call the (internal) stored procedure sp_reset_connection when re-using a connection from a connection pool. It does this to reset the state of the connection before it gets re-used.
There does not appear to be official documentation on what things get reset, but here is an unofficial list.
sp_reset_connection resets the following aspects of a connection:
It resets all error states and numbers (like ##error)
It stops all EC's (execution contexts) that are child threads of a
parent EC executing a parallel query
It will wait for any outstanding I/O operations that is outstanding
It will free any held buffers on the server by the connection
It will unlock any buffer resources that are used by the connection
It will release all memory allocated owned by the connection
It will clear any work or temporary tables that are created by the
connection
It will kill all global cursors owned by the connection
It will close any open SQL-XML handles that are open
It will delete any open SQL-XML related work tables
It will close all system tables
It will close all user tables
It will drop all temporary objects
It will abort open transactions
It will defect from a distributed transaction when enlisted
It will decrement the reference count for users in current database;
which release shared database lock
It will free acquired locks
It will releases any handles that may have been acquired
It will reset all SET options to the default values
It will reset the ##rowcount value
It will reset the ##identity value
It will reset any session level trace options using dbcc traceon()
sp_reset_connection will NOT reset:
Security context, which is why connection pooling matches connections
based on the exact connection string
If you entered an application role using sp_setapprole, since application
roles can not be reverted
The transaction isolation level
From this forum post:
The sp_reset_connection stored procedure is used to reset a connection
so that when it is used in a pool, nothing from a previous session is stored
that is connection-specific.