DBNETLIB ConnectionWrite常规网络错误导致ADO连接在Delphi应用程序中脱机时自动恢复?

时间:2012-05-25 13:47:57

标签: sql-server delphi ado

谷歌搜索此ADO错误消息表明它在ASP.NET开发中经常遇到,但我没有发现它在Delphi应用程序中何时出现。我们有一些客户站点遇到短暂的网络问题,这是症状性错误消息。我们可以轻松地在办公室测试中复制它;只需在delphi TADOConnection对象连接到该服务器实例上的数据库时关闭MS SQL Server服务,就会出现以下异常:

   [DBNETLIB][ConnectionWrite (send()).]General network error. Check your network documentation.

是的,抓住这个例外,你知道(或者你呢?)发生了这个错误。除了这是一个800 KLOC +应用程序,其中有超过10,000个围绕数据库操作的try-except块,其中任何一个都可能因此错误而失败。

TADOConnection有一些错误事件,在这种情况下都不会触发。但是,一旦发生这种情况,ADO Connection本身就会出现故障,即使重新启动SQL数据库,TADOConnection.Connected仍然是正确的,但它对你说谎。它确实处于故障状态。

那么,我的问题是:

你能否以任何方式检测这种故障状态并从中恢复,而不是进入10,000个单独的try-except块并设置一些全局“重新连接ADO全局变量”? < / p>

我希望有一种方法可以进入TADOConnection.ConnectionObject(底层的原始OLEDB COM ADO对象)并在我们启动新查询时检测到这种错误情况,这样我们就可以重置ADOConnection并继续下一步我们运行查询的时间。由于我们的代码组织方式允许我们在“故障之后”检测到这一点,这比允许我们在10行演示应用程序中执行此操作更容易。

This other SO question询问为什么会发生这种情况,我要问的是什么,请不要给我“预防”答案,我已经知道了,我正在寻找用于恢复和检测停滞ADO连接技术,而不是捕获异常。事实上,这是出错的例子;在这种失败模式下,ADO是一个schrodingers-cat对象。

我知道MS知识库文章以及互联网上的各种解决方案。一旦错误情况(在我们的情况下通常是短暂的)已经清除,我问的是RECOVERING而不会丢失客户数据。这意味着我们冻结了我们的应用程序,向客户展示了例外情况,当客户点击“重试”或“继续”时,我们会尝试修复并继续。请注意,我们现有的代码会执行一百万次try-except-log-and-continue代码,这将会妨碍我们,因此我希望有人能够回答未处理异常的应用程序处理程序是最好的方法,但遗憾的是我们不能使用它。我真的希望能够检测到冻结/故障/死ADO连接对象。

这就是我所拥有的:

try
  if fQueryEnable and ADOConnection1.Connected then begin
    qQueryTest1.Active := false;
    qQueryTest1.Active := true;
    Inc(FQryCounter);
    Label2.Caption := IntToStr(qQueryTest1.RecordCount)+' records';

  end;
except
      on E:Exception do begin
         fQueryEnable := false;
         Memo1.Lines.Add(E.ClassName+' '+E.Message);
         if E is EOleException and Pos('DBNETLIB',E.Message)>0 then begin
            ADOConnectionFaulted := boolean; { Global variable. }
         end;
         raise;
      end;
end;

上述解决方案的问题是我需要在我的应用程序中复制并粘贴大约10,000个地方。

3 个答案:

答案 0 :(得分:8)

没有人回答这个问题,我认为一些后续工作会有所帮助。

以下是我所学到的:

  • 没有可靠的情况,在测试环境中,您可以重现此常规网络错误。也就是说,我们正在处理不可复制的结果,这是许多开发人员试图“破坏”他们破碎系统的恶意hackery。

  • 当SQL库给出“常规网络错误”时,修复底层错误始终比在代码中修复它更好。从来没有证明任何修复是可能的,因为通常它意味着“网络是如此不可靠,以至于TCP本身放弃了提供我的数据”,这种情况发生在:

    • 您的网线不良。

    • 您在网络上有重复的IP地址。

    • 您已经决定各自处理不同默认网关的DHCP服务器。

    • 您的本地以太网网段之间的连接性较差。

    • 您的以太网交换机或集线器出现故障。

    • 您的防火墙出现间歇性阻塞。

    • 您的客户可能已在网络上更改了某些内容,现在可能无法使用您的软件。 (这最后一个实际发生的次数比你想象的要多)

    • 有人可能使用cliconfg或其他特定于单个工作站的注册表设置的客户端配置元素配置了SQL别名,并且此本地配置可能会导致难以诊断的错误行为,可能仅限于大型网络上的一个或多个工作站。

无论是在TCP还是SQL级别都无法检测和报告上述任何内容。当SQL最终放弃,并且它给出了“常规网络错误”时,我的软件中的任何哄骗都不会让它放弃,即使它没有放弃,我也会做“尝试/除外” /忽略“反模式。此错误非常严重,我们应该将其一直提升到用户,在错误日志中将其记录到磁盘,放弃(退出程序),并告诉用户网络连接已关闭。

答案 1 :(得分:2)

由于编码错误,我也看到了这种情况。

如果使用连接打开记录集,并且如果在第一个连接未关闭的情况下在另一个记录集的循环中重用相同的连接,则可能导致类似的错误。

Web应用程序很少出现的另一种情况是,当应用程序池回收时,您可能会收到类似的错误。

我们在同一台服务器上有不同的网站,我注意到使用相同的应用程序但具有不同的自定义,只有一个网站导致此问题。这导致了上述发现。

这个博客帮助我找到了问题:

http://offbeatmammal.hubpages.com/hub/Optimising_SQL_Server

答案 2 :(得分:1)

此处的代码检测到断开事件触发,并使用计时器重新连接。假定您意识到在阅读此代码时必须将TTimer拖放到此处显示的数据模块上,并使用以下代码创建OnTimer事件。

请检查下一个代码:

unit uDM;

interface

uses
  SysUtils, Classes, DB, ADODB, Vcl.ExtCtrls;

type
  TDM = class(TDataModule)
    ADOConnection: TADOConnection;
    ConnectionTimmer: TTimer;
    procedure ADOConnectionDisconnect(Connection: TADOConnection;
      var EventStatus: TEventStatus);
    procedure ConnectionTimmerTimer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  DM: TDM;

implementation

{$R *.dfm}

procedure TDM.ADOConnectionDisconnect(Connection: TADOConnection;
  var EventStatus: TEventStatus);
begin
  if eventStatus in [esErrorsOccured, esUnwantedEvent] then
    ConnectionTimmer.Enabled := True;
end;

procedure TDM.ConnectionTimmerTimer(Sender: TObject);
begin
  ConnectionTimmer.Enabled := False;
  try
    ADOConnection.Connected := False;
    ADOConnection.Connected := True;
  except
    ConnectionTimmer.Enabled := True;
  end;
end;

end.