# Background tasks race conditions

**URL:** <https://www.ruby-forum.com/t/background-tasks-race-conditions/105145>\
**Category:** Rails\
**Created:** [May 26, 2007, 6:52am UTC](https://www.ruby-forum.com/t/background-tasks-race-conditions/105145 "2007-05-26T06:52:13Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![rpm13](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/3d9bf3/32.png) [@rpm13](https://www.ruby-forum.com/u/rpm13)\
**Post date:** [May 26, 2007, 6:52am UTC](https://www.ruby-forum.com/t/background-tasks-race-conditions/105145/1 "2007-05-26T06:52:13Z")

</div>

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?

Thanks

---

<div class="post-metadata">

**Author:** ![rpm13](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/3d9bf3/32.png) [@rpm13](https://www.ruby-forum.com/u/rpm13)\
**Post date:** [May 27, 2007, 6:11am UTC](https://www.ruby-forum.com/t/background-tasks-race-conditions/105145/2 "2007-05-27T06:11:22Z")

</div>

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
