SQL 服务器 Service Broker 外部激活器检查点错误

SQL Server Service Broker External Activator Checkpointing Error

我们使用 Microsoft 的 Service Broker 外部激活器服务已有一段时间了,可以毫无问题地处理 Service Broker 队列的外部激活。但是在过去的一周里,一个错误不断发生,我无法深入了解。

每天至少一次,在随机时间,服务会遇到错误并卡在停止状态。此时能做的就是杀掉进程,重启服务。检查 EATrace.log 文件显示以下错误:

18/07/2018 09:59:45 EXCEPTION
ERROR = 90, Internal exceptions have occurred when External Activator is runtime checkpointing.
18/07/2018 09:59:45 EXCEPTIONDETAILS Inner Exception:
18/07/2018 09:59:45 EXCEPTIONDETAILS System.IO.IOException: Cannot create a file when that file already exists.
18/07/2018 09:59:45 EXCEPTIONDETAILS 
18/07/2018 09:59:45 EXCEPTIONDETAILS    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
18/07/2018 09:59:45 EXCEPTIONDETAILS    at System.IO.File.Move(String sourceFileName, String destFileName)
18/07/2018 09:59:45 EXCEPTIONDETAILS    at ExternalActivator.LogManager.SaveRecoveryContext(LogRecoveryContext recoveryContext)
18/07/2018 09:59:45 EXCEPTIONDETAILS    at ExternalActivator.LogManager.Checkpoint(LogRecoveryContext recoveryContext)
18/07/2018 09:59:45 EXCEPTIONDETAILS    at ExternalActivator.LogManager.Log(LogRecord recoveryLogRec)
18/07/2018 09:59:45 EXCEPTIONDETAILS    at ExternalActivator.ApplicationMonitor.OnProcessExited(ProcessMonitor processMonitor)
18/07/2018 09:59:45 EXCEPTIONDETAILS    at ExternalActivator.ProcessMonitor.NotifySubscriber()
18/07/2018 09:59:45 EXCEPTIONDETAILS    at ExternalActivator.ProcessMonitor.OnProcessExited(Object a, EventArgs b)

使用 Reflector 我发现了有问题的 SaveRecoveryContext 方法:

private void SaveRecoveryContext(LogRecoveryContext recoveryContext)
    {
      this.m_file = File.Open(this.TempLogFileName, FileMode.Create, FileAccess.Write, FileShare.Read);
      foreach (LogRecord recoveryLogRec in recoveryContext.List)
        this.Write(recoveryLogRec);
      this.CloseFiles();
      File.Delete(this.LogFileName);
      File.Move(this.TempLogFileName, this.LogFileName);
      this.m_file = File.Open(this.LogFileName, FileMode.Append, FileAccess.Write, FileShare.Read);
    }

请注意,LogFileName 是 EARecovery.rlog,TempLogFileName 是 EARecovery_temp.rlog。错误发生后检查日志文件夹时,只有临时文件,原始文件已按预期删除。

我的第一个想法是,也许多个线程同时尝试检查点并相互绊倒,但向上堆栈跟踪将我们带到以下内容:

public void Log(LogRecord recoveryLogRec)
    {
      lock (this)
      {
        this.Write(recoveryLogRec);
        if (!recoveryLogRec.CanCompress)
          return;
        ++this.m_recordsToCompress;
        if (this.m_recordsToCompress <= 100)
          return;
        LogRecoveryContext local_0 = new LogRecoveryContext();
        string local_1 = Global.GetEaContext();
        Global.SetEaContext(Localized.GL_EaContext_RuntimeCheckpoint);
        this.Checkpoint(local_0);
        Global.SetEaContext(local_1);
      }
    }

我原以为 lock 语句可以防止这种情况发生。

除了通常的 Windows 更新和负载没有增加外,服务器上没有任何变化,但由于某种原因,这个错误从 7 月 16 日左右开始出现。打开详细日志记录,我可以看到它的检查点比我预期的要多得多,并且当错误发生时,它总是在前一个检查点的一两秒内。

任何关于下一步去哪里的帮助或指示都将不胜感激,因为我正在为这个而烦恼。

这不是 Service broker 特有的,但我以前见过这种情况,尤其是代码:

  File.Delete(this.LogFileName);
  File.Move(this.TempLogFileName, this.LogFileName);

如果后台进程(如防病毒软件)使文件保持打开状态,则文件可能不会立即消失。如果您使用 运行 防病毒软件,例如 Windows Defender 或其他产品,您可能需要将文件夹列入白名单,这样它就不会尝试扫描文件。

我能提出的唯一其他建议(同样无关)是清除 Windows 临时文件夹。几年前,我 运行 遇到了一些问题,其中创建了许多临时文件导致 Windows 出现问题,具体取决于它们在内部命名或跟踪它们的方式。我没有 link 给你,我认为这不是你的问题,但你可能想尝试一下。