On Thursday 02 November 2006 16:01, Cameron Price wrote:
> class TaskTracker  # this consists of nothing more than an id,
> user_id, and a completed flag (which is indexed)
>   belongs_to :user
>
>   def perform_task
>     ... do expensive operation on self.user ...
>     self.completed = true
>     self.save
> end

> TaskTracker.find(:all, :conditions => 'completed = false', :limit =>
> 100).each do
> |t|
> t.perform_task
> end

Did you omit transaction handling on purpose? If one of the calls to 
t.perform_task raises an exception for any reason, this is likely to 
leave your data in an inconsistent state.

You might think that there's nothing in perform_task that might raise an 
exception. Think again. As you indicate, perform_task is an expensive 
operation. I take this to mean that it takes a noticeable amount of 
time. Now, when you fetch 100 objects from the database, the last one 
will have reached a considerable age when you come to it. That object, 
or any other, may have been changed by a user or some automated process 
when you finally come to process it. Depending on whether you're using 
optimistic locking (lock_version column), you either get a 
StaleObjectError or you corrupt your database by overwriting a record 
with obsolete data.

In effect, there are two cases you need to guard against.
The first is that your process may cause an error all by itself. That's 
where transactions come in to ensure consistency

TaskTracker.transaction do
  TaskTracker.find(:all, :conditions => 'completed = false', :limit => 
100).each do |t|
    t.perform_task
  end
end

The second case is that your process is vulnerable to concurrent changes 
of data. When you're sure that this only happens very rarely, it may be 
sufficient to detect these changes using optimistic locking. Then, when 
something does go wrong, rely on the transaction to rollback your 
earlier changes and restart the process in the hope that everything 
goes well the next time. When conflicts are rather likely, consider 
using pessimistic locking. It's not yet available in a released version 
of Rails/ActiveRecord, but will apparently be in 1.2.

Michael

-- 
Michael Schuerig
mailto:[EMAIL PROTECTED]
http://www.schuerig.de/michael/

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby 
on Rails: Core" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/rubyonrails-core
-~----------~----~----~----~------~----~------~--~---

Reply via email to