我らがおーみさんが、.NET Framework 4.6.1 is now available! の投稿を共有されていて知ったのですが、System.Data.SqlClient に ConnectRetryCount / ConnectRetryInterval というプロパティが追加されていたので、どのような動きになるかを少し試してみました。
キター transient faults 時のretry 実装 Improve connection open resiliency when connecting to Azure SQL Database https://t.co/TqqzZIYLNh #azurejp
— OMI Takekazu (@takekazuomi) November 30, 2015
マルチサブネットフェールオーバーの設定の動作がデフォルトになっているっぽいのも興味深いですが。
これらのプロパティですが、「アイドル状態の接続の失敗があったことの識別後」の動作となるため、初回の接続については成功している必要があるようです。
そのため、コネクションをオープンするときではなく、コネクションをオープン後に、コマンドを実行するタイミングでコネクションが切断されていた場合のリトライの動作となるようですね。
まずは、以下のようなスクリプトを実行してみます。
[System.Data.SqlClient.SqlConnection]::ClearAllPools() $Error.Clear() $Connection = New-Object System.Data.SqlClient.SqlConnection $ConnectionString = New-Object System.Data.SqlClient.SqlConnectionStringBuilder $ConnectionString.psbase.DataSource = "localhost" $ConnectionString.psbase.IntegratedSecurity = $true $ConnectionString.psbase.ConnectTimeout = 5 $Connection.ConnectionString = $ConnectionString Clear-Host "{0} {1}" -f (Get-Date), "TEST Start" try{ $Connection.Open() }catch{ "{0} {1}" -f (Get-Date), "Cpnnection Error" return } $Command = $Connection.CreateCommand() $Command.CommandType = [System.Data.CommandType]::Text $command.CommandTimeout = 30 $Command.CommandText = "SELECT @@SERVERNAME" while($true){ try{ "{0} {1}" -f (Get-Date), "Execute" $ret = $Command.ExecuteReader() $ret.Read() > $null "{0} {1}" -f (Get-Date), $ret[0] $ret.Close() Start-Sleep 1 }catch{ "{0} {1} {2}" -f (Get-Date), "Command Error", $Error[0] break } } $Connection.Close() $Connection.Dispose()
単純にループしながら、クエリを実行するスクリプトになります。
このスクリプトを実行している際に、以下を動作させて、SQL Server を一度再起動させてみます。
Add-Type -Assembly System.Windows.Forms Stop-Service MSSQLSERVER Start-Sleep -Seconds 10 Start-Service MSSQLSERVER [System.Windows.Forms.MessageBox]::Show("Service Start")
今回はコマンドタイムアウトを 30 秒にしているのですが、SQL Server のサービス停止後に実行されたクエリがタイムアウトを待たずにエラーとなっているとが確認できますね。
“0” 個の引数を指定して “ExecuteReader” を呼び出し中に例外が発生しました: “接続が中断されたため復元できません。クライアント ドライバーは接続の復元を 1 回以上試みましたが、すべての試行は失敗しました。ConnectRetryCount の値を増やして、復元の試行回数を増やしてください。
それでは、次に以下を追加して実行してみます。
$ConnectionString.psbase.ConnectRetryCount = 1 $ConnectionString.psbase.ConnectRetryInterval = 5
こちらもエラーとなりますね。
それでは、以下を設定するとどうなるでしょう。
$ConnectionString.psbase.ConnectRetryCount = 5 $ConnectionString.psbase.ConnectRetryInterval = 5
この設定の場合は、エラーは発生せずにクエリを実行することができますね。
ConnectRetryCount / ConnectRetryInterval ですが、コマンドタイムアウトと合わせて、設定値を検討する必要があるようです。
今回は、以下のような環境でテストをしています。
- コマンドタイムアウト = 30 秒
- SQL Server のサービスを停止してから起動するまでのスリープ = 10 秒
ConnectRetryCount / ConnectRetryInterval ですが、コマンドタイムアウトの秒数を全体として、その秒数の中でどのような間隔で何回リトライをするかを指定する設定となるようですね。
ConnectRetryCount = 1 / ConnectRetryInterval =5 の場合は、接続がアイドル状態でコマンドが実行された場合、30秒のコマンドタイムアウトの期間の中で、、5 秒間隔で 1 回は接続の再試行を実行し、それで実行できなかった場合はエラーとするという動作となっている感じでしょうか。
今回、SQL Server は 10 秒程度停止の状態となっているため、コマンドタイムアウトが 30 秒で設定されていてもその中での再試行の間隔が 5 秒間隔で 1 回のため、最初に試した設定では指定した再試行間隔内で SQL Server が起動していないためエラーとなります。
ConnectRetryCount = 5 / ConnectRetryInterval =5 の場合は 30 秒のコマンドタイムアウトの期間の中で、5 秒間隔で 5 回、接続の再試行を実行するため、10 秒程度 SQL Server が停止していたとしても、コマンドタイムアウト内の接続再試行の範囲内ですのでエラーとならない動作となっているのかと。
それでは、以下のような設定をするとどうなるでしょう。
$ConnectionString.psbase.ConnectRetryCount = 10 $ConnectionString.psbase.ConnectRetryInterval = 1 $command.CommandTimeout = 5
“0” 個の引数を指定して “ExecuteReader” を呼び出し中に例外が発生しました: “次の再接続の試行はクエリ タイムアウトを超過します。再接続は終了しました。
接続の再試行は、1 秒間隔で 10 回の設定となっていますが、コマンドタイムアウトが 5 秒になっているため、コマンドタイムアウト期間内に設定した再試行の間隔をすべて実施することができません。
そのため、再接続の試行がクエリタイムアウトを超過したというメッセージでエラーとなっていますね。
コマンドタイムアウトと組み合わせて使うことで、有効に働いてくるプロパティな感じなんですかね。