Categories: 開発

GORM で Lock

音楽の話しではなく、とある技術的な話で、GORM = Grails’Object Relational Mapping の テーブルロックに関するメモ。

Lock は、トランザクションの中で行うので、

Domain.withTransaction { status –>

     def  a = Domain.lock(id);

}

と書けば、 lock の位置で、ロックされる。つまり最初にここを実行したスレッドは通るが、そのスレッドのトランザクションが完了するまで、次に来たスレッドは待たされる。ロックするということは、たいてい、排他的な更新をしたいからなので、 a に変更を加えて保存(DB更新)することになる。となると、

Domain.withTransaction { status –>

     def  a = Domain.lock(id);

    a.save(flush:true);

}

みたいな感じになる。

a を id で取得したい場合はこれで良いのだが、id以外のキーで取得したい場合は、次のようになる。

Domain.withTransaction { status –>

     def  a = Domain.findByKey(key, [lock : true]);

    a.save(flush:true);

}

第二引数の、[lock : true] は、あまりドキュメントに出てこないのであるが、これで実際にロックされる。

ところが、実際にこれを頻繁に更新される状況で、非同期で実行すると、save のタイミングで、楽観的ロックエラーになる。

何故か? ロックされるのであるから、楽観的ロックエラーになるのはおかしいと思うのだが、実際に試してみると、findByKey() で取得した値は、ロック前の値なのだ。ロックはされるが、ロックが解除された後に取得した値は、ロックする前の値なので、前のスレッドが更新する前の値となるために、次に更新しようとすると、楽観的ロックエラーになる。

想像するに、以下のような処理になっているのではないかと思われる。

     def  a = Domain.findByKey(key);
     a.lock();

ロックはされるが、取得される a はロック前の古い値で、待たされている間に変わってしまった新しいテーブル上の値ではない。

     def  a = Domain.findByKey(key);
     a = Domain.get(a.id);

にしたらどうかと思ったが、どうやらキャッシュされているようで、駄目だった。

結局、次のコードで落ち着いた。

     def  a = Domain.findByKey(key, [lock : true]);
     a.refresh();

 

ロックは、findByKey で行う。

で、自分の番が回ってきたら、refresh でDBから値を取り直す。

SQL直接だと、こんなに悩む必要は無いのだけれど、GORMだと時々こういう事がある。

はじめ

Share
Published by
はじめ

Recent Posts

NISA のアイデア

NISA(2年目になるので新N…

3か月 ago

眩しいマップランプ

LEDに取り替えているというこ…

4か月 ago

アウトドアコーヒー

キャンプ、車中泊、ホテル泊まで…

4か月 ago

株式投資に踏み出す為の簡単な計算

かつて昭和の時代、銀行預金は絶…

4か月 ago

株はハロウィーンに買え

今日の米国市場は、まさにそんな…

5か月 ago