Avoiding race conditions

I want to be able to display a number of messages, and guarantee that
they’re only displayed once. Something like this :

def get_new_messages
@messages = Message.find(:all, :limit => 5, :conditions => ‘new = 1’)
@messages.each do |msg|
msg.new = 0
msg.save
end
end

However, there’s a race condition in the above code if two users try
to access the get_new_messages action at the same time. How can I
avoid this?

Thanks,
Jon

On Jun 27, 2006, at 11:19 AM, Jonathan del Strother wrote:

However, there’s a race condition in the above code if two users
try to access the get_new_messages action at the same time. How
can I avoid this?

There is no race condition, but concurrent users may get the same
records. To ensure a user has exclusive access to the selected
records, use row locking (recently introduced to edge rails - rake
rails:freeze:edge).

def get_new_messages
Message.transaction do
# select * from messages limit 5 for update
Message.find(:all, :limit => 5, :conditions => ‘new=1’, :lock =>
true).map do |message|
message.new = false
message.save!
message
end
end
end

(Note that mysql locks all rows touched by the index it chooses, not
just those returned, so you’ll suffer heavy lock contention.)

A pleasant way to solve the problem is to avoid it - is there a
better way to track unread messages?

jeremy

Hi,

2006/6/27, Jeremy K. [email protected]:

A pleasant way to solve the problem is to avoid it - is there a
better way to track unread messages?

I’d say: why not associate the last message id viewed to the user
model and retrieve only the messages after (e.g. with id greater than)
that (you could also use a record timestamp if you don’t want to rely
on ids)…

bye
Luca

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs