2012年11月30日金曜日

[GAE/J] DataStore更新時のConcurrentModificationException エラー


背景

アプリ「大喜利部」のユーザ情報の更新ロジックを追加したところ、更新が成功するときと、失敗する時があった。
成功/失敗の切り分けが難しく、原因も特定できずに、ハマりにハマって4時間以上も時間を費やしてしまったので、メモをとっておくことに。

エラー

Google App Engine のログを参照したところ、commitのタイミングで「ConcurrentModificationException」エラーが発生してました。

java.util.ConcurrentModificationException: too much contention on these datastore entities. please try again.

原因に辿り着くまで

「ConcurrentModificationException」エラーは以下が原因で発生します。
 (1)Datastoreの楽観的ロックが失敗した場合
 (2)複数のリクエストが同じエンティティを並行して「get~put」した場合

アプリ「大喜利部」ではAPIを作成して、XMLによる通信を行い、データのGET/POSTをしているわけですが、単純なファイルの更新なので、デッドロックしているような個所は見当たらない。
よって(1)の可能性はないため、考えられる可能性としては(2)。

HTMLファイルに更新するformを作成してみて、アクセスしてみる。
この場合、問題なく更新されるので、更新プログラムは問題ないはずと判断し、クライアント(APIの使用側)を疑うことに。

ユーザ情報更新ロジックに問題はなさそうだけど、なんどやっても成功と失敗を繰り返す。
ソースをデバッグ実行するとほぼ成功する。(このへんでかなり混乱しました)
悩みに悩んだ挙句、更新を非同期で行っていることに気づき、続きのロジックを読み込むと、その流れでユーザ情報を再取得していました。
Google App Engine の DataStore 更新は JDO で行っているのですが、更新を非同期でputし、更新中の情報を同時にgetしていたということになります。
まさに(2)の状態(同じエンティティをput-get)であったわけです。

対処方法

更新を同期処理に変更することで、「ConcurrentModificationException」エラーは起こらなくなりました。
※今回の場合はユーザ情報の更新なので、基本は1人しか更新と取得はしないですが、複数の人が更新するエンティティの場合は、失敗時にリトライするなどの工夫が必要です。

参考





0 件のコメント:

コメントを投稿