zookeeper 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +3 -0
- data/Manifest +13 -1
- data/README +30 -0
- data/Rakefile +1 -1
- data/examples/cloud_config.rb +125 -0
- data/ext/zkc-3.3.1.tar.gz +0 -0
- data/ext/zookeeper_c.c +338 -393
- data/ext/zookeeper_lib.c +527 -0
- data/ext/zookeeper_lib.h +151 -0
- data/lib/zookeeper/acls.rb +31 -0
- data/lib/zookeeper/callbacks.rb +89 -0
- data/lib/zookeeper/constants.rb +54 -0
- data/lib/zookeeper/exceptions.rb +91 -0
- data/lib/zookeeper/stat.rb +12 -0
- data/lib/zookeeper.rb +207 -51
- data/test/test_basic.rb +16 -39
- data/test/test_callback1.rb +36 -0
- data/test/test_esoteric.rb +7 -0
- data/test/test_watcher1.rb +56 -0
- data/test/test_watcher2.rb +52 -0
- data/zookeeper.gemspec +6 -6
- data.tar.gz.sig +0 -0
- metadata +31 -7
- metadata.gz.sig +0 -0
- data/ext/zkc-3.2.2.tar.gz +0 -0
data/CHANGELOG
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
|
2
|
+
v0.3.0. Wickman's rewrite.
|
3
|
+
|
2
4
|
v0.2.2. Fix compatibility with stock Leopard fat-binary Ruby.
|
3
5
|
|
4
6
|
v0.2.1. No more camelcase classname.
|
@@ -6,3 +8,4 @@ v0.2.1. No more camelcase classname.
|
|
6
8
|
v0.2. Bundle C dependencies, like memcached.gem.
|
7
9
|
|
8
10
|
v0.1. First release.
|
11
|
+
|
data/Manifest
CHANGED
@@ -3,8 +3,20 @@ LICENSE
|
|
3
3
|
Manifest
|
4
4
|
README
|
5
5
|
Rakefile
|
6
|
+
examples/cloud_config.rb
|
6
7
|
ext/extconf.rb
|
7
|
-
ext/zkc-3.
|
8
|
+
ext/zkc-3.3.1.tar.gz
|
8
9
|
ext/zookeeper_c.c
|
10
|
+
ext/zookeeper_lib.c
|
11
|
+
ext/zookeeper_lib.h
|
9
12
|
lib/zookeeper.rb
|
13
|
+
lib/zookeeper/acls.rb
|
14
|
+
lib/zookeeper/callbacks.rb
|
15
|
+
lib/zookeeper/constants.rb
|
16
|
+
lib/zookeeper/exceptions.rb
|
17
|
+
lib/zookeeper/stat.rb
|
10
18
|
test/test_basic.rb
|
19
|
+
test/test_callback1.rb
|
20
|
+
test/test_esoteric.rb
|
21
|
+
test/test_watcher1.rb
|
22
|
+
test/test_watcher2.rb
|
data/README
CHANGED
@@ -4,7 +4,14 @@ An interface to the Zookeeper distributed configuration server.
|
|
4
4
|
|
5
5
|
== License
|
6
6
|
|
7
|
+
<<<<<<< HEAD
|
7
8
|
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.
|
9
|
+
=======
|
10
|
+
Copyright 2008 Phillip Pearson, and 2010 Twitter, Inc. Licensed under the
|
11
|
+
MIT License. See the included LICENSE file. Portions copyright 2008-2010
|
12
|
+
the Apache Software Foundation, licensed under the Apache 2 license, and
|
13
|
+
used with permission.
|
14
|
+
>>>>>>> wickman
|
8
15
|
|
9
16
|
== Install
|
10
17
|
|
@@ -17,6 +24,7 @@ Connect to a server:
|
|
17
24
|
require 'rubygems'
|
18
25
|
require 'zookeeper'
|
19
26
|
z = Zookeeper.new("localhost:2181")
|
27
|
+
<<<<<<< HEAD
|
20
28
|
|
21
29
|
Create, set and read nodes:
|
22
30
|
|
@@ -51,3 +59,25 @@ Acquire locks:
|
|
51
59
|
z.try_acquire "/parent/lock-", "content for the lock file" do |have_lock|
|
52
60
|
puts have_lock ? "we have the lock" : "we don't have the lock"
|
53
61
|
end
|
62
|
+
=======
|
63
|
+
z.get_children(:path => "/")
|
64
|
+
|
65
|
+
== Idioms
|
66
|
+
|
67
|
+
The following methods are initially supported:
|
68
|
+
get
|
69
|
+
set
|
70
|
+
get_children
|
71
|
+
stat
|
72
|
+
create
|
73
|
+
delete
|
74
|
+
get_acl
|
75
|
+
set_acl
|
76
|
+
|
77
|
+
All support async callbacks. get, get_children and stat support both
|
78
|
+
watchers and callbacks.
|
79
|
+
|
80
|
+
Calls take a dictionary of parameters. With the exception of set_acl, the
|
81
|
+
only required parameter is :path. Each call returns a dictionary with at
|
82
|
+
minimum two keys :req_id and :rc.
|
83
|
+
>>>>>>> wickman
|
data/Rakefile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'echoe'
|
2
2
|
|
3
3
|
Echoe.new("zookeeper") do |p|
|
4
|
-
p.author = "Phillip Pearson, Eric Maland, Evan Weaver"
|
4
|
+
p.author = "Phillip Pearson, Eric Maland, Evan Weaver, Brian Wickman"
|
5
5
|
p.project = "fauna"
|
6
6
|
p.summary = "An interface to the Zookeeper distributed configuration server."
|
7
7
|
p.url = "http://blog.evanweaver.com/files/doc/fauna/zookeeper/"
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "zookeeper"
|
3
|
+
|
4
|
+
# A basic cloud-based YAML config library. Ruby Zookeeper client example.
|
5
|
+
#
|
6
|
+
# If you pass in a file as 'zk:/foo.yml/blah' it will go out to zookeeper.
|
7
|
+
# Otherwise the file is assumed to be local. The yml file will get parsed
|
8
|
+
# and cached locally, and keys after the .yml get interpreted as keys into
|
9
|
+
# the YAML.
|
10
|
+
#
|
11
|
+
# e.g. get(zk:/config/service.yml/key1/key2/key3..) =>
|
12
|
+
# zk.get(:path => /config/service.yml)
|
13
|
+
# yaml <= YAML.parse(data)
|
14
|
+
# yaml[key1][key2][key3]...
|
15
|
+
#
|
16
|
+
# If keys are unspecified, it returns the parsed YAML as one big object
|
17
|
+
#
|
18
|
+
# TODO if staleness is set to 0, read in YAML immediately before next
|
19
|
+
# get(...)
|
20
|
+
|
21
|
+
class CloudConfig
|
22
|
+
class NodeNotFound < StandardError; end
|
23
|
+
class BadPathError < StandardError; end
|
24
|
+
|
25
|
+
DEFAULT_SERVERS = "localhost:2181"
|
26
|
+
|
27
|
+
def initialize(zkservers = DEFAULT_SERVERS, staleness = 15) # maximum allowed staleness in seconds
|
28
|
+
@staleness = staleness
|
29
|
+
@lock = Mutex.new
|
30
|
+
@zkservers = DEFAULT_SERVERS
|
31
|
+
|
32
|
+
# cache
|
33
|
+
@data = {}
|
34
|
+
@zkcb = Zookeeper::WatcherCallback.new { dirty_callback(@zkcb.context) }
|
35
|
+
@zk = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def get(node)
|
39
|
+
filename, keys = extract_filename(node)
|
40
|
+
|
41
|
+
# read(filename) is potentially a zk call, so do not hold the lock during the read
|
42
|
+
if @lock.synchronize { !@data.has_key?(filename) }
|
43
|
+
d = YAML.load(read(filename))
|
44
|
+
@lock.synchronize { @data[filename] = d }
|
45
|
+
end
|
46
|
+
|
47
|
+
# synchronized b/c we potentially have a background thread updating data nodes from zk
|
48
|
+
# if keys is empty, return the whole file, otherwise roll up the keys
|
49
|
+
@lock.synchronize {
|
50
|
+
keys.empty? ? @data[filename] : keys.inject(@data[filename]) { |hash, key| hash[key] }
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
# todo:
|
55
|
+
# factor get-and-watch into a different subsystem (so you can have
|
56
|
+
# polling stat() ops on local filesystem.)
|
57
|
+
def read(yaml)
|
58
|
+
# read yaml file and register watcher. if watcher fires, set up
|
59
|
+
# background thread to do read and update data.
|
60
|
+
if yaml.match(/^zk:/)
|
61
|
+
@zk ||= init_zk
|
62
|
+
yaml = yaml['zk:'.length..-1] # strip off zk: from zk:/config/path.yml
|
63
|
+
resp = get_and_register(yaml)
|
64
|
+
|
65
|
+
if resp[:rc] != Zookeeper::ZOK
|
66
|
+
@zk.unregister_watcher(resp[:req_id])
|
67
|
+
raise NodeNotFound
|
68
|
+
end
|
69
|
+
|
70
|
+
resp[:data]
|
71
|
+
else
|
72
|
+
raise NodeNotFound unless File.exists?(yaml)
|
73
|
+
File.read(yaml)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def extract_filename(node)
|
78
|
+
path_elements = node.split("/")
|
79
|
+
|
80
|
+
yamlindex = path_elements.map{ |x| x.match("\.yml$") != nil }.index(true)
|
81
|
+
raise BadPathError unless yamlindex
|
82
|
+
|
83
|
+
yamlname = path_elements[0..yamlindex].join '/'
|
84
|
+
yamlkeys = path_elements[(yamlindex+1)..-1]
|
85
|
+
|
86
|
+
return yamlname, yamlkeys
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
def init_zk
|
91
|
+
Zookeeper.new(@zkservers)
|
92
|
+
end
|
93
|
+
|
94
|
+
def get_and_register(znode)
|
95
|
+
@zk.get(:path => znode, :watcher => @zkcb,
|
96
|
+
:watcher_context => { :path => znode,
|
97
|
+
:wait => rand(@staleness) })
|
98
|
+
end
|
99
|
+
|
100
|
+
def dirty_callback(context)
|
101
|
+
path = context[:path]
|
102
|
+
wait = context[:wait]
|
103
|
+
|
104
|
+
# Fire off a background update that waits a randomized period of time up
|
105
|
+
# to @staleness seconds.
|
106
|
+
Thread.new do
|
107
|
+
sleep wait
|
108
|
+
background_update(path)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def background_update(zkpath)
|
113
|
+
# do a synchronous get/register a new watcher
|
114
|
+
resp = get_and_register(zkpath)
|
115
|
+
if resp[:rc] != Zookeeper::ZOK
|
116
|
+
# puts "Unable to read #{zkpath} from Zookeeper!" @logger.error
|
117
|
+
zk.unregister_watcher(resp[:req_id])
|
118
|
+
else
|
119
|
+
# puts "Updating data."
|
120
|
+
d = YAML.load(resp[:data])
|
121
|
+
@lock.synchronize { @data["zk:#{zkpath}"] = d }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
Binary file
|