Problemas con TransactionScope y Oracle
hemos escrito un cliente C # 3.5 hablando con una base de datos Oracle (11g) usando ODP.NET.
Esta aplicación tiene un proceso por lotes en el que se realiza una tarea de larga ejecución haciendo varias llamadas a la base de datos dentro de un TransactionScope.
En nuestro entorno de desarrollo, todo va bien, pero en el entorno UAT de uno de nuestros clientes (que tiene un montón de datos) se producen dos errores alternos (a veces uno, a veces el otro ...):
No se puede alistar en una transacción distribuidaLa transacción ha abortado. (excepción interna: Tiempo de espera de transacción)Actualmente utilizamos un tiempo de espera deun día para la transacción (con fines de prueba).
La ejecución de dicho proceso en el entorno UAT hace que se detenga después de aprox. 10 minutos con una de las excepciones anteriores, por lo que no se acerca el valor del tiempo de espera.
Aquí hay un fragmento del stacktrace para el segundo error:
at System.Transactions.TransactionStatePromotedAborted.CreateAbortingClone(InternalTransaction tx)
at System.Transactions.DependentTransaction..ctor(IsolationLevel isoLevel, InternalTransaction internalTransaction, Boolean blocking)
at System.Transactions.Transaction.DependentClone(DependentCloneOption cloneOption)
at System.Transactions.TransactionScope.SetCurrent(Transaction newCurrent)
at System.Transactions.TransactionScope.PushScope()
at System.Transactions.TransactionScope..ctor(TransactionScopeOption scopeOption)
at System.Transactions.TransactionScope..ctor()
at Application.Domain.DataAccess.Oracle.EntityDaoBase`2.SaveItem(TEntity item, EntityReference`1 user)
El proceso intenta guardar un elemento en la base de datos dentro del alcance de la transacción, pero el seguimiento de la pila muestra que el constructor es afectado por la clase TransactionScope, lo que significa que crea un nuevo TransactionScope.
¿Estoy en lo cierto hasta ahora?
Debido a que no sé mucho sobre el funcionamiento interno de TransactionScope, pero parece que cuando llamas a un método dentro del alcance, creará una nueva transacción (asumiendo que heredará de la transacción ambiental).
¿Podría ser que si tengo razón, que esta nueva transacción no herede el tiempo de espera correcto (sino el predeterminado), de modo que una transacción anidada causará esta excepción de tiempo de espera?
Si no, ¿alguna idea sobre lo que puede ser? En una nota al margen, no hay transacciones anidadas definidas dentro de los métodos llamados desde dentro de la transacción ambiente.
Cualquier ayuda sería muy apreciada!
Editar 1:
Fragmento de código simplificado de la función:
public void SomeLengthyBatchProcess()
{
using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(1, 0, 0, 0)))
{
foreach (var item in Items)
{
SaveItemToDB(item);
}
transaction.Complete();
}
}
public void SaveItemToDB(object item)
{
using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(1, 0, 0, 0)))
{
// Performing data persistency here
transaction.Complete();
}
}
Edición 2:
Bien, entonces resulta que hayes una transacción anidada en el método 'SaveItemToDB'. Después de investigar un poco el código que hizo un colega, vi que tiene su propio TransactionScope definido, pero sin opciones ni tiempo de espera.
Después de modificar este método para que tenga los mismos parámetros con respecto al tiempo de espera, volví a ejecutar el código en el servidor del cliente y aún no tuve suerte (nuevamente, la transacción anuló el error con el tiempo de espera).
Entonces mis preguntas son las siguientes:
¿Es necesario definir valores de tiempo de espera para transacciones anidadas o heredan esto de la transacción ambiente?¿Cómo es posible que se produzca una excepción de tiempo de espera cuando la configuración de tiempo de espera es (presumiblemente, aparte del funcionamiento interno que no conozco) la misma para todos los ámbitos de transacción y tiene un valor de tiempo de espera definido de 1 día, donde la excepción ocurre después de aprox. ¿10 minutos?¿Es posible evitar que Oracle cree una transacción distribuida para transacciones donde la cadena de conexión es la misma?¿Puede ser que la sobrecarga agregada de una transacción distribuida provoque excepciones como la transacción cancelada?Actualicé el fragmento de código para que refleje mejor la situación.
(por cierto: la segunda transacción anidada es necesaria porque el DAL también persiste por separado algunos elementos secundarios, si están presentes, y el elemento completo debería, por supuesto, revertirse si algo sale mal mientras persiste los elementos secundarios)
¡Esperemos que con esta adición sea más fácil arrojar algo de luz sobre este tema!