Estoy tratando de determinar el comportamiento de la conexión de bases de datos múltiples en una transacción distribuida.¿Cómo se comportan las transacciones distribuidas con múltiples conexiones al mismo DB en un entorno enhebrado?
Tengo un proceso de larga ejecución que genera una serie de subprocesos y cada subproceso es responsable de administrar sus 'conexiones de base de datos y tal. Todo esto se ejecuta dentro del alcance de la transacción y cada hilo se alista en la transacción a través de un objeto DependentTransaction
.
Cuando fui a poner este proceso en paralelo me encontré con algunos problemas, es decir, que parece haber algún tipo de bloqueo que impide que las consultas se ejecuten al mismo tiempo en la transacción.
Lo que me gustaría saber es cómo el coordinador de transacciones maneja las consultas de múltiples conexiones a la misma base de datos y si incluso es aconsejable pasar un objeto de conexión a través de subprocesos?
He leído que MS SQL solo permite una conexión por transacción, pero claramente puedo crear e inicializar más de una conexión al mismo DB en la misma transacción. Simplemente no puedo ejecutar los subprocesos en paralelo sin obtener una excepción de "Contexto de transacción en uso por otra sesión" al abrir las conexiones. El resultado es que las conexiones tienen que esperar para ejecutarse en lugar de ejecutarse al mismo tiempo y, al final, el código se ejecuta hasta su finalización, pero no hay ganancia neta para enhebrar la aplicación debido a este problema de bloqueo.
El código se parece a esto.
Sub StartThreads()
Using Scope As New TransactionScope
Dim TL(100) As Tasks.Task
Dim dTx As DependentTransaction
For i As Int32 = 0 To 100
Dim A(1) As Object
dTx = CType(Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete), DependentTransaction)
'A(0) = some_other_data
A(1) = dTx 'the Dependent Transaction
TL(i) = Tasks.Task.Factory.StartNew(AddressOf Me.ProcessData, A) 'Start the thread and add it to the array
Next
Tasks.Task.WaitAll(TL) 'Wait for threads to finish
Scope.Complete()
End Using
End Sub
Dim TransLock As New Object
Sub ProcessData(ByVal A As Object)
Dim DTX As DependentTransaction = A(1)
Dim Trans As Transactions.TransactionScope
Dim I As Int32
Do While True
Try
SyncLock (TransLock)
Trans = New Transactions.TransactionScope(DTX, TimeSpan.FromMinutes(1))
End SyncLock
Exit Do
Catch ex As TransactionAbortedException
If ex.ToString.Contains("Failure while attempting to promote transaction") Then
ElseIf ex.Message = "The transaction has aborted." Then
Throw New Exception(ex.ToString)
Exit Sub
End If
I += 1
If I > 5 Then
Throw New Exception(ex.ToString)
End If
Catch ex As Exception
End Try
Thread.Sleep(10)
Loop
Using Trans
Using DALS As New DAC.DALScope
Do While True
Try
SyncLock (TransLock)
'This opens two connection to the same DB for later use.
DALS.CurrentDAL.OpenConnection(DAC.DAL.ConnectionList.FirstConnection)
DALS.CurrentDAL.OpenConnection(DAC.DAL.ConnectionList.SecondConnection)
End SyncLock
Exit Do
Catch ex As Exception
'This is usually where I find the bottleneck
'"Transaction context in use by another session" is the exception that I get
Thread.Sleep(100)
End Try
Loop
'*****************
'Do some work here
'*****************
Trans.Complete()
End Using
End Using
DTX.Complete()
End Sub
EDITAR
Mis pruebas han demostrado de manera concluyente que esto no se puede hacer. Incluso si hay más de una conexión o se utiliza la misma conexión, todas las solicitudes en la transacción o las preguntas se procesan secuencialmente.
Quizás cambien este comportamiento en el futuro.
OK veo que tiene un dilema. Pero desde la perspectiva de SQL, una transacción debe tener límites. En mi opinión, respetar una transacción a través de múltiples conexiones viola el concepto. Puedo pedir mi ensalada con mi cena, pero no quiero que la siguiente mesa diga que quieren su ensalada con la mía. – Paparazzi