redis-em-mutex
- Author
-
Rafał Michalski ([email protected])
DESCRIPTION
redis-em-mutex is the cross server-process-fiber EventMachine + Redis based semaphore.
FEATURES
-
only for EventMachine
-
no CPU-intensive sleep/polling while waiting for lock to become available
-
fibers waiting for the lock are signalled via Redis channel as soon as the lock has been released (~< 1 ms)
-
multi-locks (all-or-nothing) locking (to prevent possible deadlocks when multiple semaphores are required to be locked at once)
-
best served with EM-Synchrony (uses EM::Synchrony::ConnectionPool internally)
-
fiber-safe
-
deadlock detection (only trivial cases: locking twice the same resource from the same fiber)
-
mandatory lock expiration (with refreshing)
BUGS/LIMITATIONS
-
only for EventMachine
-
NOT thread-safe
-
locking order between concurrent processes is undetermined (no FIFO)
-
it’s not nifty, rather somewhat complicated
REQUIREMENTS
-
ruby >= 1.9 (tested: 1.9.3-p194, 1.9.2-p320, 1.9.1-p378)
-
github.com/redis/redis-rb >= 3.0.1
-
rubyeventmachine.com >= 0.12.10
-
(optional) github.com/igrigorik/em-synchrony
INSTALL
$ [sudo] gem install redis-em-mutex
Gemfile
gem "redis-em-mutex", "~> 0.1.1"
Github
git clone git://github.com/royaltm/redis-em-mutex.git
USAGE
require 'em-synchrony'
require 'em-redis-mutex'
Redis::EM::Mutex.setup(size: 10, url: 'redis:///1', expire: 600)
# or
Redis::EM::Mutex.setup do |opts|
opts.size = 10
opts.url = 'redis:///1'
...
end
EM.synchrony do
Redis::EM::Mutex.synchronize('resource.lock') do
... do something with resource
end
# or
mutex = Redis::EM::Mutex.new('resource.lock')
mutex.synchronize do
... do something with resource
end
# or
begin
mutex.lock
... do something with resource
ensure
mutex.unlock
end
...
Redis::EM::Mutex.stop_watcher
EM.stop
end
Namespaces
Redis::EM::Mutex.setup(ns: 'my_namespace', ....)
# or multiple namespaces:
ns = Redis::EM::Mutex::NS.new('my_namespace')
EM.synchrony do
ns.synchronize('foo') do
.... do something with foo and bar
end
...
EM.stop
end
Multi-locks
EM.synchrony do
Redis::EM::Mutex.synchronize('foo', 'bar', 'baz') do
.... do something with foo, bar and baz
end
...
EM.stop
end
Locking options
EM.synchrony do
begin
Redis::EM::Mutex.synchronize('foo', 'bar', block: 0.25) do
.... do something with foo and bar
end
rescue Redis::EM::Mutex::MutexTimeout
... locking timed out
end
Redis::EM::Mutex.synchronize('foo', 'bar', expire: 60) do |mutex|
.... do something with foo and bar in less than 60 seconds
if mutex.refresh(120)
# now we have additional 120 seconds until lock expires
else
# too late
end
end
...
EM.stop
end
Advanced
mutex = Redis::EM::Mutex.new('resource1', 'resource2', expire: 60)
EM.synchrony do
mutex.lock
EM.fork_reactor do
Fiber.new do
mutex.locked? # true
mutex.owned? # false
mutex.synchronize do
mutex.locked? # true
mutex.owned? # true
....
end
...
Redis::EM::Mutex.stop_watcher
EM.stop
end.resume
end
mutex.locked? # true
mutex.owned? # true
mutex.unlock
mutex.owned? # false
...
Redis::EM::Mutex.stop_watcher
EM.stop
end
LICENCE
The MIT License - Copyright © 2012 Rafał Michalski