slyphon-zookeeper 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ *.o
3
+ *.so
4
+ *~
5
+ *.log
6
+ Makefile
7
+ pkg
8
+ zookeeper.gemspec
data/CHANGELOG ADDED
@@ -0,0 +1,23 @@
1
+ v0.4.3 Fix a handful of memory-related bugs, fix SIGSEGV on master change,
2
+ reduce latency of event handling, fix compilation on OSX.
3
+
4
+ v0.4.2 Add options to Zookeeper#initialize, silence most Zookeeper logs.
5
+
6
+ v0.4.1 Upgrade to ZooKeeper 3.3.2
7
+
8
+ v0.4.0. More attr-readers (StarvingMarvin) and 1.9 compatibility (tsuraan)
9
+
10
+ v0.3.2. Handle close, closed connections and expired sessions a little more gracefully.
11
+
12
+ v0.3.1. ACL bugfix.
13
+
14
+ v0.3.0. Wickman's rewrite, breaks dependencies from myelin/emaland port.
15
+
16
+ v0.2.2. Fix compatibility with stock Leopard fat-binary Ruby.
17
+
18
+ v0.2.1. No more camelcase classname.
19
+
20
+ v0.2. Bundle C dependencies, like memcached.gem.
21
+
22
+ v0.1. First release.
23
+
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ (The MIT License)
2
+
3
+ Copyright (C) 2008 Phillip Pearson
4
+ Copyright (C) 2010 Twitter, Inc.
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ 'Software'), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest ADDED
@@ -0,0 +1,33 @@
1
+ CHANGELOG
2
+ Gemfile
3
+ LICENSE
4
+ Manifest
5
+ README
6
+ Rakefile
7
+ Session.vim
8
+ examples/cloud_config.rb
9
+ ext/extconf.rb
10
+ ext/zkc-3.3.2.tar.gz
11
+ ext/zookeeper_base.rb
12
+ ext/zookeeper_c.c
13
+ ext/zookeeper_lib.c
14
+ ext/zookeeper_lib.h
15
+ java/zookeeper_base.rb
16
+ lib/zookeeper.rb
17
+ lib/zookeeper/acls.rb
18
+ lib/zookeeper/callbacks.rb
19
+ lib/zookeeper/common.rb
20
+ lib/zookeeper/constants.rb
21
+ lib/zookeeper/exceptions.rb
22
+ lib/zookeeper/stat.rb
23
+ spec/log4j.properties
24
+ spec/spec_helper.rb
25
+ spec/zookeeper_spec.rb
26
+ tags
27
+ test/test_basic.rb
28
+ test/test_callback1.rb
29
+ test/test_close.rb
30
+ test/test_esoteric.rb
31
+ test/test_watcher1.rb
32
+ test/test_watcher2.rb
33
+ zookeeper.gemspec
data/README ADDED
@@ -0,0 +1,42 @@
1
+ zookeeper
2
+
3
+ An interface to the Zookeeper distributed configuration server.
4
+
5
+ == License
6
+
7
+ Copyright 2008 Phillip Pearson, and 2010 Twitter, Inc. Licensed under the
8
+ MIT License. See the included LICENSE file. Portions copyright 2008-2010
9
+ the Apache Software Foundation, licensed under the Apache 2 license, and
10
+ used with permission.
11
+
12
+ == Install
13
+
14
+ sudo gem install zookeeper
15
+
16
+ == Usage
17
+
18
+ Connect to a server:
19
+
20
+ require 'rubygems'
21
+ require 'zookeeper'
22
+ z = Zookeeper.new("localhost:2181")
23
+ z.get_children(:path => "/")
24
+
25
+ == Idioms
26
+
27
+ The following methods are initially supported:
28
+ get
29
+ set
30
+ get_children
31
+ stat
32
+ create
33
+ delete
34
+ get_acl
35
+ set_acl
36
+
37
+ All support async callbacks. get, get_children and stat support both
38
+ watchers and callbacks.
39
+
40
+ Calls take a dictionary of parameters. With the exception of set_acl, the
41
+ only required parameter is :path. Each call returns a dictionary with at
42
+ minimum two keys :req_id and :rc.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'echoe'
2
+
3
+ Echoe.new("zookeeper") do |p|
4
+ p.author = "Phillip Pearson, Eric Maland, Evan Weaver, Brian Wickman"
5
+ p.project = "fauna"
6
+ p.summary = "An interface to the Zookeeper distributed configuration server."
7
+ p.url = "https://github.com/twitter/zookeeper"
8
+ p.docs_host = "blog.evanweaver.com:~/www/bax/public/files/doc/"
9
+ p.clean_pattern += ["ext/lib", "ext/include", "ext/c", "ext/bin", "ext/conftest.dSYM"]
10
+ p.rdoc_pattern = /README|TODO|LICENSE|CHANGELOG|BENCH|COMPAT|zookeeper_c.c|zookeeper.rb/
11
+ end
12
+
13
+ namespace :mb do
14
+ task :build_gems do
15
+ sh "gem build slyphon-zookeeper.gemspec"
16
+ ENV['JAVA_GEM'] = '1'
17
+ sh "gem build slyphon-zookeeper.gemspec"
18
+ end
19
+ end
@@ -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
+
data/ext/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ bin
2
+ include
3
+ lib
4
+ *.bundle
5
+
data/ext/extconf.rb ADDED
@@ -0,0 +1,56 @@
1
+
2
+ require 'mkmf'
3
+ require 'rbconfig'
4
+
5
+ HERE = File.expand_path(File.dirname(__FILE__))
6
+ BUNDLE = Dir.glob("zkc-*.tar.gz").first
7
+ BUNDLE_PATH = "c"
8
+
9
+ $CFLAGS = "#{RbConfig::CONFIG['CFLAGS']} #{$CFLAGS}".gsub("$(cflags)", "").gsub("-arch ppc", "")
10
+ $LDFLAGS = "#{RbConfig::CONFIG['LDFLAGS']} #{$LDFLAGS}".gsub("$(ldflags)", "").gsub("-arch ppc", "")
11
+ $CXXFLAGS = " -std=gnu++98 #{$CFLAGS}"
12
+ $CPPFLAGS = $ARCH_FLAG = $DLDFLAGS = ""
13
+
14
+ if ENV['DEBUG']
15
+ puts "Setting debug flags."
16
+ $CFLAGS << " -O0 -ggdb3 -DHAVE_DEBUG"
17
+ $EXTRA_CONF = " --enable-debug"
18
+ $CFLAGS.gsub!(/ -O[^0] /, ' ')
19
+ end
20
+
21
+ $includes = " -I#{HERE}/include"
22
+ $libraries = " -L#{HERE}/lib -L#{RbConfig::CONFIG['libdir']}"
23
+ $CFLAGS = "#{$includes} #{$libraries} #{$CFLAGS}"
24
+ $LDFLAGS = "#{$libraries} #{$LDFLAGS}"
25
+ $LIBPATH = ["#{HERE}/lib"]
26
+ $DEFLIBPATH = []
27
+
28
+ Dir.chdir(HERE) do
29
+ if File.exist?("lib")
30
+ puts "Zkc already built; run 'rake clean' first if you need to rebuild."
31
+ else
32
+ puts "Building zkc."
33
+ puts(cmd = "tar xzf #{BUNDLE} 2>&1")
34
+ raise "'#{cmd}' failed" unless system(cmd)
35
+
36
+ Dir.chdir(BUNDLE_PATH) do
37
+ puts(cmd = "env CC=gcc CXX=g++ CFLAGS='-fPIC #{$CFLAGS}' LDFLAGS='-fPIC #{$LDFLAGS}' ./configure --prefix=#{HERE} --without-cppunit --disable-dependency-tracking #{$EXTRA_CONF} 2>&1")
38
+ raise "'#{cmd}' failed" unless system(cmd)
39
+ puts(cmd = "make CXXFLAGS='#{$CXXFLAGS}' CFLAGS='-fPIC #{$CFLAGS}' LDFLAGS='-fPIC #{$LDFLAGS}' || true 2>&1")
40
+ raise "'#{cmd}' failed" unless system(cmd)
41
+ puts(cmd = "make install || true 2>&1")
42
+ raise "'#{cmd}' failed" unless system(cmd)
43
+ end
44
+
45
+ system("rm -rf #{BUNDLE_PATH}") unless ENV['DEBUG'] or ENV['DEV']
46
+ end
47
+ end
48
+
49
+ # Absolutely prevent the linker from picking up any other zookeeper_mt
50
+ Dir.chdir("#{HERE}/lib") do
51
+ system("cp -f libzookeeper_mt.a libzookeeper_mt_gem.a")
52
+ system("cp -f libzookeeper_mt.la libzookeeper_mt_gem.la")
53
+ end
54
+ $LIBS << " -lzookeeper_mt_gem"
55
+
56
+ create_makefile 'zookeeper_c'
Binary file
@@ -0,0 +1,117 @@
1
+ # The low-level wrapper-specific methods for the C lib
2
+ # subclassed by the top-level Zookeeper class
3
+ class ZookeeperBase < CZookeeper
4
+ include ZookeeperCommon
5
+ include ZookeeperCallbacks
6
+ include ZookeeperConstants
7
+ include ZookeeperExceptions
8
+ include ZookeeperACLs
9
+ include ZookeeperStat
10
+
11
+
12
+ ZKRB_GLOBAL_CB_REQ = -1
13
+
14
+ # debug levels
15
+ ZOO_LOG_LEVEL_ERROR = 1
16
+ ZOO_LOG_LEVEL_WARN = 2
17
+ ZOO_LOG_LEVEL_INFO = 3
18
+ ZOO_LOG_LEVEL_DEBUG = 4
19
+
20
+ def reopen(timeout = 10, watcher=nil)
21
+ watcher ||= @default_watcher
22
+
23
+ @req_mutex.synchronize do
24
+ # flushes all outstanding watcher reqs.
25
+ @watcher_req = {}
26
+ set_default_global_watcher(&watcher)
27
+ end
28
+
29
+ init(@host)
30
+
31
+ if timeout > 0
32
+ time_to_stop = Time.now + timeout
33
+ until state == Zookeeper::ZOO_CONNECTED_STATE
34
+ break if Time.now > time_to_stop
35
+ sleep 0.1
36
+ end
37
+ end
38
+
39
+ state
40
+ end
41
+
42
+ def initialize(host, timeout = 10, watcher=nil)
43
+ @watcher_reqs = {}
44
+ @completion_reqs = {}
45
+ @req_mutex = Monitor.new
46
+ @current_req_id = 1
47
+ @host = host
48
+
49
+ watcher ||= get_default_global_watcher
50
+
51
+ @_running = nil # used by the C layer
52
+ reopen(timeout, watcher)
53
+ return nil unless connected?
54
+ setup_dispatch_thread!
55
+ end
56
+
57
+ # if either of these happen, the user will need to renegotiate a connection via reopen
58
+ def assert_open
59
+ raise ZookeeperException::SessionExpired if state == ZOO_EXPIRED_SESSION_STATE
60
+ raise ZookeeperException::ConnectionClosed unless connected?
61
+ end
62
+
63
+ def connected?
64
+ state == ZOO_CONNECTED_STATE
65
+ end
66
+
67
+ def connecting?
68
+ state == ZOO_CONNECTING_STATE
69
+ end
70
+
71
+ def associating?
72
+ state == ZOO_ASSOCIATING_STATE
73
+ end
74
+
75
+ def close
76
+ @_running = false;
77
+ wake_event_loop!
78
+
79
+ @dispatcher.join
80
+
81
+ super
82
+ end
83
+
84
+ # set the watcher object/proc that will receive all global events (such as session/state events)
85
+ def set_default_global_watcher(&block)
86
+ @req_mutex.synchronize do
87
+ @default_watcher = block # save this here for reopen() to use
88
+ @watcher_reqs[ZKRB_GLOBAL_CB_REQ] = { :watcher => @default_watcher, :watcher_context => nil }
89
+ end
90
+ end
91
+
92
+ protected
93
+ def running?
94
+ false|@_running
95
+ end
96
+
97
+ def setup_dispatch_thread!
98
+ @dispatcher = Thread.new do
99
+ while running?
100
+ begin # calling user code, so protect ourselves
101
+ dispatch_next_callback
102
+ rescue Exception => e
103
+ $stderr.puts "Error in dispatch thread, #{e.class}: #{e.message}\n" << e.backtrace.map{|n| "\t#{n}"}.join("\n")
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ # TODO: Make all global puts configurable
110
+ def get_default_global_watcher
111
+ Proc.new { |args|
112
+ logger.debug { "Ruby ZK Global CB called type=#{event_by_value(args[:type])} state=#{state_by_value(args[:state])}" }
113
+ true
114
+ }
115
+ end
116
+ end
117
+