-
-
Save maplebed/4693078 to your computer and use it in GitHub Desktop.
This gist (forked from https://gist.github.com/1328677) shows a simple example of using zookeeper to create a distributed lock. Run multiple instances of this script to see them fighting for the lock.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'zookeeper' | |
class Lock | |
def initialize(host, root="/my-app") | |
@zk = Zookeeper.new(host) | |
@root = root | |
end | |
def with_lock(app, timeout, timeout_callback, &block) | |
new_lock_res = @zk.create(:path => "#{@root}/#{app}-", :sequence => true, :ephemeral => true) | |
unique_lock_path = new_lock_res[:path] | |
if get_lock(unique_lock_path, timeout) | |
yield | |
@zk.delete(:path => unique_lock_path) | |
else | |
timeout_callback.call | |
@zk.delete(:path => unique_lock_path) | |
end | |
end | |
private | |
def get_lock(unique_lock_path, timeout) | |
lock_key = unique_lock_path.gsub(/^#{Regexp.quote(@root)}\//, '') | |
(0..4).each do | |
children = @zk.get_children(:path => @root)[:children].sort | |
watcher = Zookeeper::Callbacks::WatcherCallback.new {} | |
if (children.first == lock_key) | |
return true | |
else | |
less_than_path_idx = (children.index {|p| p == lock_key}) - 1 | |
stat_res = @zk.stat(:path => "#{@root}/#{children[less_than_path_idx]}", | |
:watcher => watcher) | |
if stat_res[:stat].exists | |
success = wait_until(timeout) { watcher.completed? } | |
if !success | |
return false | |
end | |
end | |
end | |
end | |
return false | |
end | |
def wait_until(timeout=10, &block) | |
time_to_stop = Time.now + timeout | |
until yield do | |
if Time.now > time_to_stop | |
return false | |
end | |
sleep 0.1 | |
end | |
return true | |
end | |
end | |
z = Zookeeper.new('localhost:2181') | |
z.create(:path => "/my-app") | |
### forever, try and grab the lock. Until you get it, keep retrying with a 2s | |
# timeout. If you get it, hold it for 4s, give it up, and sleep for 1-2s. Run | |
# as many instances of this script as you like; they'll fight for the lock but | |
# only one will hold it at a time. | |
l = Lock.new('localhost:2181') | |
while true do | |
puts "#{Time.now()} waiting for lock..." | |
if l.with_lock( | |
'mylock', | |
2, | |
lambda { puts "#{Time.now()} timed out on lock..." } | |
) { puts "#{Time.now()} Hey, I got the lock! holding for 4s" ; sleep 4; puts "#{Time.now()} ::sigh:: ok, giving up the lock. :'("} | |
sleeptime = 1 + (rand(10) / 10.0) | |
puts "#{Time.now()} sleeping for #{sleeptime}s" | |
sleep sleeptime | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment