ZombieCheck Exception - This SqlTransaction has completed; it is no longer usable -- during simple commit - sql-server

I have the following code which performs a commit of a single row to a database table (SQL 2008 / .NET 4)
using (var db = new MyDbDataContext(_dbConnectionString))
{
Action action = new Action();
db.Actions.InsertOnSubmit(dbAction);
db.SubmitChanges();
}
Normally everything is fine, but once in a while I get the following exception:
System.InvalidOperationException: This SqlTransaction has completed; it is no longer usable.
at System.Data.SqlClient.SqlTransaction.ZombieCheck()
at System.Data.SqlClient.SqlTransaction.Rollback()
at System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)
There are a number of similar questions on SO but I after reading them I cannot work out the cause.
Could this be simply due to a SQL timeout (the exception occurs close to 25s after the call is made)? Or should I expect a SQL timeout exception in that case?
Does anyone know what else may cause this?

The DataContext.SubmitChanges method has the following code lines in it's body:
// ...
try
{
if (this.provider.Connection.State == ConnectionState.Open)
{
this.provider.ClearConnection();
}
if (this.provider.Connection.State == ConnectionState.Closed)
{
this.provider.Connection.Open();
flag = true;
}
dbTransaction = this.provider.Connection.BeginTransaction(IsolationLevel.ReadCommitted);
this.provider.Transaction = dbTransaction;
new ChangeProcessor(this.services, this).SubmitChanges(failureMode);
this.AcceptChanges();
this.provider.ClearConnection();
dbTransaction.Commit();
}
catch
{
if (dbTransaction != null)
{
dbTransaction.Rollback();
}
throw;
}
// ...
When the connection times out, the catch block is executed and the dbTransaction.Rollback(); line will throw a InvalidOperationException.
If you had control over the code, you could catch the exception like this:
catch
{
// Attempt to roll back the transaction.
try
{
if (dbTransaction != null)
{
dbTransaction.Rollback();
}
}
catch (Exception ex2)
{
// This catch block will handle any errors that may have occurred
// on the server that would cause the rollback to fail, such as
// a closed connection.
Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
Console.WriteLine(" Message: {0}", ex2.Message);
}
throw;
}

YES! I had the same issue. The scary answer is that SQLServer sometimes rolls back a transaction on the server side when it encounters an error, and does not pass the error back to the client. YIKES!
Look on the Google Group microsoft.public.dotnet.framework.adonet for "SqlTransaction.ZombieCheck error" Colberd Zhou [MSFT] explains it very well.
and see aef123's comment on this SO post

May I suggest that connection closes earlier that transaction commits. Then the transaction is rolled back. Check this article on MSDN Blog.

Related

SqlException logs RAISEERROR and PRINT statements from .NET [duplicate]

I have a stored procedure in SQL Server that throws an error whenever a condition is hit. In order to catch this error and display it to the user, I use
try
{
//code
}
catch (Exception e)
{
return BadRequest(e.Message);
}
This catches most of the cases but on the other hand, this procedure has some print messages that also get caught by this Exception. Is there a way to catch the exception thrown only from RAISERROR and ignore this print message from SQL Server?
All info and error messages generated during command execution are buffered and available when a SqlException is caught. The Message property includes the text of all info messages (print statements and errors with a severity of less than 11) and the warnings/errors that caused the exception.
To ignore info messages, use the SqlException Errors collection and process only those with severity (SqlError.Class property) of 11 or higher:
catch (SqlException e)
{
var sb = new StringBuilder();
foreach(var error in e.Errors)
{
if(error.Class > 10) sb.AppendLine(error.message);
}
return BadRequest(sb.ToString());
}

ServiceStack OrmLite and transactions

I am trying to execute sql inside a transaction using ServiceStack OrmLite. The code below works with Sqlite but not with SqlServer. With SqlServer I get the following error:
ExecuteScalar requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized.
Is there something wrong with this code?
using (var trans = Db.BeginTransaction())
{
try
{
foreach (myObject in myObjects)
Db.Insert<MyObject>(myObject);
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
}
Someone else put this answer in a comment and then deleted it... so:
BeginTransaction needs to be OpenTransaction

Objectify 4 transactions

With Objectify 4 I am trying to throw a controlled exception inside a transaction. As vrun() interface does not allow throwing exceptions, I have to catch it. I do it, and then I want to perform the rollback, but when the transaction finishes and it all fails (I think it is because it tries to do a commit), not even doing the rollback. How can I do it?
Thanks in advance!
Code example:
ofy().transact(new VoidWork() {
#Override
public void vrun() {
//do something
//...
//find someting wrong and want to throw an exception
try {
throw new MyException(); //throw the exception
} catch (MyException e) {
ofy().getTransaction().rollback(); //catch it and perform rollback
}
}
});
//an error occurs

database connection on local machine

I was originally coding on a server with Apache installed and used this function to connect to the database
public Connection getDBConnection()
{
java.sql.Connection conn=null;
//synchrnized(this)
try
{
DriverManager.registerDriver(new OracleDriver());
conn = DriverManager.getConnection("jdbc:oracle:thin:#dukeorrac01:1521:ORDB1","nrsc","nrsc");
}
catch (Exception e)
{
e.printStackTrace();
}
return conn;
}
Probably not the best way to do it, but it worked. However, I've moved to coding on my local machine and I was given jboss as an IDE to work with. I've gotten everything working (sorta) except, it seems to be connecting to a different databse. I get exhausted resultsets (which isn't right). I "thought" the "getConnection("jdbc:oracle....") was what established the connection to the actual database.
Why is this not working on my local machine when it worked perfectly fine on the remote server?
P.S. I'm new to database/server configuration stuff. So, don't assume I know some step in setting up a database or server. Also, I did not create this orginally. It was given to me to use.
Thanks
The error seems to indicate that you are not closing your result sets properly. In the following example see how after a statement is executed you have to close those resources in the finally block.
W.r.t why you did not see this error before is probably because your server probably had lots of resources whereas your local machine has limited resources and your default settings are not modified to reflect the needs of your application.
In general, its better to do basic house keeping like closing all the open result sets and statements whether or not you have enough resources allocated on your DB.
public void getDBConnection() {
Connection conn = null;
Statement statement = null;
ResultSet rs = null;
try {
connection = DriverManager.getConnection("jdbc:oracle:thin:#dukeorrac01:1521:ORDB1","nrsc","nrsc");
statment = connection.createStatement();
statement.setFetchSize(Integer.MIN_VALUE);
// Do more stuff, iterate to ResultSet etc...
} catch (SQLException ex) {
// Exception handling stuff
...
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) { /* ignored */}
}
if (statment != null) {
try {
statment.close();
} catch (SQLException e) { /* ignored */}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) { /* ignored */}
}
}
}

java:not able to set auto commit mode with value false in java 1.4 api?

sql server 200
java 1.4
jboss 3
HI am getting exception message
"You cannot set autocommit during a managed transaction"
code is below
try {
try {
connection = getConnection();
} catch (Exception e) {
throw new ConnectionException(e.getMessage());
}
for(int i=0;i<recordIds.size();i++)
{
String currentRecordId=(String)recordIds.get(i);
try
{
//exception on this line connection.setAutoCommit(false);
preparedStatement = connection.prepareStatement(getSQL("PurgeRecordInDumpData"));
preparedStatement.setLong(1,Long.parseLong(currentRecordId));
int numberOfUpdates=preparedStatement.executeUpdate();
if(numberOfUpdates!=1)
{
throw new Exception("Record with record id "+currentRecordId +"could not be purged.");
}
preparedStatement.close();
connection.commit();
listOfPurgedRecords.add(currentRecordId);
}
catch(Exception e)
{
connection.rollback();
}
}
return listOfPurgedRecords;
}
what is cause of this exception and what does it mean?
The error is clear, you cannot set autocommit while you are in a managed transaction. You should not even need to set this to false as that is the default, you use to enable it autocommit.
I am not sure if you are using J2EE and EJB's, if you are and you want to ENABLE autocommit, you can change your setting to bean managed transaction (BMT) and this would allow you to modify this setting.
However, the way you are using it in your code you don't need to set it to false, everything is done in transactions and you control them with commit or rollback.

Resources