8

このADOエラーメッセージをグーグルで検索すると、ASP.NET開発で一般的に発生することがわかりますが、Delphiアプリケーションでいつ発生するかについてはあまり言及されていません。一時的なネットワークの問題が発生しているお客様のサイトがいくつかありますが、これは症状のあるエラーメッセージです。オフィステストで簡単に複製できます。delphiTADOConnectionオブジェクトがそのサーバーインスタンス上のデータベースに接続されている間にMSSQLServerサービスをシャットダウンするだけで、次の例外が発生します。

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

はい、この例外をキャッチすると、このエラーが発生したことがわかります(またはわかりますか?)。これが、データベースアクションの周りのブロックを除いて10,000回を超える試行を伴う800 KLOC +アプリケーションであることを除いて、いずれもこのエラーで失敗する可能性があります。

TADOConnectionいくつかのエラーイベントがありますが、この場合はいずれも発生しません。ただし、これが発生するとADO接続自体に障害が発生します。SQLデータベースを再起動しても、TADOConnection.Connectedはtrueのままですが、それはあなたに嘘をついています。本当に故障状態です。

それで、私の質問は:

この障害状態を検出し、それから回復することはできますか?ブロックを除いて10,000回の個別の試行を行い、グローバルな「再接続ADOグローバル変数」を設定するよりも少ない作業で済みますか?

TADOConnection.ConnectionObject(基になる生のOLEDB COM ADOオブジェクト)にアクセスして、新しいクエリを開始するときにこの障害状態が存在することを検出する方法があることを期待しています。これにより、ADOConnectionをリセットして、次に実行するときに続行できます。クエリ。私たちのコードは、10行のデモアプリケーションでこれを行う方法よりもはるかに簡単に「障害後」にこれを検出できるように編成されているためです。

この他のSOの質問は、なぜそれが起こるのかを尋ねます、それは私が求めているものではありません、私に「予防」の答えを与えないでください、私はすでにそれらについて知っています、私は回復と失速したADOの検出を探しています-例外をキャッチする以外の接続手法。実際、これは例外がうまくいかなかった良い例です。ADOは、この障害モードのschrodingers-catオブジェクトです。

私はMSナレッジベースの記事とインターネット上に浮かんでいるさまざまなソリューションを知っています。エラー状態(私たちの状況では一時的なものであることが多い)が解消されたら、顧客データを失うことなく回復することについて質問しています。つまり、アプリをフリーズし、お客様に例外を表示し、お客様が[再試行]または[続行]をクリックすると、修復して続行を試みます。既存のコードは100万回の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か所にコピーして貼り付ける必要があることです。

4

3 に答える 3

8

さて、誰もこの質問に答えていません、そして私はいくつかのフォローアップが役立つと思います。

これが私が学んだことです:

  • テスト環境でこの一般的なネットワークエラーを再現できる信頼できる状況はありません。つまり、私たちは再現不可能な結果を​​扱っています。これは、多くの開発者が壊れたシステムを「モンキーパッチ」しようとして邪悪なハッカーに飛び込むところです。

  • SQLライブラリで「一般的なネットワークエラー」が発生した場合、根本的な障害を修正することは、コードで修正するよりも常に優れています。通常、「ネットワークの信頼性が低いため、TCP自体がデータの配信をあきらめている」ことを意味するため、修復が可能であることが示されたことはありません。これは次の場合に発生します。

    • ネットワークケーブルが不良です。

    • ネットワーク上に重複するIPアドレスがあります。

    • それぞれが異なるデフォルトゲートウェイを処理するDHCPサーバーを決闘しています。

    • ローカルイーサネットセグメント間の接続が不十分です。

    • 障害が発生しているイーサネットスイッチまたはハブがあります。

    • 誤動作しているファイアウォールによって断続的にブロックされています。

    • 顧客がネットワーク上の何かを変更し、ソフトウェアを使用できなくなった可能性があります。(この最後の1つは、実際にはあなたが思っているよりも多く発生します)

    • cliconfg誰かが、単一のワークステーションのレジストリ設定に固有の、または他のクライアント側の構成要素を使用してSQLエイリアスを構成した可能性があります。このローカル構成は、診断が困難な不正な動作を引き起こし、大規模なワークステーションの1つまたは複数のワークステーションに制限される可能性があります。通信網。

上記のいずれも、TCPレベルまたはSQLレベルで検出および報告することはできません。SQLが最終的に諦め、この「一般的なネットワークエラー」が発生した場合、ソフトウェアからのキャジョリングによって諦めなくなることはありません。たとえそうだったとしても、「try/except」を実行します。 /ignore」アンチパターン。このエラーは非常に深刻であるため、ユーザーまで発生させ、エラーログでディスクに記録し、あきらめて(プログラムを終了)、ネットワーク接続がダウンしていることをユーザーに通知する必要があります。

于 2012-11-30T14:57:15.727 に答える
2

私もこれが悪いコーディングのために起こっているのを見ました。

接続を使用してレコードセットを開き、最初の接続が閉じられていないときに同じ接続を別のレコードセットのループで再利用すると、同様のエラーが発生する可能性があります。

Webアプリケーションで非常にまれな別の機会は、アプリケーションプールがリサイクルされているときに、同様のエラーが発生する可能性があることです。

同じサーバーに異なるサイトがあり、同じアプリケーションで異なるカスタマイズを使用している場合、この問題の原因となっているのは1つのサイトだけです。それが上記の発見につながります。

このブログは私が問題を見つけるのを助けました:

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

于 2013-09-03T15:32:43.977 に答える
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.
于 2020-01-26T17:13:16.920 に答える