Как распределенные транзакции ведут себя с несколькими подключениями к одной и той же БД в многопоточной среде?
Я пытаюсь определить поведение соединения нескольких баз данных в распределенной транзакции.
У меня есть длительный процесс, который порождает серию потоков, и каждый поток затем отвечает за управление своими подключениями к БД и тому подобное. Все это выполняется внутри области транзакции, и каждый поток зачисляется в транзакцию черезDependentTransaction
объект.
Когда я решил поставить этот процесс параллельно, я столкнулся с несколькими проблемами, а именно с тем, что, похоже, существует какой-то блок, препятствующий одновременному выполнению запросов в транзакции.
Я хотел бы знать, как координатор транзакций обрабатывает запросы от нескольких подключений к одной и той же БД и целесообразно ли передавать объект подключения между потоками?
Я читал, что MS SQL допускает только одно соединение на транзакцию, но я ясно могу создать и инициализировать более одного соединения с одной и той же БД в одной транзакции. Я просто не могу выполнять потоки параллельно, не получая исключение «контекст транзакции используется другим сеансом» при открытии соединений. В результате соединения должны ждать выполнения, а не запускаться в одно и то же время, и в конце код выполняется до завершения, но нет никакой выгоды для многопоточности приложения из-за этой проблемы с блокировкой.
Код выглядит примерно так.
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
РЕДАКТИРОВАТЬ
Мои тесты убедительно показали, что это просто невозможно сделать. Даже если используется более одного соединения или используется одно и то же соединение, все запросы в транзакции или вопросы обрабатываются последовательно.
Возможно, они изменят это поведение в будущем.