Edited at

ASP.NET で SQLのコマンドがタイムアウトした場合の対応方法

More than 3 years have passed since last update.

ASP.NET で重たいクエリをSQL Serverに投げたところ、タイムアウトしてしまいました。

その対処についての記録です。

ログには「Timeout に達しました。操作が完了する前にタイムアウト期間が過ぎたか、またはサーバーが応答していません。」のメッセージ。

スタックトレースはこんな感じ。

   場所 System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)

場所 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
場所 System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)
場所 System.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()
場所 System.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()
場所 System.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()
場所 System.Data.SqlClient.TdsParserStateObject.TryReadByteArray(Byte[] buff, Int32 offset, Int32 len, Int32& totalRead)
場所 System.Data.SqlClient.TdsParserStateObject.TryReadByteArray(Byte[] buff, Int32 offset, Int32 len)
場所 System.Data.SqlClient.TdsParser.TrySkipValue(SqlMetaDataPriv md, Int32 columnOrdinal, TdsParserStateObject stateObj)
場所 System.Data.SqlClient.TdsParser.TrySkipRow(_SqlMetaDataSet columns, Int32 startCol, TdsParserStateObject stateObj)
場所 System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
場所 System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
場所 System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
場所 System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
場所 System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
場所 System.Data.SqlClient.SqlCommand.ExecuteNonQuery()

時間に関する設定は3か所。

(1)web.configのconnectionString属性にConnection Timeout=180;を追記しました。

 <connectionStrings>   

<add name="DefaultConnection" connectionString="Data Source=XXXXX;Initial Catalog=XXXXX;Persist Security Info=False;Integrated Security=False;User Id=XXXXX;Password=XXXXX;Connection Timeout=180;" providerName="System.Data.SqlClient" />
</connectionStrings>

(2)web.configのhttpRuntimeにexecutionTimeout属性を追加し180秒に設定しました。

<system.web>

<httpRuntime executionTimeout="180" />
</system.web>

(3)ソースコードの中でコマンドのタイムアウト時間を180秒に設定

  SqlCommand cmd = new SqlCommand("ExportCSV", cn);

・・・・中略
cmd.CommandTimeout = 180;
cmd.ExecuteNonQuery();

私は(3)のコマンドのタイムアウト時間について漏れていてハマりました。