Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions lib/redis_mutex.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class RedisMutex < RedisClassy
DEFAULT_EXPIRE = 10
LockError = Class.new(StandardError)
UnlockError = Class.new(StandardError)
ExpireResetError = Class.new(StandardError)
AssertionError = Class.new(StandardError)

def initialize(object, options={})
Expand Down Expand Up @@ -56,6 +57,15 @@ def try_lock
return false # Dammit, it seems that someone else was even faster than us to remove the expired lock!
end

# Extends the expire time just in case process needs more time
def reset_expire
raise ExpireResetError, "extend_expire should be called only on locked resources" unless locked?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should check the ownership of the lock by get == @expires_at.to_s instead of just locked?

# we've the lock so we can safely set the new expire time
now = Time.now.to_f
@expires_at = now + @expire
set(@expires_at)
end

# Returns true if resource is locked. Note that nil.to_f returns 0.0
def locked?
get.to_f > Time.now.to_f
Expand Down
21 changes: 21 additions & 0 deletions spec/redis_mutex_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,27 @@
end
end

it 'resets the expire' do
mutex = RedisMutex.new(:test_lock, :expire => 0.5, :block => 0)
expect(mutex.lock).to be_truthy
sleep 0.3

mutex.reset_expire
sleep 0.3 # now it shouldn't be expired if reset works

# someone tries to overwrite the reset expired lock
mutex2 = RedisMutex.new(:test_lock, :expire => 0, :block => 0)
expect(mutex2.lock).to be_falsey
end

it 'raises ExpireResetError if reset_expire is called when lock mutex is not locked' do
mutex = RedisMutex.new(:test_lock, :expire => 0.1, :block => 0)
expect(mutex.lock).to be_truthy
sleep 0.2 # wait enough to make the lock expired

expect { mutex.reset_expire {} }.to raise_error(RedisMutex::ExpireResetError)
end

it 'resets locking state on reuse' do
mutex = RedisMutex.new(:test_lock, SHORT_MUTEX_OPTIONS)
expect(mutex.lock).to be_truthy
Expand Down