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
-~----------~----~----~----~------~----~------~--~---