none
How does the COM+ transaction timeout work? RRS feed

  • Question

  • I do not understand, how the transaction timeout works in COM+. I have the class ClassComPlus which inherits from ServicedComponent, has transaction timeout attribute set to 1 second, and has a method SubSleep, that sleeps 3 seconds. I expect, the client which calls the method SubSleep to get an exception, because the transaction timeout has elapsed, but I do not get such an exception, the method completes without an exception. 

    Here is the code in VB.NET:

    1. Project Test1BO.vbproj as Class Library signed with a strong key, having the two files:

      1.1 AssemblyInfo.vb:

        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 must be registered in COM+ for example with the following RegisterComPlus.bat file in the folder where the dll is created:

        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. Project Test1CA.vbproj as Console Application with reference to Test1BO.dll has just 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

    I would expect, that because the transaction timeout is 1 second, but the sleep time is 3 seconds, that the statement obj.SubSleep would throw an exception. Instead, obj.SubSleep finishes normally. 

    1. What am I doing wrong? 
    2. Did I wrongly understood the transaction timeout?
    Tuesday, October 25, 2016 11:49 AM

Answers

  • With the help of Microsoft, I finally understood how the COM+ transaction timeout works. The COM+ transaction timeout, is a timeout on the transaction, not on the method call. The method will execute, as long as it has to execute. The moment the first transaction is enlisted, the transaction timeout starts to work. If the transaction takes more time than the specified timeout, the transaction is marked for abortion, but is not canceled, it continues to run. When the COM+ method calls SetComplete, only then an exception is thrown. The timeout exception is the following: System.Runtime.InteropServices.ComException: {"The root transaction wanted to commit, but transaction aborted (Exception from HRESULT: 0x8004E002)"}. A transaction is enlisted as soon as a connection is opened to the database or as soon as something is written to a message queue.

    Suppose the transaction timeout is 10 seconds. Let us consider the following 3 cases:
    1. The COM+ method immediately enlists a transaction that takes 11 seconds. 
      In this case the timeout exception is thrown after 11 seconds, because the transaction took longer than the timeout.
    2. The COM+ method sleeps for 6 seconds (or does some calculations) and then enlists a transaction that takes 5 seconds.
      In this case the method completes without an error, although the total execution time is also 11 seconds. 
    3. The COM+ method immediately enlists a transaction that takes 5 seconds, and then sleeps for 6 seconds.
    In this case the timeout exception is thrown after 11 seconds, because the time between the enlistment of the transaction and the SetΨomplete call is 11 seconds, which is more than the timeout.

    In summary: If the time between the first enlistment of the transaction and the SetComplete call is more than the transaction timeout, the timeout exception is thrown. However, the COM+ method always runs, for as long as it has to run, irrespective to the transaction timeout.

    To test the above cases, here is the code in VB.NET:

    1. Project Test1BO.vbproj as Class Library signed with a strong key, having the two files:

      1.1 AssemblyInfo.vb:

        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 must be registered in COM+ for example with the following RegisterComPlus.bat file in the folder where the dll 

    is created:

        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. Project Test1CA.vbproj as Console Application with reference to Test1BO.dll has just 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

    Running the above, the following output is expected:

    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 abort
    ed (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 abort
    ed (Exception from HRESULT: 0x8004E002)
    Press any key to close

    Microsoft assured me that it still considers COM+ an important product, and will continue to provide it for many years to come, and recommends the use of COM+ especially in intranet installations, where it is even faster than WCF.
    • Marked as answer by pappasa Friday, December 9, 2016 3:17 PM
    Friday, December 9, 2016 3:17 PM

All replies

  • Hi pappasa,

    Thank you for posting here.

    The transaction time out is a unit of work in which a series of operations occur. Transactions to ensure

    that resources are not permanently updated unless all operations within the transaction complete

    successfully.

    Within 1 second the resource was locked. 

    From the snippet code. when sleep 3 seconds, the code will execute the sentence that subsleep finished.

    You do not write some code throwing an exception.

    So it cannot catch any exception.

    Best Regards,

    Hart


    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, October 26, 2016 2:51 AM
  • As I understood the documentation, the timeout applies to the whole duration of the transaction. If the timeout is set to 1 second, then any transaction with duration more than one second is aborted by COM+ and the client gets an exception. So your interpretation of the transaction timeout, is the maximum time to wait for a lock on a resource? This means, that if I get the lock in less than 1 second, but the operation on the resource takes 100 seconds, then this transaction will not time out? I find this strange, but if it is like this, I will try it.
    Wednesday, October 26, 2016 6:18 AM
  • Hi,

    >>So your interpretation of the transaction timeout, is the maximum time to wait for a lock on a resource? 

    Yes. it is my understand. if you have latest, please tell us.

    Best  Regards,

    Hart


    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, October 27, 2016 6:39 AM
  • Can you please provide the reference where this is documented? I have never seen this in the documentation, but something might have escaped me.
    Tuesday, November 1, 2016 11:32 AM
  • Hi pappasa,

    Here is my provided the reference.

    1. https://www.safaribooksonline.com/library/view/com-net/0596001037/ch04s12.html

    Here is explanation.

    COM+ allows you to configure a maximum execution time for your transactions. If your transaction reaches that timeout, COM+ aborts it automatically. Transaction timeouts prevent resource manager deadlocks from hanging the system.

    I also find a good explanation about transaction time out.  please refer to it.

    I wish these information will help you.  The previous reply is my understand.

    Best Regards,

    Hart

    Note: This response contains a reference to a third party World Wide Web site. Microsoft is providing this information as a convenience to you. Microsoft does not control these sites and has not tested any software or information found on these sites; Therefore, Microsoft cannot make any representations regarding the quality, safety, or suitability of any software or information found there.


    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, November 2, 2016 7:57 AM
  • In the documentation you provide, it is nowhere written that the COM+ transaction timeout is the maximum time to wait for a lock on a resource.

    The following is written:

    If your transaction reaches that timeout, COM+ aborts it automatically.

    So if I sleep in the transaction it should timeout, but it does not! Can you please provide an example that demonstrates how the transaction timeout works?

    Wednesday, November 2, 2016 11:05 AM
  • Hi pappasa,

    According to MSDN policy. we don't provide demo.

    Here are tutorials how to use com.

    Best Regards,

    Hart

    Note: This response contains a reference to a third party World Wide Web site. Microsoft is providing this information as a convenience to you. Microsoft does not control these sites and has not tested any software or information found on these sites; Therefore, Microsoft cannot make any representations regarding the quality, safety, or suitability of any software or information found there.


    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, November 4, 2016 6:35 AM
  • In the link you provide, there is no mention of COM+ transaction timeout!

    Monday, November 7, 2016 8:00 AM
  • Explanation for "How does the COM+ transaction timeout work?" already provided in locked thread https://social.msdn.microsoft.com/Forums/en-US/c7f4bebf-7337-4107-a883-72b18c83a36f/how-does-the-com-transaction-timeout-work?forum=vbgeneral along with much more information and links which OP disregards.

    La vida loca

    Tuesday, November 22, 2016 10:18 PM
  • Unfortunately, it was not answered why the COM+ transaction timeout does not work.
    Wednesday, November 23, 2016 11:21 AM
  • Unfortunately, it was not answered why the COM+ transaction timeout does not work.

    The question posed was the same question in this threads and two other threads, 4 threads with the same question, you posted. And the answer to the question was provided.

    The question was not "howcome I can not get COM+ transaction timeout to function?" but was "How does the COM+ transaction timeout work?". So please stick to the actual question and not move around about things other than the actual question. Or create a new question thread if you have new question.


    La vida loca

    Wednesday, November 23, 2016 7:04 PM
  • I cannot put everything into the subject. The question text, I think is clear: 

    I would expect, that because the transaction timeout is 1 second, but the sleep time is 3 seconds, that the statement obj.SubSleep would throw an exception. Instead, obj.SubSleep finishes normally.

    Thursday, November 24, 2016 2:34 PM
  • I cannot put everything into the subject. The question text, I think is clear: 

    I would expect, that because the transaction timeout is 1 second, but the sleep time is 3 seconds, that the statement obj.SubSleep would throw an exception. Instead, obj.SubSleep finishes normally.


    Your original question was answered in this locked thread How does the COM+ transaction timeout work?.

    Apparently you have no idea how to set systems up to use features of COM+ among numerous other information you do not know which numerous links were provided in the locked thread.

    You have been advised constantly that nobody knows this ability which is antiquated and never really used anyhow considering the lack of much about COM+ on the net after 2002. If anybody was using it then it couldn't have been many on the planet considering the difficulties required to use it which viewing all the links about it provided in your locked thread seem quite the amount.

    And then there's some program you wrote with some expectation it should work as you say. You don't know anything about COM+ apparently so why should anybody accept your program should work as you say especially when nobody knows if your systems are configured correctly and on and on.

    But like I said your question was answered. Regardless you can't apparently get anything working. Links have been provided to you about all that and yet you can't get anything working. You have been provided phone numbers to Microsoft support and you can't get anything working.

    You should advise whoever is paying you that you are wasting alot of their time and money.


    La vida loca

    Friday, November 25, 2016 12:43 AM
  • With the help of Microsoft, I finally understood how the COM+ transaction timeout works. The COM+ transaction timeout, is a timeout on the transaction, not on the method call. The method will execute, as long as it has to execute. The moment the first transaction is enlisted, the transaction timeout starts to work. If the transaction takes more time than the specified timeout, the transaction is marked for abortion, but is not canceled, it continues to run. When the COM+ method calls SetComplete, only then an exception is thrown. The timeout exception is the following: System.Runtime.InteropServices.ComException: {"The root transaction wanted to commit, but transaction aborted (Exception from HRESULT: 0x8004E002)"}. A transaction is enlisted as soon as a connection is opened to the database or as soon as something is written to a message queue.

    Suppose the transaction timeout is 10 seconds. Let us consider the following 3 cases:
    1. The COM+ method immediately enlists a transaction that takes 11 seconds. 
      In this case the timeout exception is thrown after 11 seconds, because the transaction took longer than the timeout.
    2. The COM+ method sleeps for 6 seconds (or does some calculations) and then enlists a transaction that takes 5 seconds.
      In this case the method completes without an error, although the total execution time is also 11 seconds. 
    3. The COM+ method immediately enlists a transaction that takes 5 seconds, and then sleeps for 6 seconds.
    In this case the timeout exception is thrown after 11 seconds, because the time between the enlistment of the transaction and the SetΨomplete call is 11 seconds, which is more than the timeout.

    In summary: If the time between the first enlistment of the transaction and the SetComplete call is more than the transaction timeout, the timeout exception is thrown. However, the COM+ method always runs, for as long as it has to run, irrespective to the transaction timeout.

    To test the above cases, here is the code in VB.NET:

    1. Project Test1BO.vbproj as Class Library signed with a strong key, having the two files:

      1.1 AssemblyInfo.vb:

        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 must be registered in COM+ for example with the following RegisterComPlus.bat file in the folder where the dll 

    is created:

        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. Project Test1CA.vbproj as Console Application with reference to Test1BO.dll has just 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

    Running the above, the following output is expected:

    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 abort
    ed (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 abort
    ed (Exception from HRESULT: 0x8004E002)
    Press any key to close

    Microsoft assured me that it still considers COM+ an important product, and will continue to provide it for many years to come, and recommends the use of COM+ especially in intranet installations, where it is even faster than WCF.
    • Marked as answer by pappasa Friday, December 9, 2016 3:17 PM
    Friday, December 9, 2016 3:17 PM