On May 25, 2007, at 11:52 PM, Ra PM wrote:
>
> I need to run some cron jobs that clean up a table T which is used and
> created by the foreground rails controller. This could naturally lead
> to synchronization issues/race conditions etc
>
> eg. Let RC = rails controller. BJ background cron job
>
> RC: Queries T for a record using find
> BJ: queries T for the same record and removes it
> RC: proceeds as though record still exists
>
> Are there any special methods for handling this?
You could set up a simple Semaphore table:
class CreateSemaphores < ActiveRecord::Migration
def self.up
create_table(:semaphores, :id => false, :primary_key => :name)
do |t|
t.column :name, :string, :null => false, :limit => 20
t.column :counter, :integer, :default => 1, :null => false
end
execute("alter table semaphores add constraint pk_semaphores
primary key(name)")
end
def self.down
drop_table :semaphores
end
end
and then write a model that gives you locked access to a named thing:
# A basic in-database class. Call ::lock to lock a given name,
# ::unlock to unlock it
class Semaphore < ActiveRecord::Base
# These are the names you can lock
RESOURCE1 = "Some name"
RESOURCE2 = "Some other name"
# Attempt to lock the given name by decrementing the count. Return
+true+
# if the lock succeeds, +false+ otherwise
def self.lock(name)
# start by assuming the row exists and is lockable. That gives
us an early
# exit 99.99% of the time
count = update_all("counter = counter - 1", [ "counter >
0 &&
name = ?", name])
return true if count == 1
# If no row found, might be because there isn''t one.
Let''s try
inserting
# it. If it fails, then we know that there was one already
(because +name+
# is the primary key) and we can exit. If the insert succeeds, then
# the count is zero, because it now is claimed
create(:name => name, :counter => 0) rescue false
end
# Pretty much the opposite of lock... We don''t return anything
meaningful.
# It''s an error to unlock a semaphore that doesn''t exist
def self.unlock(name)
count = update_all("counter = counter + 1", [ "name =
?", name])
fail "Unknown semaphore: #{name}" unless count == 1
end
# Run a block if a semaphore is available, otherwise return false
def self.run_block(name)
if (result = lock(name))
begin
yield
ensure
unlock(name)
end
end
return result
end
end
Given that, you could write something like the following in both your
controller and your external script
locked = Semaphore.lock(Semaphore::RESOURCE1) do
mess around
with shared resource
end
if !locked
flash[:notice] = "Sorry, but the resource was busy..."
end
Dave
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Talk" group.
To post to this group, send email to
rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
To unsubscribe from this group, send email to
rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en
-~----------~----~----~----~------~----~------~--~---