• 从报错“无效操作,连接被关闭”探究Transaction的Timeout超时机制


    1、报错如下:Invalid Operation the connection is closed,无效操作,连接被关闭。这个错误是并不是每次都报,只有在复杂操作、大事务的情况下才偶然报出来。

    stackOverflow上有很多关于这个问题的讨论,例如这个:《System.Data.OracleClient random Invalid Operation the connection is closed》,但较零散,全扫了一遍之后,我仍然有如下疑问:

    1)怎么看TransactionScope里的Timeout到底是多少,比如通过TransactionOptions.Timeout = new TimeSpan(0,0,3)设置之后,怎么知道TransactionScope里的超时就是3s?

    2)默认MaxTimeout=10m,怎么才能设置任意值?

    3)嵌套事务的外层超时是否包括所有内层事务的时间?

    4)超时机制是怎么起作用的?

    通过Reflector之类的工具查看源码,终于有了初步的理解,不对的地方,请大家指正!

    2、怎么看TransactionScope里的Timeout?

    用调试器逐级展开TransactionScope实例如下:

    scope->committableTransaction->base->internalTransaction->absoluteTimeout //注:这是展开过程,不是代码

    这个absoluteTimeout就是实际结果,但往往与你设置的值不匹配,比如:

    TimeSpan absoluteTimeout
    3s 5
    10s 19
    1m 117
    10m 1171
    30m 3515
    1h 7031

    看起来是经过某个算法转换后的值,这个算法写在TransactionTable.TimeoutTicks()里:

    internal long TimeoutTicks(TimeSpan timeout){
      if(timeout != TimeSpan.Zero) {
        return (timeout.Ticks / 10000L >> 9) + this.ticks;
      } 
      return 9223372036854775807L;
    }

    这就清楚了,再加上上面那张对照表,就可以通过absoluteTimeout清楚知道设置是否生效。

    3、默认MaxTimeout=10m,怎么才能设置任意值?

    1)先说一下这个10m怎么来的?

    TransactionScope.ctr()->TransactionManager.ValidateTimeout()->MaximumTimeout->MachineSettingsSection.MaximumTimeout

    这个属性先读取c:windowsMicrosoft.NETFrameworkv4.0.30319Configmachine.config里的值://注意对应的framework的版本!

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

    当然,我查看下来,所有版本的machine.config里默认都没有这个节点。那么退一步,由Attribute设置默认值:

    [ConfigurationProperty("maxTimeout", DefaultValue = "00:10:00")]
    public TimeSpan MaxTimeout

    2)再说一下怎么改成任意值?

    2.1)修改machine.config,优点:方便,缺点:会影响机器上的所有事务。方法是在全局的machine.config里添加对应节点,注意在自己的app/web.config里添加<machineSettings>节点会报错。

    2.2)用反射(神器)!优点:只作用于自己的事务,缺点:用反射、稍麻烦、稍慢。

    static void ChangeMaximumTimeout() {
     Type t = typeof(TransactionManager);
     FieldInfo f = t.GetField("_cachedMaxTimeout", BindingFlags.NonPublic | BindingFlags.Static);
     f.SetValue(null, true);
     f = t.GetField("_maximumTimeout", BindingFlags.NonPublic | BindingFlags.Static);
     f.SetValue(null, new TimeSpan(2, 0, 0));
    }

    这么写是因为最终大家取的都是static类里的static属性:

    public static class TransactionManager {
     public static TimeSpan MaximumTimeout {
      get {
       if(!_cachedMaxTimeout) {
         _maximumTimeout = MachineSettings.MaxTimeout;
       }
       return _maximumTimeout; }}}

    贴出来,一目了然!

    4、嵌套事务的外层超时是否包括所有内层事务的时间?

    我理解外层超时包含所有内层事务的执行时间,测试代码如下:

    option.Timeout = new TimeSpan(0,0,3);
    using(var scope = new TransactionScope(TransactionScopeOption.Required, option)) {
     Thread.Sleep(2000);
    
     option1.Timeout = new TimeSpan(0,0,5);
     using(var scope1 = new TransactionScope(TransactionScopeOption.Required, option)) {
      Thread.Sleep(500); // 800以上基本就报超时错误了
    }}

    5、超时机制是怎么起作用的?

    1)对于非根(committableTransaction == null)的事务,会构造一个scopeTimer = new Timer(timeSpan, TimeoutCallback)来触发超时回滚;

    2)对于committableTransaction != null的根事务,超时机制尚未找到,有待补充完整。

  • 相关阅读:
    你真的了解JSON吗?
    FormData对象
    javascript类数组
    Windows环境下XAMPP的相关设置
    PhpStorm相关设置
    yarn 与 npm 比较
    JavaScript+HTML+CSS 无缝滚动轮播图的两种方式
    javascript数据类型和类型转换
    焦大:以后seo排名核心是用户需求点的挖掘
    焦大:seo思维进化论(番外)
  • 原文地址:https://www.cnblogs.com/AlexanderYao/p/4065575.html
Copyright © 2020-2023  润新知