Transaction scope timeout on 10 minutes
Asked Answered
A

10

43

I have a long running TransactionScope in C#. I told the scope that it should have a long timespan, but still I get a timeout. What could cause this?

TransactionOptions transactionOptions = new TransactionOptions();
transactionOptions.IsolationLevel = IsolationLevel.ReadCommitted;
transactionOptions.Timeout = TimeSpan.MaxValue;
using (var ts = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
{ 
    DoLongCode();
}
Arvonio answered 21/8, 2012 at 13:5 Comment(4)
As @nonnb mentions, you'll need to set the timeout on the SQL calls, or the object context as well.Giblets
Ouch; why would you want a 10-minute TransactionScope? I would be getting extremely anxious if I had a TransactionScope that lasted more than a couple of seconds. Long-running transactions can severely impact all other callers... You also need to consider the commit/rollback cost; on many platforms, it is "rollback" that pays a penalty (commit being cheap); if this has done a lot of work in the 10 minutes, the rollback could be a killer.Plasm
It is indeed not a desired situation but I have to deal with it atm. It is a weakly process that has a terrible performance.Arvonio
@MarcGravell Because sometimes you just have to move a lot of data.Colt
V
26

Hello you can verify maxTimeout in your config file, if you don't have this section on your web.config or app.config

Verify your machine.config

<configuration> 
  <system.transactions>
    <machineSettings maxTimeout=""/>
  </system.transactions>
</configuration> 

Adjust the value

Verse answered 21/8, 2012 at 13:10 Comment(4)
In Windows Azure this cannot be changed, 10 minutes is the maximum timeout.Gilley
Yes it's possible to override machine.configVerse
According to this thread on MSDN you cannot override the maxTimeout value in your web.config or app.config file. social.msdn.microsoft.com/Forums/da-DK/…Kookaburra
@Kookaburra I really hate Microsoft for this. Why should I change a system setting when there is just this single app which needs extraordinary behavior. Have they ever thought about exclusive mode local DBs like 'LocalDB', 'SQLCe', 'SQLite'? NO!Roundhead
I
46

To allow transaction to take more than 10 minutes, without need to change machine.config, use this code

    private void SetTransactionManagerField(string fieldName, object value)
    {
        typeof(TransactionManager).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, value);
    }

    public TransactionScope CreateTransactionScope(TimeSpan timeout)
    {
        // or for netcore / .net5+ use these names instead:
        //    s_cachedMaxTimeout
        //    s_maximumTimeout
        SetTransactionManagerField("_cachedMaxTimeout", true);
        SetTransactionManagerField("_maximumTimeout", timeout);
        return new TransactionScope(TransactionScopeOption.RequiresNew, timeout);
    }

Usage:

using (var ts = CreateTransactionScope(TimeSpan.FromMinutes(20)))
{ 
    DoLongCode();
    ts.Complete();
}

Based on this article The code of article was originally pasted here. The code in the answer is now refactored and simplified.

Incur answered 3/3, 2015 at 23:45 Comment(4)
Not how I wished to implement it, but It's a solution and works for me (Azure WebJob)Machute
I like this solution a lot as in many environments you have no access to the machine.config and this solution does work as intended.Underdog
For me, the fields were named s_cachedMaxTimeout and s_maximumTimeout, so I've changed it to try either field name and use what works. This is still pretty much a hack (as noted in the original article), but sometimes there really is no other option.Renteria
Which perfectly demonstrates the problem with this hack. It breaks easily. This should never be used in production code. Who knows, in a next version these members may be completely gone and you have a big problem.Serpentine
S
43

To further clarify:

Transaction Scope uses the Machine config setting as the maximum timeout. The default machine timeout is 10 minutes.

Setting the machine config to 2 hours:

      <system.transactions>
        <machineSettings maxTimeout="02:00:00"/>
      </system.transactions> 

The app.config or web.config can be used reduced to the timeout but can not be used to exceed the machine config timeout.

Setting the app config to 1 hour:

<system.transactions>
     <defaultSettings timeout="01:00:00" />
</system.transactions>

Also we did NOT receive any exceptions when the limit was reached, also no trace or event log records.

Also the TransactionScope object has constructor overloads which allow you to specify a timeout, but I'm not sure how that is handled.

Sulfa answered 17/10, 2012 at 19:29 Comment(0)
V
26

Hello you can verify maxTimeout in your config file, if you don't have this section on your web.config or app.config

Verify your machine.config

<configuration> 
  <system.transactions>
    <machineSettings maxTimeout=""/>
  </system.transactions>
</configuration> 

Adjust the value

Verse answered 21/8, 2012 at 13:10 Comment(4)
In Windows Azure this cannot be changed, 10 minutes is the maximum timeout.Gilley
Yes it's possible to override machine.configVerse
According to this thread on MSDN you cannot override the maxTimeout value in your web.config or app.config file. social.msdn.microsoft.com/Forums/da-DK/…Kookaburra
@Kookaburra I really hate Microsoft for this. Why should I change a system setting when there is just this single app which needs extraordinary behavior. Have they ever thought about exclusive mode local DBs like 'LocalDB', 'SQLCe', 'SQLite'? NO!Roundhead
M
7

They dont' work because is the wrong context where you are trying to change timeout.

try to change it closer to the effective query.

You should have these contexts:

    using (var txn = new TransactionScope(
                            TransactionScopeOption.Required,
                            new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted, Timeout = new TimeSpan(1,0,0) })) // 1 hour or wathever, will not affect anything
                    {

                        using (SqlConnection connection = new SqlConnection(ConnectionString))
                        {
                            int ct = connection.ConnectionTimeout // (read Only, this is the effective default timeout is 15 seconds)
                            connection.Open();

                            SqlCommand select = new SqlCommand(sql.query, connection); // bind to server
                            select.CommandTimeout = 0; // <-- here does apply infinite timeout
SqlDataReader reader = select.ExecuteReader(); // never stop
Moorish answered 18/10, 2015 at 19:16 Comment(0)
E
4

I resolve this problem modifying the "physical file" machine.config.


1. You have to localize the file:

  • 32 Bits: C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machie.config
  • 64 Bits: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config

2. You have to add the following code:

<system.transactions>
     <defaultSettings timeout="00:59:00" />
</system.transactions>
Euchromatin answered 1/7, 2016 at 20:45 Comment(0)
B
3

Considering a full trust environment, you can override max timeout using reflection:

            //Get machineSettings session
            var machineSettings = (System.Transactions.Configuration.MachineSettingsSection)ConfigurationManager.GetSection("system.transactions/machineSettings");
            //Allow modifications
            var bReadOnly = (typeof(ConfigurationElement)).GetField("_bReadOnly", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            bReadOnly.SetValue(machineSettings, false);
            //Change max allowed timeout
            machineSettings.MaxTimeout = TimeSpan.MaxValue;

            using (var t = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(1,0,0))) { //1 hour transaction
                //...
            }
Bertle answered 11/11, 2014 at 13:29 Comment(1)
Possible limitation: it should be called before any transaction (works fine in console, but not in web app).Calistacalisthenics
O
2

If anyone is wondering how to achieve this on .NET Core, this is the answer. Basically, you have to update the fields' names:

_cachedMaxTimeout -> s_cachedMaxTimeout

_maximumTimeout -> s_maximumTimeout

Obeng answered 23/8, 2021 at 17:21 Comment(1)
Not sure at what point it was changed, but current Core library has a normal setter for it. System.Transactions.TransactionManager.MaximumTimeout = ...; System.Transactions.TransactionManager.DefaultTimeout = ...;Theatrician
D
0

You can add this code in your project to extend the transaction time.

// This is used for set the transaction timeout to 40 minutes.
Type oSystemType = typeof(global::System.Transactions.TransactionManager);
System.Reflection.FieldInfo oCachedMaxTimeout = 
                    oSystemType.GetField("_cachedMaxTimeout", 
                    System.Reflection.BindingFlags.NonPublic | 
                    System.Reflection.BindingFlags.Static);
System.Reflection.FieldInfo oMaximumTimeout = 
                    oSystemType.GetField("_maximumTimeout", 
                    System.Reflection.BindingFlags.NonPublic | 
                    System.Reflection.BindingFlags.Static);
oCachedMaxTimeout.SetValue(null, true);
oMaximumTimeout.SetValue(null, TimeSpan.FromSeconds(2400));
Dichlamydeous answered 4/7, 2018 at 8:10 Comment(2)
Please... reflection should never be used to set inaccessible members because, basically, you don't know what you're doing. There are legal method to do what OP wants. Also, this is a repetition of an existing answer.Serpentine
@GertArnold I would generally agree that setting the value of a private member of a type that you do not own is a very bad idea. However I do not agree for this specific scenario. AFAIK the only officially supported way to increase the maximum for the transaction timeout is modifying the machine.config. However this is something we are all to often not allowed to do, because of the IT policies that are in place. Almost none of my customers would allow anyone to make such a change. Therefore using the mentioned hack is AFAIK the only possible alternative solution to this problem.Suricate
A
0

TransactionScope has multiple constructors. Some of them allow you to change Timeout without config changes.

using (var scope = new TransactionScope(new TransactionScopeOption(), TimeSpan.FromMinutes(3), TransactionScopeAsyncFlowOption.Enabled))
{
    // Long operation here

    scope.Complete();
}
Anonymous answered 24/4, 2023 at 16:13 Comment(0)
T
0

In .NETCore (tested in NET6) you don't need to alter the machine config or use reflection.

You can just write:

TransactionManager.MaximumTimeout = TimeSpan.FromMinutes(60);

for a timeout of one hour.

Tabular answered 22/9, 2023 at 14:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.