Skip to content

Commit

Permalink
move to Pessimistic locking
Browse files Browse the repository at this point in the history
return of the transaction!
  • Loading branch information
matthewrudy committed Jul 25, 2008
1 parent 9a361a4 commit cbad1f3
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 24 deletions.
18 changes: 17 additions & 1 deletion lib/rude_q.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def get(queue_name)
# if we use a transaction with the current tokenised lock
# its equivalent to a table lock == :bad
def fetch_with_lock(qname, &block) # :nodoc:
RudeQ::TokenLock.fetch_with_lock(self, qname, &block)
RudeQ::PessimisticLock.fetch_with_lock(self, qname, &block)
end

# class method to make it more easily stubbed
Expand All @@ -80,6 +80,22 @@ def sanitize_queue_name(queue_name) # :nodoc:
queue_name.to_s
end
end

module PessimisticLock
class << self

def fetch_with_lock(klass, qname) # :nodoc:
klass.transaction do
record = klass.find(:first,
:conditions => {:queue_name => qname, :processed => false},
:lock => true, :order => "id ASC", :limit => 1)

return yield(record)
end
end

end
end

module TokenLock
class << self
Expand Down
10 changes: 10 additions & 0 deletions spec/process_queue.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
class ProcessQueue < ActiveRecord::Base
include RudeQ

class << self
def processed_with_raise_hack!(*args)
processed_without_raise_hack!(*args)
raise RuntimeError if raise_on_processed # want to be able to raise afterwards to check transactions
end
alias_method_chain :processed!, :raise_hack
attr_accessor :raise_on_processed
end

end
46 changes: 23 additions & 23 deletions spec/rude_q_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
describe RudeQ::ClassMethods do # ProcessQueue extends ClassMethods
before(:each) do
ProcessQueue.delete_all
ProcessQueue.raise_on_processed = false
create_some_noise
end

def create_some_noise
ProcessQueue.create!(:queue_name => "doNT use this in Specs", :data => {:not => "to be messed with"})
ProcessQueue.create!(:queue_name => "abcde", :data => {:same_as => "the specs but already processed"}, :processed => true)
ProcessQueue.create!(:queue_name => "abcde", :data => {:same_as => "the specs but with token"}, :token => " unlikely ")
end

describe "get and set" do
Expand Down Expand Up @@ -82,40 +82,25 @@ def create_some_noise
end
end

describe ".get" do
it "should not return a processed item with the same token" do
@token = "tokEEEannn"

RudeQ::TokenLock.should respond_to(:get_unique_token) # ensure our stub is safe
RudeQ::TokenLock.should_receive(:get_unique_token).exactly(3).times.and_return(@token)

@existing = ProcessQueue.create!(:queue_name => 'abcde', :data => :old_data, :token => @token, :processed => true)

ProcessQueue.get('abcde').should be(nil)

ProcessQueue.set('abcde', :new_data)
ProcessQueue.get('abcde').should == :new_data
ProcessQueue.get('abcde').should be(nil)
end

it ", unfortunately, should not revert a record if something goes wrong before it finishes" do
ProcessQueue.should_receive(:processed!).and_raise(RuntimeError)
ProcessQueue.set('abcde', :this_will_remain_tokenised)
describe ".get" do
it "should revert a record if something goes wrong before it finishes" do
ProcessQueue.raise_on_processed = true
ProcessQueue.set('abcde', :this_will_remain_unprocessed)

# confirm the object is in the db
record = ProcessQueue.find(:first, :order => "id DESC")
record.queue_name.should == 'abcde'
record.data.should == :this_will_remain_tokenised
record.data.should == :this_will_remain_unprocessed
record.processed?.should == false
record.token.should == nil

lambda {ProcessQueue.get('abcde')}.should raise_error(RuntimeError)

record.reload
record.queue_name.should == 'abcde'
record.data.should == :this_will_remain_tokenised
record.data.should == :this_will_remain_unprocessed
record.processed?.should == false
record.token.should_not == nil
record.token.should == nil
end
end

Expand Down Expand Up @@ -249,4 +234,19 @@ def create_some_noise
end
end

# it "should not return a processed item with the same token" do
# @token = "tokEEEannn"
#
# RudeQ::TokenLock.should respond_to(:get_unique_token) # ensure our stub is safe
# RudeQ::TokenLock.should_receive(:get_unique_token).exactly(3).times.and_return(@token)
#
# @existing = ProcessQueue.create!(:queue_name => 'abcde', :data => :old_data, :token => @token, :processed => true)
#
# ProcessQueue.get('abcde').should be(nil)
#
# ProcessQueue.set('abcde', :new_data)
# ProcessQueue.get('abcde').should == :new_data
# ProcessQueue.get('abcde').should be(nil)
# end

end

0 comments on commit cbad1f3

Please sign in to comment.