diff --git a/lib/mega_mutex.rb b/lib/mega_mutex.rb index 3adf3a8..ef249fe 100644 --- a/lib/mega_mutex.rb +++ b/lib/mega_mutex.rb @@ -56,8 +56,16 @@ def self.get_current_lock(mutex_id) # with_distributed_mutex('my_mutex_id_1234', :timeout => 20) do # do_something! # end + # + # Additionally, you can specify the amount of time the lock should be valid + # for. This is helpful in preventing deadlocks when the lock isn't deleted + # for whatever reason. + # + # with_distributed_mutex('my_mutex_id_1234', :ttl => 20) do + # do_something! + # end def with_distributed_mutex(mutex_id, options = {}, &block) - mutex = DistributedMutex.new(mutex_id, options[:timeout]) + mutex = DistributedMutex.new(mutex_id, options[:timeout], options[:ttl]) begin mutex.run(&block) rescue Object => e diff --git a/lib/mega_mutex/distributed_mutex.rb b/lib/mega_mutex/distributed_mutex.rb index 346a776..13d0b36 100644 --- a/lib/mega_mutex/distributed_mutex.rb +++ b/lib/mega_mutex/distributed_mutex.rb @@ -11,9 +11,10 @@ def cache end end - def initialize(key, timeout = nil) + def initialize(key, timeout = nil, ttl = nil) @key = key @timeout = timeout + @ttl = ttl end def logger @@ -74,7 +75,7 @@ def locked_by_me? end def set_current_lock(new_lock) - cache.add(@key, my_lock_id) + cache.add(@key, my_lock_id, @ttl) end def my_lock_id diff --git a/spec/lib/mega_mutex_spec.rb b/spec/lib/mega_mutex_spec.rb index 95627b3..213710a 100644 --- a/spec/lib/mega_mutex_spec.rb +++ b/spec/lib/mega_mutex_spec.rb @@ -129,5 +129,25 @@ def mutex_id assert @exception.is_a?(MegaMutex::TimeoutError), "Expected TimeoutError to be raised, but wasn't" end end + + describe 'with a TTL' do + it "should release the lock after the TTL has expired" do + messages = [] + + threads << Thread.new do + with_distributed_mutex('foo', :ttl => 0.2) do + sleep 0.4 + messages << 'Second message' + end + end + threads << Thread.new do + with_distributed_mutex('foo') { messages << 'First message' } + end + + wait_for_threads_to_finish + messages.first.should eq('First message') + messages.first.should eq('Second message') + end + end end end