びぼーろくっ!

誰かに見せるわけでもないけど、備忘録として。。

トランザクションで迷い頭の中で議論が起きた話。

こういう処理をしたいとします。
・大量のデータ&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件を実行することになります。

うーん、これはどっちがいいんだろう。。。