次のエンティティと関係があるとします。
A<-B<-C<-D<-E
同じ A に対して複数の E があります (E は A から D、C、および B に属します)
次のようなコード スニペットがあります。
eList=E.all
varA=eList.first.D.C.B.A
E.transaction do
...Operations that take some time...
varA.update_attributes(::last_update=>Time.now)
end
このスニペットがスレッドで実行されると考えてください。E エンティティごとに 1 つのスレッド。つまり、1 つの A に対して 10 個の E がある場合、このスニペットでは 10 個のスレッドが同時に実行されます。
私の問題はラインに付属しています
varA.update_attributes(:last_update=>Time.now)
コードを見ると、次のようになっていることがわかります。
[DEBUG] 2013/02/13 13:09:51 [66222] - abstract_adapter.rb:198 - A Update (3107.1ms) UPDATE "A" SET "updated_at" = '2013-02-13 13:09:47.932899', "last_update" = '2013-02-13 13:09:47.932209' WHERE "id" = 144
[DEBUG] 2013/02/13 13:09:54 [48600] - abstract_adapter.rb:198 - A Update (6812.2ms) UPDATE "A" SET "updated_at" = '2013-02-13 13:09:47.218032', "last_update" = '2013-02-13 13:09:47.217421' WHERE "id" = 144
[DEBUG] 2013/02/13 13:09:56 [66190] - abstract_adapter.rb:198 - A Update (6717.5ms) UPDATE "A" SET "updated_at" = '2013-02-13 13:09:49.328496', "last_update" = '2013-02-13 13:09:49.327777' WHERE "id" = 144
[DEBUG] 2013/02/13 13:09:59 [66219] - abstract_adapter.rb:198 - A Update (10816.2ms) UPDATE "A" SET "updated_at" = '2013-02-13 13:09:48.236539', "last_update" = '2013-02-13 13:09:48.235895' WHERE "id" = 144
[DEBUG] 2013/02/13 13:10:01 [66200] - abstract_adapter.rb:198 - A Update (13450.9ms) UPDATE "A" SET "updated_at" = '2013-02-13 13:09:47.584182', "last_update" = '2013-02-13 13:09:47.583467' WHERE "id" = 144
ご覧のとおり、各クエリの実行時間はスレッドごとに増加します。最初のものは 3 秒、最後のものは 13 秒かかります。これは、同じレコード (A.id=144) を更新しているため、レコードがロックされ、他のスレッドが待機する必要があるためだと思います。
私の質問は、Rails 2.3 で、更新中のレコードがロックされているかどうかを検出して、他のスレッドが更新をスキップできるようにする方法はありますか?
何かのようなもの:
if not varA.locked then varA.update_attributes(:last_update=>Time.now) end
It is good enough for me to update it just once so I want the others threads to check if it is already updating and move on.
FWIW, my DB is postgres 9.0