On May 25, 2007, at 11:52 PM, Ra PM wrote:
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