今までトランザクションの単位は基本的に処理の開始から終了までを範囲にすることが多かったのですが(ループがある場合はループ全体ではなく、1ループをトランザクションとみなします)、複数の処理が絡む場合、不都合が起きることがあります。
個人的にトランザクションを分けるときなどでポイントとなる点を。
失敗した場合に本当に最初からロールバックすべきか?
要は失敗した場合に、「失敗した処理」として明記するような場合、その処理自体はコミットする必要があります。
1 2 3 4 5 6 7 |
トランザクションスタート 処理1 処理2 コミット |
ではなく、
1 2 3 4 5 6 7 8 9 10 11 |
トランザクションスタート 処理1 コミット トランザクションスタート 処理2 コミット |
のようにする必要があります。
要は処理1と処理2は連続しているけれどもコミットの単位としては分割したいときなどですね。
仕様やそもそもユーザーがどのように使うかという問題になってきます。
処理1は完結しているので、次は処理2からはじめたい時(始めるのが可能な時)は処理を分ける必要があります。
あるいは単純に1処理が長すぎる場合なども適切に分けたほうが良いでしょう。
成否が確認できない、または影響が少ない
例えば完了した後、メールやプッシュ通知などを送ったことの成否の確認をしてコミットするか否かですが、そもそもこれらのサービスの成否というのは完全にはわかりません。(送信成功と出たところでユーザーに完全にメールや通知が届くかは保証できない)
そのため、コミットの外におく必要がある場合もあるでしょう。
またはこけたとしてもロールバックまでには値しない処理(ログ的なデータの書き込みなど)はトランザクションの範囲外にしたり、処理結果によるロールバックを行わないということもあるでしょう。(前回のエラーのブログのExceptionを握りつぶす時に似てます。)
ロールバックできない処理の考慮があるか
またトランザクション内では処理順番なども大切です。
例えばAPIでの登録→DBの登録とすると、DBでこけた場合、APIの処理の取り消ししなければいけなくなります(API側に取り消しする機能がない場合、宙ぶらりんのデータができてしまいます。)。
そのため、このようなロールバックが難しい処理の場合は、DBの登録→API更新などとすると処理が確定させるかどうかをAPI更新の成否で判断することができるため、APIがこけたときにDB側のロールバックをすることができます。このため整合性のあるデータ登録をすることができます。