5

La pregunta subyacente a esta publicación es "¿Por qué una transacción LTM no promocionada alguna vez estará en duda?"TransactionInDoubtException usando System.Transactions en SQL Server 2005

Obtengo System.Transactions.TransactionInDoubtException y no puedo explicar por qué. Desafortunadamente no puedo reproducir este problema, pero de acuerdo con los archivos de rastreo ocurre. Estoy usando SQL 2005, conectándome a una base de datos y usando una SQLConnection, así que no espero que la promoción tenga lugar. El mensaje de error indica un tiempo de espera. Sin embargo, a veces recibo un mensaje de tiempo de espera, pero la excepción es que la transacción se ha cancelado en lugar de tener dudas, lo cual es mucho más fácil de manejar.

Aquí está la traza completa:

System.Transactions.TransactionInDoubtException: The transaction is in doubt. ---> System.Data.SqlClient.SqlException: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. 
    at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection) 
    at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj) 
    at System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error) 
    at System.Data.SqlClient.TdsParserStateObject.ReadSni(DbAsyncResult asyncResult, TdsParserStateObject stateObj) 
    at System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket() 
    at System.Data.SqlClient.TdsParserStateObject.ReadBuffer() 
    at System.Data.SqlClient.TdsParserStateObject.ReadByte() 
    at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) 
    at System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest) 
    at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest transactionRequest, String transactionName, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest) 
    at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransaction(TransactionRequest transactionRequest, String name, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest) 
    at System.Data.SqlClient.SqlDelegatedTransaction.SinglePhaseCommit(SinglePhaseEnlistment enlistment) 
    --- End of inner exception stack trace --- 
    at System.Transactions.TransactionStateInDoubt.EndCommit(InternalTransaction tx) 
    at System.Transactions.CommittableTransaction.Commit() 
    at System.Transactions.TransactionScope.InternalDispose() 
    at System.Transactions.TransactionScope.Dispose() 

¿Alguna idea? ¿Por qué estoy teniendo dudas y qué debo hacer cuando lo reciba?

EDITAR para más información

que en realidad todavía no tienen la respuesta para esto. Lo que sí me di cuenta es que la transacción en realidad se compromete parcialmente. Una tabla recibe la inserción pero la otra no recibe la actualización. El código está trazado de manera PESADA y no hay mucho espacio para que me esté perdiendo algo.

Hay una forma en que puedo averiguar fácilmente si la transacción se ha promocionado. ¿Podemos decir desde el seguimiento de la pila si es así? El compromiso de fase simple (que está en el seguimiento de la línea) parece indicar que no hay promoción para mí, pero tal vez me falta algo. Si no se promociona, ¿cómo puede estar en duda?

Otra pieza interesante del rompecabezas es que creo un clon de la transacción actual. Lo hago como un workarround a este problema. http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=914869&SiteID=1

Desafortunadamente, no sé si este problema se ha resuelto. Quizás crear el clon está causando un problema. Aquí está el código correspondiente

using (TransactionScope ts = new TransactionScope()) 
{ 
    transactionCreated = true; 
    //part of the workarround for microsoft defect mentioned in the beginning of this class 
    Transaction txClone = Transaction.Current.Clone(); 
    transactions[txClone] = txClone; 
    Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(TransactionCompleted); 
    MyTrace.WriteLine("Transaction clone stored and attached to event"); 

    m_dataProvider.PersistPackage(ControllerID, package); 
    MyTrace.WriteLine("Package persisted"); 
    m_dataProvider.PersistTransmissionControllerStatus(this); 
    MyTrace.WriteLine("Transmission controlled updated"); 
    ts.Complete(); 
} 

Gracias

+0

se refleja la base de datos? –

+0

No la base de datos no está reflejada. Tampoco esto ocurre solo en uno o dos entornos, sino en docenas de ellos – Mark

+0

¿Ha intentado preguntar en [serverfault] (http://serverfault.com/)? O incluso presentar una solicitud de comunicación con Microsoft. Por favor, publique la respuesta cuando la encuentre ... –

Respuesta

2

La respuesta es que no puede. Lo que aparentemente estaba sucediendo era que la promoción estaba teniendo lugar. (Descubrimos esto accidentalmente) Aún no sé cómo detectar si se está produciendo un intento de promoción. Eso hubiera sido extremadamente útil para detectar esto.

+3

Podría haber manejado el [DistributedTransactionStarted] (http: //msdn.microsoft.com/en-us/library/system.transactions.transactionmanager.distributedtransactionstarted.aspx) event. –

0

duro para asesorar nada sin mirar en su código, pero mi primera sugerencia es que TransactionScope() es una sobrecarga cuando se tiene 1 servidor SQL con 1 conexión.

¿Por qué no utilizar System.Data.SqlClient.SqlTransaction() en su lugar?

La documentación dice que "Si se abre una conexión a un servidor remoto dentro de una transacción de base de datos, la conexión al servidor remoto se alista en la transacción distribuida y la transacción local se promueve automáticamente a una transacción distribuida". Sin embargo, si usa realmente solo una conexión es un error muy extraño. ¿Está seguro de que no está llamando a ningún componente de terceros que pueda crear conexiones a MS SQL, MS MQ u otra cosa que requiera la creación de una transacción distribuida?

Además, si usa TransactionScope() en el procedimiento CLR de SQL Server, promoverá la transacción en cualquier caso.

También si llama a un procedimiento de tienda que acceda a una tabla desde el servidor SQL vinculado, supongo que esto también requerirá promoción.

La pregunta es bastante antigua, quizás ya conozca la respuesta y podría publicarla aquí para otros. ¡Gracias!

+0

1) He dado un pequeño fragmento del código 2) Podría usar SqlTransaction pero desde un sistema de punto de vista OO.Transacciones es una abstracción bella y clara 3) Todo indica que solo se está utilizando una conexión. No estoy seguro de cómo puedo probar esto de manera concluyente 4) No hay componentes de terceros en esta área del código 5) No está en un CLR 6) No hay servidores vinculados – Mark

0

Me gana el aliento.

Tengo la costumbre de ejecutar ExecuteNonQuery en "BEGIN TRANSACTION" y "COMMIT" o "ROLLBACK" a mano.

Completamente por accidente, esto funcionó muy bien cuando algunos códigos necesitaban funcionar igual ya fuera en una transacción o no.

0

En realidad tengo el mismo problema y parece estar relacionado con las especificaciones del servidor db. Me gustaría que tu dba eche un vistazo a la utilización de la CPU de la caja mientras estás ejecutando este código. Esto sucede en nuestro entorno porque estamos intentando realizar una operación de actualización en una gran cantidad de filas en nuestra base de datos dentro de una transacción. Esto está sucediendo en nuestra base de datos OLTP en una de nuestras tablas más utilizadas que creará contención de bloqueo. Lo que me parece fascinante sobre el problema es el aspecto del tiempo de espera que veo en el seguimiento de tu pila. No importa qué valores de tiempo de espera establezca, ya sea en el comando o como argumento para el constructor de TransactionScope, no parece abordar el problema. La forma en que abordaré el problema es dividir los commits. Espero que esto ayude

11

La respuesta aceptada actual es que una transacción LTM no promovida (no MSDTC) nunca puede estar en duda. Después de mucha investigación sobre un problema similar, he encontrado que esto es incorrecto.

Debido a la forma en que se implementa el protocolo de confirmación de fase única, hay un pequeño período de tiempo donde la transacción está "en duda", después de que el administrador de transacciones envía la solicitud SinglePhaseCommit a su subordinado y antes de que el subordinado responda ya sea un mensaje de compromiso/abortado/o preparado (se necesita promocionar/escalar a MSDTC). Si la conexión se pierde durante este tiempo, entonces la transacción está "en duda", b/c el TransactionManager nunca recibió una respuesta cuando le pidió al subordinado que realizara un SinglePhaseCommit.

De MSDN Single-Phase Commit, también ver la imagen "monofásico cometer flujo" en la parte inferior de esta respuesta:

Hay una posible desventaja de esta optimización: si el administrador transacción pierde el contacto con el participante subordinado después de enviar la solicitud de compromiso monofásico pero antes de recibir una notificación de resultado , no tiene un mecanismo confiable para recuperar el resultado real de la transacción. En consecuencia, el administrador de transacciones envía un resultado duda de que las aplicaciones o los votantes espera de la notificación resultado informativo

También aquí son algunos ejemplos prácticos de cosas que he encontrado que la promoción de la causa System.Transaction/escalada a un MSDTC transacción (esto no está directamente relacionada con el PO, pero he encontrado muy útil Probado en VS 2013, SQL Server 2008 R2, .NET 4.5, excepto donde se indique.):

  1. (éste es específico de SQL Server 2005 o si el nivel de compatibilidad es < 100) - Llamar a Connection.Open() más de una vez en cualquier punto withi n un TransactionScope. Esto también incluye llamar a .Open(), .Close(), .Open() en la instancia de conexión SAME.
  2. Apertura anidado conexiones dentro de un TransactionScope
  3. El uso de múltiples conexiones que no utilizan la agrupación de conexiones, incluso si no están anidados y la conexión a la misma base de datos.
  4. Consultas que implican servidores vinculados
  5. Procedimientos de SQL CLR que utilizan TransactionScope. Ver: http://technet.microsoft.com/en-us/library/ms131084.aspx "TransactionScope debe usarse solo cuando se accede a fuentes de datos locales y remotas o administradores de recursos externos. Esto se debe a que TransactionScope [dentro de CLR] siempre causa que las transacciones promuevan, incluso si se usa solo dentro de una conexión de contexto "
  6. Parece que si usa la agrupación de conexiones, y la misma conexión física exacta que se usó en Connection1 no está disponible por algún motivo en las Conexiones" 2 a N ", se promoverá toda la transacción (b/c éstas se tratan como 2 recursos duraderos separados, el ítem n. ° 2 es la lista oficial de MS a continuación). No he probado/confirmado este caso particular, pero entiendo cómo funciona. Tiene sentido b/c detrás de las escenas esto es similar a usar conexiones anidadas o no usar la agrupación de conexiones b/c se usan conexiones físicas múltiples. http://msdn.microsoft.com/en-us/library/8xx3tyca(v=vs.110).aspx "Cuando se cierra una conexión y se devuelve al grupo con una transacción alistada System.Transactions, se reserva de tal manera que la siguiente solicitud de ese grupo de conexiones con la misma transacción System.Transactions devolverá la misma conexión si está disponible. Si se emite una solicitud, y no hay conexiones agrupadas disponibles, se establece una conexión de la parte no transaccionada del grupo y se enlista "

Y aquí está la lista oficial de MS de las causas escalada: http://msdn.microsoft.com/en-us/library/ms229978(v=vs.85).aspx

  1. Al menos un recurso duradero que no admite notificaciones monofásicas está enlisado ted en la transacción.
  2. Al menos dos recursos duraderos que admiten notificaciones monofásicas se alistan en la transacción. Por ejemplo, alistar una sola conexión con SQL Server 2005 no provoca que se promueva una transacción. Sin embargo, cada vez que abre una segunda conexión a una base de datos de SQL Server 2005 que hace que la base de datos se aliste, la infraestructura System.Transactions detecta que es el segundo recurso duradero en la transacción y lo escala a una transacción MSDTC.
  3. Se invoca una solicitud para "ordenar" la transacción a un dominio de aplicación diferente o a un proceso diferente. Por ejemplo, la serialización del objeto de transacción a través de un límite de dominio de aplicación. El objeto de transacción se clasifica por su valor, lo que significa que cualquier intento de pasarlo a través de un límite de dominio de aplicación (incluso en el mismo proceso) da como resultado la serialización del objeto de transacción. Puede pasar los objetos de transacción haciendo una llamada en un método remoto que toma una transacción como parámetro o puede intentar acceder a un componente remoto con servicio transaccional. Esto serializa el objeto de transacción y los resultados en una escalada, como cuando una transacción se serializa en un dominio de aplicación. Se está distribuyendo y el administrador de transacciones local ya no es adecuado.

Single phase commit flow