IEの制限の影響によりWCF Data Services クライアントにおいて、300秒で'System.InvalidOperationException'が発生することがある

 

横井 羽衣子
SQL Developer Support

みなさんごきげんよう。今回は、WCF Data Services とデータアクセスを行う Silverlight アプリケーションにおいて、DataServiceQuery.EndExecute でキッチリ 300 秒で内部エラーが発生してしまうという現象についてお話したいと思います。

「DataServiceQuery.EndExecute で内部エラー」というと、WCF Data Services か、あるいは Silverlight 側を疑ってしまいがちではないでしょうか。ところが、今回ご紹介する現象は、実はクライアント側、それも Internet Explorer (IE) の制限事項の影響を受けた結果なのです。
WCF Data Services など、Web 系のアプリケーション、サービスにおいては、様々な要素が絡み合って機能を実現しています。たとえば "メモ帳" などのように、ローカルで閉じた環境で、他プロセスなどが介在しないようなアプリケーションで問題が発生した時とは異なります。まずシステムの構成要素と動作時の処理の流れを把握することが最も重要です。

今回の場合エラーが一般的問題を表すだけの内容の上、クライアントといっても、例外は Silverlight で発生していますので、 その先の IE 側まで着目せず、惑わされやすいパターンと言えます。
image

問題
Silverlight から WCF Data Services に要求を投げてデータ取得を行うシステムを実装している。 システム要件として、複数の画面用の処理を同時実行したいため、以下のような動作の流れとなっている。

1. WCF Data Services Client (Silverlight 側) において DataServiceQuery.BeginExecute を実行
2. WCF Data Services Client において別のページ用の処理のための DataServiceQuery.BeginExecute を実行 
3. 以下例外がクライアントにて発生

'System.InvalidOperationException' の初回例外が System.Data.Services.Client で発生しました。 上位例外 : 変更の保存中にエラーが発生しました。詳細については、内部例外を参照してください。 'iexplore.exe' (Silverlight): 'c:\Program Files (x86)\Microsoft Silverlight\5.1.10411.0\ja\mscorlib.resources.dll' が読み込まれました。 上位例外 : 場所 System.Data.Services.Client.BaseAsyncResult.EndExecute[T](Object source, String method, IAsyncResult asyncResult) 場所 System.Data.Services.Client.QueryAsyncResult.EndExecute[TElement](Object source, IAsyncResult asyncResult) 場所 System.Data.Services.Client.DataServiceQuery`1.EndExecute(IAsyncResult asyncResult) 場所 SampleWeb.MainPage.<>c__DisplayClass2.<BeginQueryData>b__0(IAsyncResult asyncResult) 内部例外 : 場所 System.Data.Services.Http.HttpWebResponse.NormalizeResponseStatus(Int32& statusCode) 場所 System.Data.Services.Http.HttpWebResponse..ctor(HttpWebRequest request, Int32 statusCode, String responseHeaders) 場所 System.Data.Services.Http.HttpWebRequest.CreateResponse() 場所 System.Data.Services.Http.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult) 場所 System.Data.Services.Client.QueryAsyncResult.AsyncEndGetResponse(IAsyncResult asyncResult) 内部例外 : 'HttpWebResponse.NormalizeResponseStatus' での内部エラーです。

この動作を切り分けてみた方は、以下の状況を確認できたのではないでしょうか。

- WCF Data Services から WCF Data Services Client にデータは正しい形で到達している。
- すべてのデータが返るまでに時間を要する場合(データ量が多い場合)に発生している。       - クエリの取得件数を絞るとエラーは発生しない。
- 実行されているクエリを SQL Server Management Studio (SSMS) で実行すると約 30、40 秒程度要するような比較的大きいクエリが流れている。      
- DataServiceQuery.BeginExecute をループなどで 5 回など複数回実行した場合のみ高確率で発生。1 回だけの呼び出しでは発生していない。
- クエリ実行開始から、常に約 300 秒でエラーになる
- サーバートレースを取るとExecution Warnings (Query Wait) が発生している。      
- デリゲートを介さず実行するように変更しても問題は変わらない。
- コールバックを明示的に非同期で対応するようにプロパティなどを設定しておいても効果がない。
- エンティティを使いまわしても問題は解消しない。
- WCF トレースを採取しても何もエラーは発生していない。
- WCF Data Services の Web.config で、executionTimeout の設定に追加して、sendTimeout 等を追加しても効果はない。

原因
この問題は、WCF Data Service ではなく、クライアントの Internet Explorer のタイムアウトの影響に起因しています。
Data Source (SQL Server データベースなど) からのクエリの結果が WCF Data Services に返され、そのデータがさらにクライアントに返される際、XMLHTTPRequest が用いられます。
この、XMLHTTP でクライアントにデータが返されるまでの時間が、Internet Explorer のタイムアウト値の時間を超える場合に、処理が中断されてしまい、結果としてこの例外がクライアント側で発生することになります。 よって、この現象については、WCF Data Services としてトレースを採取いただいても、問題として検知されません。

今回の問題に関係するタイムアウトの設定が、以下のレジストリ キーとなります。ここを延長することで、問題の回避が可能です。
なお、どの程度延長すべきかは、システムの設計次第といえます。ユーザーが使用するアプリケーションであれば、あまり長いと使用に耐えないということになってしまいます。この場合、既定 5 分以上でも結構長いのではないかと思いますので、クエリのチューニングなどを検討してもいいかもしれません。
バッチなどで比較的待機することにデメリットが少ないという場合などは、10 分など、クエリの実行時間を SSMS で計測し、それに合わせて調整するということなども良いかと思います。

キー : HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\InternetSettings
値の種類 : REG_DWORD
名前 : ConnectTimeOut データ : ミリ秒単位の時間 (10進)
既定値 : 300000 ミリ秒 (=5分)
(既定では、レジストリは存在しませんので、明示的にキーを作成いただく必要があります。)

参考情報
Silverlight 用 WCF Data Services クライアント ライブラリ
https://msdn.microsoft.com/ja-jp/library/ff650916(v=vs.95).aspx
→ "データ サービスに対するほとんどの要求に関して、Silverlight 用 WCF Data Services クライアントは XMLHTTP の実装を使用します。"

Internet Explorer 8 における XMLHttpRequest の強化
https://msdn.microsoft.com/ja-jp/library/cc534581(v=vs.85).aspx
→ "Internet Explorer 8 では、非同期 JavaScript および XML (AJAX) 要求をより細かく制御できます。 具体的に、開発者は XMLHttpRequest オブジェクトのタイムアウトを指定できるようになりました。"

具体的にはクライアント処理において、内部で WinInet の以下のステータスを返された時に、この問題が発生します。
環境によっては、300 秒以内で 12152 が発生する場合もありますが、300 秒を超えた場合は、いずれの環境においても 12002 が発生し続けます。

12152 ERROR_HTTP_INVALID_SERVER_RESPONSE
12002 ERROR_INTERNET_TIMEOUT

INFO: WinInet Error Codes (12001 through 12156)
https://support.microsoft.com/default.aspx?scid=kb;EN-US;193625

ちなみに、今回は WCF トレースが残念ながらあまり役に立たないケースでしたが、WCF トレースについては、今後ブログに書いていこうと思います。

それではみなさん、ごきげんよう。