Permalink
Browse files

Merge branch 'wickman'

Conflicts:
	CHANGELOG
	Manifest
	README
	Rakefile
	ext/zookeeper_c.c
	lib/zookeeper.rb
	test/test_basic.rb
  • Loading branch information...
2 parents dcc47bd + 60249ff commit 4ad2371a2f61e75a128f0944c10f66e6e3b50ff5 Evan Weaver committed Jul 3, 2010
View
@@ -1,8 +1,11 @@
+v0.3.0. Wickman's rewrite.
+
v0.2.2. Fix compatibility with stock Leopard fat-binary Ruby.
v0.2.1. No more camelcase classname.
v0.2. Bundle C dependencies, like memcached.gem.
v0.1. First release.
+
View
@@ -3,8 +3,20 @@ LICENSE
Manifest
README
Rakefile
+examples/cloud_config.rb
ext/extconf.rb
-ext/zkc-3.2.2.tar.gz
+ext/zkc-3.3.1.tar.gz
ext/zookeeper_c.c
+ext/zookeeper_lib.c
+ext/zookeeper_lib.h
lib/zookeeper.rb
+lib/zookeeper/acls.rb
+lib/zookeeper/callbacks.rb
+lib/zookeeper/constants.rb
+lib/zookeeper/exceptions.rb
+lib/zookeeper/stat.rb
test/test_basic.rb
+test/test_callback1.rb
+test/test_esoteric.rb
+test/test_watcher1.rb
+test/test_watcher2.rb
View
30 README
@@ -4,7 +4,14 @@ An interface to the Zookeeper distributed configuration server.
== License
+<<<<<<< HEAD
Copyright 2008 Phillip Pearson, and 2010 Twitter, Inc. Licensed under the MIT License. See the included LICENSE file. Portions copyright 2008-2010 the Apache Software Foundation, licensed under the Apache 2 license, and used with permission.
+=======
+Copyright 2008 Phillip Pearson, and 2010 Twitter, Inc. Licensed under the
+MIT License. See the included LICENSE file. Portions copyright 2008-2010
+the Apache Software Foundation, licensed under the Apache 2 license, and
+used with permission.
+>>>>>>> wickman
== Install
@@ -17,6 +24,7 @@ Connect to a server:
require 'rubygems'
require 'zookeeper'
z = Zookeeper.new("localhost:2181")
+<<<<<<< HEAD
Create, set and read nodes:
@@ -51,3 +59,25 @@ Acquire locks:
z.try_acquire "/parent/lock-", "content for the lock file" do |have_lock|
puts have_lock ? "we have the lock" : "we don't have the lock"
end
+=======
+ z.get_children(:path => "/")
+
+== Idioms
+
+ The following methods are initially supported:
+ get
+ set
+ get_children
+ stat
+ create
+ delete
+ get_acl
+ set_acl
+
+ All support async callbacks. get, get_children and stat support both
+ watchers and callbacks.
+
+ Calls take a dictionary of parameters. With the exception of set_acl, the
+ only required parameter is :path. Each call returns a dictionary with at
+ minimum two keys :req_id and :rc.
+>>>>>>> wickman
View
@@ -1,7 +1,7 @@
require 'echoe'
Echoe.new("zookeeper") do |p|
- p.author = "Phillip Pearson, Eric Maland, Evan Weaver"
+ p.author = "Phillip Pearson, Eric Maland, Evan Weaver, Brian Wickman"
p.project = "fauna"
p.summary = "An interface to the Zookeeper distributed configuration server."
p.url = "http://blog.evanweaver.com/files/doc/fauna/zookeeper/"
View
@@ -0,0 +1,125 @@
+require "rubygems"
+require "zookeeper"
+
+# A basic cloud-based YAML config library. Ruby Zookeeper client example.
+#
+# If you pass in a file as 'zk:/foo.yml/blah' it will go out to zookeeper.
+# Otherwise the file is assumed to be local. The yml file will get parsed
+# and cached locally, and keys after the .yml get interpreted as keys into
+# the YAML.
+#
+# e.g. get(zk:/config/service.yml/key1/key2/key3..) =>
+# zk.get(:path => /config/service.yml)
+# yaml <= YAML.parse(data)
+# yaml[key1][key2][key3]...
+#
+# If keys are unspecified, it returns the parsed YAML as one big object
+#
+# TODO if staleness is set to 0, read in YAML immediately before next
+# get(...)
+
+class CloudConfig
+ class NodeNotFound < StandardError; end
+ class BadPathError < StandardError; end
+
+ DEFAULT_SERVERS = "localhost:2181"
+
+ def initialize(zkservers = DEFAULT_SERVERS, staleness = 15) # maximum allowed staleness in seconds
+ @staleness = staleness
+ @lock = Mutex.new
+ @zkservers = DEFAULT_SERVERS
+
+ # cache
+ @data = {}
+ @zkcb = Zookeeper::WatcherCallback.new { dirty_callback(@zkcb.context) }
+ @zk = nil
+ end
+
+ def get(node)
+ filename, keys = extract_filename(node)
+
+ # read(filename) is potentially a zk call, so do not hold the lock during the read
+ if @lock.synchronize { !@data.has_key?(filename) }
+ d = YAML.load(read(filename))
+ @lock.synchronize { @data[filename] = d }
+ end
+
+ # synchronized b/c we potentially have a background thread updating data nodes from zk
+ # if keys is empty, return the whole file, otherwise roll up the keys
+ @lock.synchronize {
+ keys.empty? ? @data[filename] : keys.inject(@data[filename]) { |hash, key| hash[key] }
+ }
+ end
+
+ # todo:
+ # factor get-and-watch into a different subsystem (so you can have
+ # polling stat() ops on local filesystem.)
+ def read(yaml)
+ # read yaml file and register watcher. if watcher fires, set up
+ # background thread to do read and update data.
+ if yaml.match(/^zk:/)
+ @zk ||= init_zk
+ yaml = yaml['zk:'.length..-1] # strip off zk: from zk:/config/path.yml
+ resp = get_and_register(yaml)
+
+ if resp[:rc] != Zookeeper::ZOK
+ @zk.unregister_watcher(resp[:req_id])
+ raise NodeNotFound
+ end
+
+ resp[:data]
+ else
+ raise NodeNotFound unless File.exists?(yaml)
+ File.read(yaml)
+ end
+ end
+
+ def extract_filename(node)
+ path_elements = node.split("/")
+
+ yamlindex = path_elements.map{ |x| x.match("\.yml$") != nil }.index(true)
+ raise BadPathError unless yamlindex
+
+ yamlname = path_elements[0..yamlindex].join '/'
+ yamlkeys = path_elements[(yamlindex+1)..-1]
+
+ return yamlname, yamlkeys
+ end
+
+ private
+ def init_zk
+ Zookeeper.new(@zkservers)
+ end
+
+ def get_and_register(znode)
+ @zk.get(:path => znode, :watcher => @zkcb,
+ :watcher_context => { :path => znode,
+ :wait => rand(@staleness) })
+ end
+
+ def dirty_callback(context)
+ path = context[:path]
+ wait = context[:wait]
+
+ # Fire off a background update that waits a randomized period of time up
+ # to @staleness seconds.
+ Thread.new do
+ sleep wait
+ background_update(path)
+ end
+ end
+
+ def background_update(zkpath)
+ # do a synchronous get/register a new watcher
+ resp = get_and_register(zkpath)
+ if resp[:rc] != Zookeeper::ZOK
+ # puts "Unable to read #{zkpath} from Zookeeper!" @logger.error
+ zk.unregister_watcher(resp[:req_id])
+ else
+ # puts "Updating data."
+ d = YAML.load(resp[:data])
+ @lock.synchronize { @data["zk:#{zkpath}"] = d }
+ end
+ end
+end
+
View
Binary file not shown.
View
Binary file not shown.
Oops, something went wrong.

0 comments on commit 4ad2371

Please sign in to comment.