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は長期保有が基本」「…

4日 ago

モバイルバッテリーはケーブルレスの時代へ

今どきのスマートフォンはバッテ…

3週間 ago

老後資産の出口戦略:自分に合った“切り崩し方”を考える

~現金残高キープ型のゆるやかな…

4週間 ago

NISA枠を利用した、税金のかからないポートフォリオのリバランス

~売却タイミングと再投資の考え…

4週間 ago

NISAへの移動、今がチャンス?相場下落時の節税テクニック

某トランプさんのおかげ(?)で…

1か月 ago