zookeeper 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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.2.2.tar.gz
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