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

🌕 見えないものを信じる力 ― 物欲・所有欲からの解放

キャンプグッズ使ってますか?買…

2日 ago

トルコ旅行で考えた、日本のこれから

先日のトルコ旅行で、いろいろ感…

2週間 ago

新千歳空港の駐車場なら、ココ!

https://www.hok…

2週間 ago

トルコ旅行Tipsまとめ(実体験から)

ネットで調べても分かりにくい、…

3週間 ago

株式投資は“余剰資金”で──その言葉の本当の意味を考える

はじめに:「余剰資金で投資しま…

3か月 ago

利確したくなるときこそ、自分の目的を見直すタイミング

投資とは「稼ぐ力」を育て、社会…

3か月 ago