I have a model which holds a counter field, the field have to be
incremented frequently to record the access frequency of the specific
object:
@obj = Temp.find(id) @obj.counter += 1 @obj.save
The implementation seems quite simple, but I am worrying about the
race condition (if there are any, I do not know whether rails is
thread-safe) since these statements may be interleaved. Could anyone
help about this? Thanks very much!
This will increment the property “:counter” for the model Temp with
“id”, this column must have a default value of 0 (zero) or else this
call won’t work.
The implementation seems quite simple, but I am worrying about the
race condition (if there are any, I do not know whether rails is
thread-safe) since these statements may be interleaved. Could anyone
help about this? Thanks very much!
Difei
You’re correct to worry about a race condition, but for the wrong
reason. Rails deployments up to now and for a while yet into the
future are clusters of mongrels / thins / etc, and as such nothing is
threaded. The race condition in who (as in, which Mongrel) gets to
save to the database first. As for protecting against this, you’ll
need to make sure to lock the row you working with, update the
counter, save, then unlock. You’ll want to do this as quick as
possible of course, but it still could cause some slowdown if a lot of
processes are trying to do the same thing.
If there are better ways around this, I’d definitely be interested in
hearing them.
The implementation seems quite simple, but I am worrying about the
race condition (if there are any, I do not know whether rails is
thread-safe) since these statements may be interleaved. Could anyone
help about this? Thanks very much!
Difei
You’re correct to worry about a race condition, but for the wrong
reason. Rails deployments up to now and for a while yet into the
future are clusters of mongrels / thins / etc, and as such nothing is
threaded. The race condition in who (as in, which Mongrel) gets to
save to the database first. As for protecting against this, you’ll
need to make sure to lock the row you working with, update the
counter, save, then unlock. You’ll want to do this as quick as
possible of course, but it still could cause some slowdown if a lot of
processes are trying to do the same thing.
If there are better ways around this, I’d definitely be interested in
hearing them.
MaurÃcio proposed the increment_counter method, may I assume that is
atomic?
On 10 Sep 2008, at 17:04, Maurício Linhares wrote:
You should be doing it like this:
Temp.increment_counter( :counter, id )
This will increment the property “:counter” for the model Temp with
“id”, this column must have a default value of 0 (zero) or else this
call won’t work.
this works because it does
update foos set counter = counter + 1
and then it’s the databases problem to worry about concurrent queries
and so on.
If what you showed was a simplification of what you’re actually doing
(and you can’t reduce it to increment_counter) then you’ll need to
look into locking (either optimistic or pessimistic)
This will increment the property “:counter” for the model Temp with
“id”, this column must have a default value of 0 (zero) or else this
call won’t work.
Thanks a lot! This look like what I am looking for.
One thing to notice is that a loaded model WILL NOT be updated by this
call, if you have a model loaded and call this method to perform the
increment, the model that is already loaded will not be updated, you
will have to call “reload” on it to get the latest counter value.
One thing to notice is that a loaded model WILL NOT be updated by this
call, if you have a model loaded and call this method to perform the
increment, the model that is already loaded will not be updated, you
will have to call “reload” on it to get the latest counter value.