views:

111

answers:

0

I watched in horror this morning as a script, running on two different machines, happily loaded, updated, and saved. I'm running ruby 1.8.6, AR 2.2.2. I started playing with some test cases and found reduced it to a minimal reproduction case:

irb(main):059:0> first = Job.find :first
=> #<Job id: 323, lock: 8, worker_host: "second">
irb(main):060:0> second = Job.find :first
=> #<Job id: 323, lock: 8, worker_host: "second">

This is all well-and-good. The worker_host field shows the result from my last round of testing, "second."

irb(main):061:0> first.worker_host = 'first'
=> "first"
irb(main):062:0> second.worker_host = 'second'
=> "second"
irb(main):063:0> first
=> #<Job id: 323, lock: 8, worker_host: "first">
irb(main):064:0> second
=> #<Job id: 323, lock: 8, worker_host: "second">

I have two instances of the same model and have now set their values differently. When I go to save these guys, I should get an exception from whichever saves second.

irb(main):065:0> first.save!
=> true
irb(main):066:0> first
=> #<Job id: 323, lock: 9, worker_host: "first">
irb(main):067:0> second
=> #<Job id: 323, lock: 8, worker_host: "second">

So far, this is all fine. The lock on the saved model has been updated - as it should be, and a quick check in mysql shows the values that 'first' sees. 'second' is now out of date and should raise a stale object exception when I try to save it:

irb(main):068:0> second.save
=> true

Nope.

irb(main):069:0> second
=> #<Job id: 323, lock: 8, worker_host: "second">

I would have expected it to have at least updated its local version of the lock, having gone through the motions of a successful save!?!?!

I have found that the only way that the locking mechanism works properly is if both contenders modify the record when the lock value is zero!!!

I have set my locking field name properly:

set_locking_column  :lock

The only thing that I've done differently than most of the examples I've seen is that my migration didn't specify :default => 0 for the locking column. My understanding is that this is unnecessary (the example I worked from didn't do it) and would still agree, given that zero is the MySQL default anyway and setting the :default=>0 only tells the database what value to use for a new record, but doesn't imply any special behavior for Active Record.

What's going on here?