Alexandros Pappas Ответов: 1

Как работает тайм-аут транзакции COM+?


Я не понимаю, как работает тайм-аут транзакции в COM+. У меня есть класс ClassComPlus, который наследуется от ServicedComponent, имеет атрибут transaction timeout, установленный на 1 секунду, и имеет метод SubSleep, который спит 3 секунды. Я ожидаю, что клиент, который вызывает метод SubSleep, получит исключение, потому что тайм-аут транзакции истек, но я не получаю такого исключения, метод завершается без исключения.

Что я уже пробовал:

Вот код в VB.NET:

1. проект Test1BO.vbproj как библиотека классов, подписанная сильным ключом, имеет два файла:

1.1 файле AssemblyInfo.ВБ:

Imports System.EnterpriseServices
Imports System.Reflection
Imports System.Runtime.InteropServices
<assembly: applicationactivation(activationoption.server)>
<assembly: applicationaccesscontrol(ctype(accesschecksleveloption.application, boolean))>
<assembly: guid("799facfd-af56-4496-bc18-618e2522e5f7")>
<assembly: assemblyversion("1.0.0.0")>
<assembly: assemblyfileversion("1.0.0.0")>


1.2 ClassComPlus.vb

Imports System.EnterpriseServices

    <transaction(transactionoption.required, isolation:="TransactionIsolationLevel.ReadCommitted", timeout:="1"), _ eventtrackingenabled(true), justintimeactivation(true)> _
    Public Class ClassComPlus
        Inherits ServicedComponent

        Public Sub SubSleep()
            Try
                Threading.Thread.Sleep(3000)
                ContextUtil.SetComplete()
            Catch
                ContextUtil.SetAbort()
                Throw
            End Try
        End Sub
    End Class

Test1BO должен быть зарегистрирован в COM+, например, со следующим файлом RegisterComPlus.bat в папке, где создается dll:
set regsvcs=C:\Windows\Microsoft.NET\Framework\v4.0.30319\regsvcs
set topdir=%~dp0
set dllname=Test1BO
%regsvcs% /u "%topdir%\%dllname%.dll"
%regsvcs% "%topdir%\%dllname%.dll"
pause


2. проект Test1CA.vbproj как консольное приложение со ссылкой на Test1BO.dll имеет только Module1.vb:

Module Module1
        Sub Main()
            Dim obj = New Test1BO.ClassComPlus
            Try
                obj.SubSleep()
                Console.WriteLine("SubSleep finished normally (unexpected)")
            Catch ex As Exception
                Console.WriteLine("SubSleep threw exception (expected)")
                Console.WriteLine(ex.ToString)
            Finally
                obj.Dispose()
            End Try
            Console.ReadKey()
        End Sub
    End Module

Я ожидал бы, что, поскольку тайм-аут транзакции составляет 1 секунду, а время сна-3 секунды, оператор obj.SubSleep вызовет исключение. Вместо этого obj. SubSleep завершается нормально.

1. Что я делаю не так?
2. Правильно ли я понял тайм-аут транзакции?

1 Ответов

Рейтинг:
2

Alexandros Pappas

С помощью Microsoft я наконец понял, как работает тайм-аут транзакции COM+. Тайм-аут транзакции COM+ - это тайм-аут транзакции, а не вызова метода. Метод будет выполняться до тех пор, пока он должен выполняться. В тот момент, когда первая транзакция зачислена, тайм-аут транзакции начинает работать. Если транзакция занимает больше времени, чем указанный тайм-аут, транзакция помечается для прерывания, но не отменяется, она продолжает выполняться. Когда метод COM+ вызывает SetComplete, только тогда возникает исключение. Исключение таймаута заключается в следующем:

Система.Время Выполнения.InteropServices.ComException: {"корневая транзакция хотела совершить фиксацию, но транзакция прервалась (исключение из HRESULT: 0x8004E002)"}.

Транзакция регистрируется, как только открывается соединение с базой данных или как только что-то записывается в очередь сообщений.

Предположим, что тайм-аут транзакции составляет 10 секунд. Рассмотрим следующие 3 случая:

1. метод COM+ немедленно подключает транзакцию, которая занимает 11 секунд.
В этом случае исключение тайм-аута генерируется через 11 секунд, поскольку транзакция заняла больше времени, чем тайм-аут.
2. Метод COM+ спит в течение 6 секунд (или выполняет некоторые вычисления), а затем подключает транзакцию, которая занимает 5 секунд.
В этом случае метод завершается без ошибок, хотя общее время выполнения также составляет 11 секунд.
3. Метод COM+ немедленно подключает транзакцию, которая занимает 5 секунд, а затем спит в течение 6 секунд.
В этом случае исключение тайм-аута генерируется через 11 секунд, поскольку время между зачислением транзакции и вызовом SetComplete составляет 11 секунд, что больше тайм-аута.

Таким образом, если время между первым зачислением транзакции и вызовом SetComplete больше тайм-аута транзакции, то возникает исключение тайм-аута. Однако метод COM+ всегда выполняется до тех пор, пока он должен выполняться, независимо от времени ожидания транзакции.

Чтобы проверить вышеперечисленные случаи, вот код в VB.NET:

1. проект Test1BO.vbproj как библиотека классов, подписанная сильным ключом, имеет два файла:

1.1 файле AssemblyInfo.ВБ:

Imports System.EnterpriseServices
Imports System.Reflection
Imports System.Runtime.InteropServices
<Assembly: ApplicationActivation(ActivationOption.Server)>
<Assembly: ApplicationAccessControl(CType(AccessChecksLevelOption.Application, Boolean))>
<Assembly: Guid("799facfd-af56-4496-bc18-618e2522e5f7")>
<Assembly: AssemblyVersion("1.0.0.0")>
<Assembly: AssemblyFileVersion("1.0.0.0")>

1.2 ClassComPlus.vb
Imports System.EnterpriseServices
Imports System.Data.SqlClient
Imports System.Transactions

<Transaction(TransactionOption.Required, isolation:=TransactionIsolationLevel.Serializable, timeout:=10), _
 EventTrackingEnabled(True), _
 JustInTimeActivation(True)>
Public Class ClassComPlus
    Inherits ServicedComponent

    Public Function DbExecuteNonQuery(
        connectionString As String, cmdText As String,
        sleepSecondsBefore As Integer, sleepSecondsAfter As Integer) As Integer
        Try
            Threading.Thread.Sleep(sleepSecondsBefore * 1000)
            Dim result = 0
            Using cn = New SqlConnection(connectionString)
                cn.Open()
                Dim cmd = New SqlCommand(cmdText, cn)
                result = cmd.ExecuteNonQuery()
            End Using
            Threading.Thread.Sleep(sleepSecondsAfter * 1000)
            ContextUtil.SetComplete()
            Return result
        Catch
            ContextUtil.SetAbort()
            Throw
        End Try
    End Function

Test1BO должен быть зарегистрирован в COM+, например, со следующим файлом RegisterComPlus.bat в папке, где создается dll:
set regsvcs=C:\Windows\Microsoft.NET\Framework\v4.0.30319\regsvcs
set topdir=%~dp0
set dllname=Test1BO
%regsvcs% /u "%topdir%\%dllname%.dll"
%regsvcs% "%topdir%\%dllname%.dll"
pause


2. проект Test1CA.vbproj как консольное приложение со ссылкой на Test1BO.dll имеет только Module1.vb:
Module Module1
Private Sub MyTrace(s As String)
    Console.WriteLine(String.Format("{0} {1}", Now.ToString("yyyy-MM-dd HH:mm:ss"), s))
End Sub

Private Sub TestDbExecNonQuery()
    Dim obj = New Test1BO.ClassComPlus
    Dim connectionString = ConfigurationManager.ConnectionStrings("DB1").ConnectionString
    Dim cmdText = "WAITFOR DELAY '00:00:011'"
    Try
        MyTrace("TestDbExecNonQuery begin " & cmdText & ", 0, 0")
        obj.DbExecuteNonQuery(connectionString, cmdText, 0, 0)
        MyTrace("TestDbExecNonQuery end")
    Catch ex As Exception
        MyTrace("TestDbExecNonQuery exception")
        MyTrace(ex.Message)
    End Try
    cmdText = "WAITFOR DELAY '00:00:05'"
    Try
        MyTrace("TestDbExecNonQuery begin " & cmdText & ", 6, 0")
        obj.DbExecuteNonQuery(connectionString, cmdText, 6, 0)
        MyTrace("TestDbExecNonQuery end")
    Catch ex As Exception
        MyTrace("TestDbExecNonQuery exception")
        MyTrace(ex.Message)
    End Try
    Try
        MyTrace("TestDbExecNonQuery begin " & cmdText & ", 0, 6")
        obj.DbExecuteNonQuery(connectionString, cmdText, 0, 6)
        MyTrace("TestDbExecNonQuery end")
    Catch ex As Exception
        MyTrace("TestDbExecNonQuery exception")
        MyTrace(ex.Message)
    End Try
    obj.Dispose()
End Sub

Sub Main()
    TestDbExecNonQuery()
    Console.WriteLine("Press any key to close")
    Console.ReadKey()
End Sub
End Module

При выполнении вышеизложенного ожидается следующий результат:

2016-12-09 16:47:17 TestDbExecNonQuery begin WAITFOR DELAY '00:00:011', 0, 0
2016-12-09 16:47:29 TestDbExecNonQuery exception
2016-12-09 16:47:29 The root transaction wanted to commit, but transaction aborted (Exception from HRESULT: 0x8004E002)
2016-12-09 16:47:29 TestDbExecNonQuery begin WAITFOR DELAY '00:00:05', 6, 0
2016-12-09 16:47:40 TestDbExecNonQuery end
2016-12-09 16:47:40 TestDbExecNonQuery begin WAITFOR DELAY '00:00:05', 0, 6
2016-12-09 16:47:51 TestDbExecNonQuery exception
2016-12-09 16:47:51 The root transaction wanted to commit, but transaction aborted (Exception from HRESULT: 0x8004E002)
Press any key to close


F-ES Sitecore

"Тайм-аут транзакции COM+ - это тайм-аут транзакции"

Худафанк? :)

Patrice T

Примите ваше решение, чтобы закрыть вопрос.