Skip to content

Instantly share code, notes, and snippets.

@maplebed
Forked from isterin/gist:1328677
Last active December 12, 2015 01:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maplebed/4693078 to your computer and use it in GitHub Desktop.
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.
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