凪瀬 Blog
Programming SHOT BAR

目次

Blog 利用状況
  • 投稿数 - 260
  • 記事 - 0
  • コメント - 48909
  • トラックバック - 192
ニュース
広告
  • Java開発者募集中
  • 経歴不問
  • 腕に自信のある方
  • 富山市内
  • (株)凪瀬アーキテクツ
アクセサリ
  • あわせて読みたい
凪瀬悠輝(なぎせ ゆうき)
  • Java技術者
  • お茶好き。カクテル好き。
  • 所属は(株)凪瀬アーキテクツ
  • Twitter:@nagise

書庫

日記カテゴリ

 

先日遭遇した深い落とし穴を紹介します。 以下のようなシステムを開発している方は同様の問題がないかチェックしてください。

  • AOPでトランザクションを管理している
  • 一部のマスタテーブルをDBから読み込んだあとシステム内でキャッシュしている
  • マスタメンテナンス画面などで更新があった場合にキャッシュをクリアしている

    問題となるコード

    Springや、SeasarといったDIコンテナなどに付随するAOPの機能を用いてトランザクション管理していると、 DBをいじる部分のメソッド内ではcommitとかrollbackとか記述する必要がありません。 これは非常に便利で、人的なコーディングでのcommit漏れ、rollback漏れがないので バグも入り込みにくく効率のよいものです。

    私のやっているプロジェクトではSpringframeworkのAOPの機能を用いてトランザクション管理を行っています。

    そして、システム内でよく使われ、更新頻度の低いUserテーブルは、DBアクセスのオーバーヘッドを軽減するために システム内でキャッシュしているのです。

      /** ユーザのキャッシュ */
      Map<Integer, User> cache;

      /** ユーザ取得処理 */
      public User getUser(int userId) {
        synchronized (this.cache) {
          // キャッシュにあればキャッシュされたUserを返す
          if (this.cache.containsKey(userId)) {
            return this.cache.get(userId);
          }
          // なければDBから読み込む
          return this.userDao.getUser(userId);
        }
      }

      /** キャッシュの削除処理 */
      public void clearCache(int userId) {
        synchronized (this.cache) {
          this.cache.remove(userId);
        }
      }

    キャッシュの実装は上記のようなもので、内部で同期をとってあります。
    そして、UserのUPDATE処理では

      public void update (User user) {
        // UPDATE
        this.userDao.update(user);
        // キャッシュから更新対象のUserを削除
        // 次回Userを取得しようとした時にキャッシュに最新のUserが読み込まれる
        clearCache(user.getUserId());
      }

    といった作りにしていたのです。このupdateメソッドに対してAOPでトランザクションがかけられるわけですね。

    Commit前、キャッシュクリア後の魔

    前置きが長くなりました。問題が発生するのはこのupdateの際のキャッシュのクリアの後です。

    並列して走っているスレッドがあり、update後のキャッシュクリア後、しかしDBのcommitが完了する前のタイミングで getUser()してしまったのです。
    すると、DBはcommitされる前ですから、更新前のUser情報が読み込まれ、キャッシュには古い情報が残り続けるというわけ。

    DBのトランザクションとキャッシュクリアは不可分に行わなければならないのです。
    しかし、AOPでトランザクションを管理している場合、メソッド境界を跨ぐことになるので synchronized文で容易にくくってやることができない…。

    わざわざキャッシュ管理している場所だけ特殊なトランザクション処理を別途書いてAOPを適用してやる必要が出てきてしまいました。
    似たようなシステム構成をしている方、潜在的なバグを孕んでいる可能性があります。早急にチェックするほうがよいでしょう。

    投稿日時 : 2007年10月6日 15:08
  • コメント
    タイトル
    名前
    Url
    コメント