モルモルしている

最近はテスト業務ばっかりで書くことがない

TransactionScopeのTimeout

今日はTimeOutではまった。

前提として、今作っているプログラムはTransactionScopeをかけて順次登録されたストアドを呼び出して実行するプログラム。
実行するストアドが一つでもエラーを返したらロールバックして全部なかったことにする。

そんなプログラム用のストアドを作成していざプログラムを実行したら
Timeout に達しました。操作が完了する前にタイムアウト期間が過ぎたか、またはサーバーが応答していません。
というエラーが返ってきた。

気にしていなかったけどDapperのCommandTimeoutってどうやって設定すんの?と思ったけど

public static int Execute(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null);

↑この通りで普通に引数に指定できたのでラッキーと思って指定した。
CommandTimeoutの単位がわからなかったけど、SqlCommandにTimeOut値渡しているだけだろうしDapperのソースから↓の記述を見つけたのでたぶん「秒」であってる。

/// <param name="commandTimeout">Number of seconds before command execution timeout</param>


これで大丈夫だろうと試しにプログラムを実行してみたら今度は
現在の接続と関連付けられているトランザクションは完了しましたが、破棄されていません。トランザクションは、接続が SQL ステートメントの実行に使用される前に破棄されなければなりません。
というエラーが返ってきて滝汗。

一瞬ストアド内でTransactionが自動コミットされてしまうような記述があったかと思ったけど特にない。
というかTransactionScopeはCREATE TABLEでもロールバックしてくれるのにまさかそんな、と。

しかもデータ量が少ない時には起こらない。データ量が多いとこのエラーが起きる。
処理内容は変わらないのに何がトランザクションを完了させているのか、わからないままストアドの処理を改善していたらエラーが出るタイミングが変わった。
1本目(一番重い処理)でエラーが出てたけど、データ量や処理方法を検討する中で2本目、3本目と後ろにずれてった。
これもしかして処理方法の問題じゃなくて…と思って「TransactionScopeのTimeout」について調べた。
そもそもTransactionにTimeoutがあること自体正直意識してなかったけど。

調べたところ、TransactisonScopeのTimeout初期値は60秒のようです。
http://msdn.microsoft.com/ja-jp/library/ms973865.aspx#introsystemtran_topic12.NET Framework 2.0 の System.Transactions について

なので↓参考URLさんの通りTransactionScopeに長めのTimeout値を設定したらエラーが出なくなった。
TransactionScopeのタイムアウト設定 (TransactionScope, TransactionScopeOption, TimeSpan) - いろいろ備忘録日記

でも↓の通り、TransactionScopeで何分を設定しても規定で最大値が10分にされており、10分で結局エラーになってしまうそうだ。
MachineSettingsSection.MaxTimeout プロパティ (System.Transactions.Configuration)

最大値を変えたければmachine.configに以下の設定を追加しないといけないのだそうです。

<configuration>
  <system.transactions>
    <machineSettings maxTimeout="01:00:00" />
  </system.transactions>
</configuration> 

そしてそんなmachine.configは↓の場所にあります。

32bitなら C:\Windows\Microsoft.NET\Framework\(.Net Frameworkのバージョン)\config\machine.config
64bitなら C:\Windows\Microsoft.NET\Framework64\(.Net Frameworkのバージョン)\Config\machine.config

とりあえず10分に延ばしたけど、10分で処理が全部完了する保証はないから上司が出張から帰ってきたら相談しなくちゃ。。。
しかしTimeoutに達したならTimeoutになったと言ってくれればいいのにわかりにくいエラーメッセージでいやだなー。

2015.11.16 追記
同僚に「で、machine.configにどう追加すればいいの?」って言われて久しぶりすぎてどこにどう追加したか思い出せなかったのでメモっとく。
machine.configの一番最後に移動しての前に

<system.transactions>
  <machineSettings maxTimeout="01:00:00" />
</system.transactions>

を貼り付ければOK(私は実際には↓15分を指定してみた)
f:id:MoruMe:20151116212357p:plain

OSは64bitでもPGが特に64bit用というわけでなければ見ているのは32bitの「C:\Windows\Microsoft.NET\Framework\(.Net Frameworkのバージョン)\config\machine.config」の方の設定のようです。
設定がちゃんと有効になっているかどうかは TransactionManager.MaximumTimeout で確認することができるので、MessageBox.Show( TransactionManager.MaximumTimeout.ToString())とかで値を見て、異なってたら設定するmachine.configを見直してください。