トランザクションで迷い頭の中で議論が起きた話。
こういう処理をしたいとします。
・大量のデータ&APIのリクエストを伴うため、1件ずつトランザクションを切らないと駄目。
そこで大量のデータのループ内でトランザクションを作成して、commit or rollbackすると思うのですが問題は例外発生後の処理ですね。
例:x~z~yのループ内で1件ずつTransactionを切るとして、z件目に例外が発生した場合
・z件目はrollbackして例外を握り潰してしてz~y件目を実行。
・z件目で例外発生して以後の処理は実行しない
↑2つのどちらかだと思うのだけど・・・結構ケースバイケースな事象な気がしてならないんです。
//ケース1 public async Task LoopTransactionCase1() { var idList = await repository.GetIdList().ConfigureAwait(false); foreach (var id in idList) { using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted }, TransactionScopeAsyncFlowOption.Enabled)) { //POSTデータを取得して作成 var data = await repository.GetPostData(id).ConfigureAwait(false); var content = new StringContent(JsonConvert.SerializeObject(data)); content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); //POST経由でAPIにリクエストを飛ばす。戻りはboolean //httpClientが失敗した場合、例外を潰しfalseを返却する。 var response = await httpClient.PostAsync("url", content).ConfigureAwait(false); if (!await deserializeResponse<bool>(response)) { throw new SystemException("failed to data registration."); } scope.Complete(); } } } //ケース2 public async Task LoopTransactionCase2() { var idList = await repository.GetIdList().ConfigureAwait(false); foreach (var id in idList) { using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted }, TransactionScopeAsyncFlowOption.Enabled)) { var data = await repository.GetPostData(id).ConfigureAwait(false); var content = new StringContent(JsonConvert.SerializeObject(data)); content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); var response = await httpClient.PostAsync("url", content).ConfigureAwait(false); if (!await deserializeResponse<bool>(response)) { scope.Dispose(); continue; } scope.Complete(); } } }
問題はケース1の場合、不正データがz件目に存在していた場合以後のデータが処理されないことです。
そのため不正なデータのせいでz + 1 ~ y件までのデータ処理が正常であっても走らない事が問題です。
ケース2の問題としては仮にx~z~yが10000件を超えるデータは全て正常だけどリクエスト先の鯖が落ちてる or clientの通信が切れてる。
などの問題があった場合、無駄な処理を10000件を実行することになります。
うーん、これはどっちがいいんだろう。。。