Multiple instances adding to the same attribute


#1

Is there a possibility, when for example, counting a certain request,
that a highly coincidental retrieval of information by two clients will
override eachother’s contributions? Suppose you have:

counter +=1
counter.save

If another request gets the initial counter value before the first one
had time to save, and then the first one saves, then the second one
saves, the counter is only updated by 1 instead of 2.

Does rails somehow protect itself from this kind of possibility? To be
honest I’m not particularly familiar with server side applications, do
they run in parallel or do they always finish processing the first
request before serving the next?


#2

You could do (and don’t quote me on this as I haven’t used it):

def fileserve
file = Client.file.find(:first, …)
respond_to |format|
format.zip {
# send file here, not a link but a real file
}
end
end

Sorry for my obscurity in the answer, don’t know how else to explain it.

That should work though I reckon. Never used respond_to for anything
but rails files though.

Cheers,
Zach I.
→ Blog – http://www.zachinglis.com
→ Company – http://www.lt3media.com
→ Portfolio – http://portfolio.zachinglis.com


#3

Maybe you can use the optimistic locking. In rails, Optimistic locking
is
enabled by default on any table that contains an
integer column called lock_version. Then, you can handler the exception
yourself or just discard it depend to your project.


#4

Michael,

Sorry about that. I got a bounce from the group and reposted it under
my other account. I misposted it to you.

Apoligies.


#5

Michael C. wrote:

saves, the counter is only updated by 1 instead of 2.

Does rails somehow protect itself from this kind of possibility? To be
honest I’m not particularly familiar with server side applications, do
they run in parallel or do they always finish processing the first
request before serving the next?

A Rails process currently serializes requests, so there is no problem
unless you run multiple Rails processes to increase server capacity.

But this is done very commonly, so you should allow for concurrency by
instead writing counter updates directly to the database:

model = Model.find(:first)
Model.increment_counter(:counter, model.id)


We develop, watch us RoR, in numbers too big to ignore.


#6

Zach, I’m really not sure how that could help in my case :slight_smile:

sishen wrote:

Maybe you can use the optimistic locking. In rails, Optimistic locking
is
enabled by default on any table that contains an
integer column called lock_version. Then, you can handler the exception
yourself or just discard it depend to your project.

I’ve heard about that. It’s intended to be used when multiple processes
have access to a database simultaneously, the problem here is I’m not
sure if in the typical scenario multiple processes do have access to the
database simultaneously or whether it’s all serialized by default so no
overlapping can take place. I suppose the question should be whether
rails is or isn’t parallelized, and if it can be whether that’s
something automatic or configureable.


#7

Michael C. wrote:

Would model.update_attribute(:counter, model.counter+1) maintain the
same concurrency as your increment_counter class method? Wouldn’t the
increment_counter method itself implement update_attribute?

No.

update_attribute uses the in-memory Ruby counter value, which
may be stale.

increment_counter reads and increments the database value.

update_attribute can however be safe if you’ve obtained
the right table locks.


We develop, watch us RoR, in numbers too big to ignore.


#8

Mark Reginald J. wrote:

But this is done very commonly, so you should allow for concurrency by
instead writing counter updates directly to the database:

model = Model.find(:first)
Model.increment_counter(:counter, model.id)

Would model.update_attribute(:counter, model.counter+1) maintain the
same concurrency as your increment_counter class method? Wouldn’t the
increment_counter method itself implement update_attribute?


#9

Mark Reginald J. wrote:

update_attribute uses the in-memory Ruby counter value, which
may be stale.

increment_counter reads and increments the database value.

update_attribute can however be safe if you’ve obtained
the right table locks.

Ah, alright. I thought increment_counter was a custom defined class
method you just used as an example, I just checked the rails docs.

Much thanks.