高橋 理香
SQL Developer Escalation Engineer
みなさん、こんにちは。
次は接続エラーを再度・・・と予告していたのですが、今回も接続タイムアウトについての情報を書きたいと思います。
接続タイムアウト値はどこで設定するのか
前回の「Troubleshooting Connectivity #6 - 接続タイムアウトは悪なのか?」に記載したように、接続タイムアウトの設定は、接続確立完了までに要する時間として許容する限界値を定めたものであり、かつ、障害が発生しているかを見極めるための時間の設定です。したがって、接続をしようとしている側 (アプリケーションやツール等) で設定するものであり、サーバー側で設定するものではありません。たとえば、サーバーがダウンしている場合を考えてみてください。接続相手がいない状態でリクエストを行うわけですから、もしサーバー側での設定があったとしたらそれは機能できませんね。
このように、接続タイムアウト値は接続をリクエストする際にそのリクエストを行う側がその接続に要してもいい時間を設定するものですので、基本的にはアプリケーションにてその設定を行うものとお考えください。
接続タイムアウト値はどのように働くのか
では、接続タイムアウト値を設定したはいいけど、指定した時間になったらちょうどでエラーになるのでしょうか。
残念ながらか、気が利いているのかは要件によるのですが、ちょうどの時間ではエラーにならないパターンがあります。
先に例にあげたサーバーがダウンしている場合がこのパターンに該当しますので、その動作について以下の2点の機能に分けて説明します。
A: 接続に使用するプロトコルの自動切り替え
B: プロトコル自体のタイムアウトおよび再転送設定
A: 接続に使用するプロトコルの自動切り替え
SQL Server へアクセスするドライバーやプロバイダーの多くは、接続時には以下のように複数のプロトコルでの接続試行を行うよう設計されています。
ADO.NET の System.Data.SqlClient (.NET Framework Data Provider for SQL Server) を使用してリモートの SQL Server に接続する場合を例にすると以下の通りです。
1) TCP/IP プロトコルで接続試行する。
2) TCP/IP プロトコルによって OS レベルのセッション確立がタイムアウトした場合、名前付きパイプに切り替えて接続試行する。
3) 名前付きパイプによるセッション確立もタイムアウトした場合、接続タイムアウトと判断してエラーを返す。
これは、可能な限りサーバー側で待ち受けているプロトコルに応じて接続が可能となる確率をあげるために行っていることです。先に記載した「気を利かせている」機能ですね。
したがって、サーバーがダウンしている状況において接続試行すると、既定の設定ではこれらの2つのプロトコル分の処理が行われることになります。
※参考
System.Data.SqlClient のプロトコルに関する既定の動作については以下に記載があります。
SqlClient Default Protocol Order
http://blogs.msdn.com/b/adonet/archive/2010/04/18/sqlclient-default-protocol-order.aspx
B: プロトコル自体のタイムアウトおよび再転送設定
System.Data.SqlClient などの SQL Server に接続に使用されるドライバやプロバイダーでは、ソケット関数を呼び出して、その結果を待ってから接続タイムアウト時間を経過しているかどうかを判断する実装となっています。これは、ソケット関数の処理の完了までに要する時間が接続タイムアウト設定値には左右されないことを意味します。
また、TCP/IP プロトコルは双方向通信のプロトコルであり、何かリクエストを送信した場合にはその応答があるまで待つのが基本の動作です。そのため、接続先のサーバーが停止している場合、そのサーバーは応答できませんので、接続リクエスト元では来ない応答を待つことになります。
わかりやすい例をあげると、電話したら通話音は鳴るけれども誰も出ない状態があります。
みなさんは何コールくらいで電話を切りますか?また、何回くらいかけてみて誰も出なければ不在なのだろうと考えますか?
私は 10コールで切ってすぐに1回かけなおし、5-10分おいて1回かけたら不在、そんなところです。
Windows では、サーバーからの応答の待ち時間 (タイムアウト) は初回 3秒、再試行回数は既定で 2回で設定されています。また、再試行時には前回の倍の秒数を待ちます。したがって、ソケット関数がタイムアウトするまでには 3 + 6 + 12 = 21 秒を要することになります。上位層ではこの完了までを待つため、例えば System.Data.SqlClient で接続タイムアウト値を 5秒にしていたとしても、21秒は待つことになります。
名前付きパイプの場合も、サーバーが停止している状態では、接続のリクエストのパケット送信によるセッション確立からの開始になるため、TCP/IP の場合と同様のタイムアウト値となります。
接続タイムアウトのエラーまでの時間を制御できるか
先の Aおよび Bの機能により、接続タイムアウトまでの時間が設定した時間よりも長い時間が経過してから発生する可能性があることをおわかりいただけたかと思います。
では、この時間を制御することはできるのでしょうか。
残念ながら厳密な制御はできませんが、それぞれの機能の面から以下の2つのいずれかの方法でより短い時間でエラーとすることが可能です。
A に対する方法: 接続試行は単一のプロトコルのみで行う
B に対する方法: TCP/IP の再転送回数を減らす
一例ですが、System.Data.SqlClientにおいては以下のいずれかの方法で単一のプロトコルを使用できるようになります。
- Network Library キーワードを使用する。
例えば接続文字列に Network Library=dbmssocn を指定すると TCP/IP のみを使用することになります。
- Data Source に指定する IP アドレスやインスタンス名にプロトコルを明示する。
例えば Server01 インスタンスにアクセスする場合には Data Source=Server01と記載しますが、名前付きパイプで接続したい場合には以下のように変更します。
Data Source=np:Server01
.NET Framework アプリケーションの場合は、どちらも MSDN リファレンスの以下のページにそれぞれの指定方法についての記載がありますのでご覧ください。
SqlConnection.ConnectionString プロパティ
http://msdn.microsoft.com/ja-jp/library/system.data.sqlclient.sqlconnection.connectionstring(v=vs.100).aspx
接続時の再転送回数設定を調整するには
・アプリケーション実行環境が Windows Server 2008 / Vista 以降のバージョンの場合
以下から入手できる修正プログラムを導入することで接続時の再転送回数の設定を netsh コマンドによって調整できます。
Hotfix enables the configuration of the TCP maximum SYN retransmission amount in Windows 7 or Windows Server 2008 R2
http://support.microsoft.com/kb/2786464
・アプリケーション実行環境が Windows Server 2008 / VIsta 以前のバージョンの場合
Windows Vista 以前ではレジストリの TcpMaxConnectRetransmissions の値を設定することで接続試行の再転送回数を制御可能ですが Windows Vista 以降ではこのレジストリは存在しません。
TCP/IP Registry Values for Microsoft Windows Vista and Windows Server 2008
http://www.microsoft.com/en-us/download/details.aspx?id=9152Appendix A: TCP/IP Configuration Parameters
http://technet.microsoft.com/ja-jp/library/cc739819(v=ws.10).aspx
今回の接続タイムアウト時の動作についての情報が何か役立つようであればうれしいです。
さて次回はスキップしたエラーについてまとめられたらと思っています。