Catalyst で DB への commit / rollback のためにこんなコードを書いていて……
package MyWebApp; # 略 sub finalize { my $c = shift; my $r = $c->NEXT::finalize(@_); if (正常終了) { $dbh->commit; } else { $dbh->rollback; } return $r; }
他の finalize が全部終わってから、エラーがなければ commit、エラーがあったら rollback をしよう、という意図だったんですけど。
いわゆる PRG パターン (POST-Redirect-GET) をやった場合に、先の POST で更新されているはずのデータが GET で読み取れない、というケースが発覚。
上の書き方だと commit の前に他の finalize が実行されて、そこでレスポンスがクライアントに送られてしまうんですね。先に。
そのレスポンス (リダイレクト指示) を受け取ったクライアントは GET を発行して、新しいトランザクションを開始してデータを読むわけですが、運が悪いと、その時点ではまだ先の POST 処理のトランザクションが commit されていない場合がある。
ということで、以下のように、先にトランザクションを完了させてから NEXT::finalize を呼べばいいんですが……
sub finalize { my $c = shift; if (正常終了) { $dbh->commit; } else { $dbh->rollback; } return $c->NEXT::finalize(@_); }
そもそもこういう DB の commit 処理って、finalize でやるべきものなのかな、という疑問も。どうするのが一般的なんでしょうか……