zookeeper 0.4.4 → 0.9.3
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/.gitignore +10 -0
- data/CHANGELOG +95 -0
- data/Gemfile +17 -0
- data/Manifest +8 -2
- data/README.markdown +59 -0
- data/Rakefile +137 -7
- data/ext/.gitignore +6 -0
- data/ext/Rakefile +51 -0
- data/ext/c_zookeeper.rb +212 -0
- data/ext/dbg.h +53 -0
- data/ext/depend +5 -0
- data/ext/extconf.rb +44 -15
- data/ext/generate_gvl_code.rb +316 -0
- data/ext/zkc-3.3.5.tar.gz +0 -0
- data/ext/zkrb_wrapper.c +731 -0
- data/ext/zkrb_wrapper.h +330 -0
- data/ext/zkrb_wrapper_compat.c +15 -0
- data/ext/zkrb_wrapper_compat.h +11 -0
- data/ext/zookeeper_base.rb +211 -0
- data/ext/zookeeper_c.c +268 -97
- data/ext/zookeeper_lib.c +157 -92
- data/ext/zookeeper_lib.h +12 -6
- data/java/zookeeper_base.rb +477 -0
- data/lib/zookeeper/acls.rb +10 -1
- data/lib/zookeeper/callbacks.rb +5 -3
- data/lib/zookeeper/common/queue_with_pipe.rb +78 -0
- data/lib/zookeeper/common.rb +174 -0
- data/lib/zookeeper/constants.rb +31 -28
- data/lib/zookeeper/em_client.rb +55 -0
- data/lib/zookeeper/exceptions.rb +10 -2
- data/lib/zookeeper/stat.rb +11 -2
- data/lib/zookeeper/version.rb +6 -0
- data/lib/zookeeper.rb +155 -122
- data/notes.txt +14 -0
- data/spec/c_zookeeper_spec.rb +50 -0
- data/spec/chrooted_connection_spec.rb +81 -0
- data/spec/default_watcher_spec.rb +41 -0
- data/spec/em_spec.rb +51 -0
- data/spec/log4j.properties +17 -0
- data/spec/shared/all_success_return_values.rb +10 -0
- data/spec/shared/connection_examples.rb +1018 -0
- data/spec/spec_helper.rb +119 -0
- data/spec/support/progress_formatter.rb +15 -0
- data/spec/zookeeper_spec.rb +24 -0
- data/zookeeper.gemspec +37 -25
- metadata +78 -34
- data/README +0 -42
- data/ext/zkc-3.3.2.tar.gz +0 -0
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'zookeeper/exceptions'
|
2
|
+
|
3
|
+
module ZookeeperCommon
|
4
|
+
# sigh, i guess define this here?
|
5
|
+
ZKRB_GLOBAL_CB_REQ = -1
|
6
|
+
|
7
|
+
def event_dispatch_thread?
|
8
|
+
@dispatcher && (@dispatcher == Thread.current)
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
def get_next_event(blocking=true)
|
13
|
+
@event_queue.pop(!blocking).tap do |event|
|
14
|
+
logger.debug { "#{self.class}##{__method__} delivering event #{event.inspect}" }
|
15
|
+
end
|
16
|
+
rescue ThreadError
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup_call(meth_name, opts)
|
21
|
+
req_id = nil
|
22
|
+
@mutex.synchronize {
|
23
|
+
req_id = @current_req_id
|
24
|
+
@current_req_id += 1
|
25
|
+
setup_completion(req_id, meth_name, opts) if opts[:callback]
|
26
|
+
setup_watcher(req_id, opts) if opts[:watcher]
|
27
|
+
}
|
28
|
+
req_id
|
29
|
+
end
|
30
|
+
|
31
|
+
def setup_watcher(req_id, call_opts)
|
32
|
+
@watcher_reqs[req_id] = { :watcher => call_opts[:watcher],
|
33
|
+
:context => call_opts[:watcher_context] }
|
34
|
+
end
|
35
|
+
|
36
|
+
# as a hack, to provide consistency between the java implementation and the C
|
37
|
+
# implementation when dealing w/ chrooted connections, we override this in
|
38
|
+
# ext/zookeeper_base.rb to wrap the callback in a chroot-path-stripping block.
|
39
|
+
#
|
40
|
+
# we don't use meth_name here, but we need it in the C implementation
|
41
|
+
#
|
42
|
+
def setup_completion(req_id, meth_name, call_opts)
|
43
|
+
@completion_reqs[req_id] = { :callback => call_opts[:callback],
|
44
|
+
:context => call_opts[:callback_context] }
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_watcher(req_id)
|
48
|
+
@mutex.synchronize {
|
49
|
+
(req_id == ZKRB_GLOBAL_CB_REQ) ? @watcher_reqs[req_id] : @watcher_reqs.delete(req_id)
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_completion(req_id)
|
54
|
+
@mutex.synchronize { @completion_reqs.delete(req_id) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def setup_dispatch_thread!
|
58
|
+
logger.debug { "starting dispatch thread" }
|
59
|
+
@dispatcher ||= Thread.new do
|
60
|
+
while true
|
61
|
+
begin
|
62
|
+
dispatch_next_callback(get_next_event(true))
|
63
|
+
rescue QueueWithPipe::ShutdownException
|
64
|
+
logger.info { "dispatch thread exiting, got shutdown exception" }
|
65
|
+
break
|
66
|
+
rescue Exception => e
|
67
|
+
$stderr.puts ["#{e.class}: #{e.message}", e.backtrace.map { |n| "\t#{n}" }.join("\n")].join("\n")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
signal_dispatch_thread_exit!
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# this method is part of the reopen/close code, and is responsible for
|
75
|
+
# shutting down the dispatch thread.
|
76
|
+
#
|
77
|
+
# @dispatcher will be nil when this method exits
|
78
|
+
#
|
79
|
+
def stop_dispatch_thread!
|
80
|
+
logger.debug { "#{self.class}##{__method__}" }
|
81
|
+
|
82
|
+
if @dispatcher
|
83
|
+
if @dispatcher.join(0)
|
84
|
+
@dispatcher = nil
|
85
|
+
return
|
86
|
+
end
|
87
|
+
|
88
|
+
@mutex.synchronize do
|
89
|
+
event_queue.graceful_close!
|
90
|
+
|
91
|
+
# we now release the mutex so that dispatch_next_callback can grab it
|
92
|
+
# to do what it needs to do while delivering events
|
93
|
+
#
|
94
|
+
# wait for a maximum of 2 sec for dispatcher to signal exit (should be
|
95
|
+
# fast)
|
96
|
+
@dispatch_shutdown_cond.wait(2)
|
97
|
+
|
98
|
+
# wait for another 2 sec for the thread to join
|
99
|
+
unless @dispatcher.join(2)
|
100
|
+
logger.error { "Dispatch thread did not join cleanly, continuing" }
|
101
|
+
end
|
102
|
+
@dispatcher = nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def signal_dispatch_thread_exit!
|
108
|
+
@mutex.synchronize do
|
109
|
+
logger.debug { "dispatch thread exiting!" }
|
110
|
+
@dispatch_shutdown_cond.broadcast
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def dispatch_next_callback(hash)
|
115
|
+
return nil unless hash
|
116
|
+
|
117
|
+
Zookeeper.logger.debug { "get_next_event returned: #{prettify_event(hash).inspect}" }
|
118
|
+
|
119
|
+
is_completion = hash.has_key?(:rc)
|
120
|
+
|
121
|
+
hash[:stat] = ZookeeperStat::Stat.new(hash[:stat]) if hash.has_key?(:stat)
|
122
|
+
hash[:acl] = hash[:acl].map { |acl| ZookeeperACLs::ACL.new(acl) } if hash[:acl]
|
123
|
+
|
124
|
+
callback_context = is_completion ? get_completion(hash[:req_id]) : get_watcher(hash[:req_id])
|
125
|
+
|
126
|
+
# When connectivity to the server has been lost (as indicated by SESSION_EVENT)
|
127
|
+
# we want to rerun the callback at a later time when we eventually do have
|
128
|
+
# a valid response.
|
129
|
+
if hash[:type] == ZookeeperConstants::ZOO_SESSION_EVENT
|
130
|
+
is_completion ? setup_completion(hash[:req_id], callback_context) : setup_watcher(hash[:req_id], callback_context)
|
131
|
+
end
|
132
|
+
if callback_context
|
133
|
+
callback = is_completion ? callback_context[:callback] : callback_context[:watcher]
|
134
|
+
|
135
|
+
hash[:context] = callback_context[:context]
|
136
|
+
|
137
|
+
# TODO: Eventually enforce derivation from Zookeeper::Callback
|
138
|
+
if callback.respond_to?(:call)
|
139
|
+
callback.call(hash)
|
140
|
+
else
|
141
|
+
# puts "dispatch_next_callback found non-callback => #{callback.inspect}"
|
142
|
+
end
|
143
|
+
else
|
144
|
+
logger.warn { "Duplicate event received (no handler for req_id #{hash[:req_id]}, event: #{hash.inspect}" }
|
145
|
+
end
|
146
|
+
true
|
147
|
+
end
|
148
|
+
|
149
|
+
def assert_supported_keys(args, supported)
|
150
|
+
unless (args.keys - supported).empty?
|
151
|
+
raise ZookeeperExceptions::ZookeeperException::BadArguments, # this heirarchy is kind of retarded
|
152
|
+
"Supported arguments are: #{supported.inspect}, but arguments #{args.keys.inspect} were supplied instead"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def assert_required_keys(args, required)
|
157
|
+
unless (required - args.keys).empty?
|
158
|
+
raise ZookeeperExceptions::ZookeeperException::BadArguments,
|
159
|
+
"Required arguments are: #{required.inspect}, but only the arguments #{args.keys.inspect} were supplied."
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
def prettify_event(hash)
|
165
|
+
hash.dup.tap do |h|
|
166
|
+
# pretty up the event display
|
167
|
+
h[:type] = ZookeeperConstants::EVENT_TYPE_NAMES.fetch(h[:type]) if h[:type]
|
168
|
+
h[:state] = ZookeeperConstants::STATE_NAMES.fetch(h[:state]) if h[:state]
|
169
|
+
h[:req_id] = :global_session if h[:req_id] == -1
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
require 'zookeeper/common/queue_with_pipe'
|
data/lib/zookeeper/constants.rb
CHANGED
@@ -6,6 +6,7 @@ module ZookeeperConstants
|
|
6
6
|
# session state
|
7
7
|
ZOO_EXPIRED_SESSION_STATE = -112
|
8
8
|
ZOO_AUTH_FAILED_STATE = -113
|
9
|
+
ZOO_CLOSED_STATE = 0
|
9
10
|
ZOO_CONNECTING_STATE = 1
|
10
11
|
ZOO_ASSOCIATING_STATE = 2
|
11
12
|
ZOO_CONNECTED_STATE = 3
|
@@ -17,38 +18,40 @@ module ZookeeperConstants
|
|
17
18
|
ZOO_CHILD_EVENT = 4
|
18
19
|
ZOO_SESSION_EVENT = -1
|
19
20
|
ZOO_NOTWATCHING_EVENT = -2
|
20
|
-
|
21
|
-
def print_events
|
22
|
-
puts "ZK events:"
|
23
|
-
ZookeeperConstants::constants.each do |c|
|
24
|
-
puts "\t #{c}" if c =~ /^ZOO..*EVENT$/
|
25
|
-
end
|
26
|
-
end
|
27
21
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
22
|
+
# only used by the C extension
|
23
|
+
ZOO_LOG_LEVEL_ERROR = 1
|
24
|
+
ZOO_LOG_LEVEL_WARN = 2
|
25
|
+
ZOO_LOG_LEVEL_INFO = 3
|
26
|
+
ZOO_LOG_LEVEL_DEBUG = 4
|
27
|
+
|
28
|
+
# used to find the name for a numeric event
|
29
|
+
# @private
|
30
|
+
EVENT_TYPE_NAMES = {
|
31
|
+
1 => 'created',
|
32
|
+
2 => 'deleted',
|
33
|
+
3 => 'changed',
|
34
|
+
4 => 'child',
|
35
|
+
-1 => 'session',
|
36
|
+
-2 => 'notwatching',
|
37
|
+
}
|
34
38
|
|
39
|
+
# used to pretty print the state name
|
40
|
+
# @private
|
41
|
+
STATE_NAMES = {
|
42
|
+
-112 => 'expired_session',
|
43
|
+
-113 => 'auth_failed',
|
44
|
+
0 => 'closed',
|
45
|
+
1 => 'connecting',
|
46
|
+
2 => 'associating',
|
47
|
+
3 => 'connected',
|
48
|
+
}
|
49
|
+
|
35
50
|
def event_by_value(v)
|
36
|
-
|
37
|
-
ZookeeperConstants::constants.each do |c|
|
38
|
-
next unless c =~ /^ZOO..*EVENT$/
|
39
|
-
if eval("ZookeeperConstants::#{c}") == v
|
40
|
-
return c
|
41
|
-
end
|
42
|
-
end
|
51
|
+
(name = EVENT_TYPE_NAMES[v]) ? "ZOO_#{name.upcase}_EVENT" : ''
|
43
52
|
end
|
44
|
-
|
53
|
+
|
45
54
|
def state_by_value(v)
|
46
|
-
|
47
|
-
ZookeeperConstants::constants.each do |c|
|
48
|
-
next unless c =~ /^ZOO..*STATE$/
|
49
|
-
if eval("ZookeeperConstants::#{c}") == v
|
50
|
-
return c
|
51
|
-
end
|
52
|
-
end
|
55
|
+
(name = STATE_NAMES[v]) ? "ZOO_#{name.upcase}_STATE" : ''
|
53
56
|
end
|
54
57
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'zookeeper'
|
2
|
+
require 'eventmachine'
|
3
|
+
|
4
|
+
module ZookeeperEM
|
5
|
+
class Client < Zookeeper
|
6
|
+
# @private
|
7
|
+
# the EM Connection instance we receive once we call EM.watch on our selectable_io
|
8
|
+
attr_reader :em_connection
|
9
|
+
|
10
|
+
def initialize(*a, &b)
|
11
|
+
@on_close = EM::DefaultDeferrable.new
|
12
|
+
@on_attached = EM::DefaultDeferrable.new
|
13
|
+
@em_connection = nil
|
14
|
+
logger.debug { "ZookeeperEM::Client obj_id %x: init" % [object_id] }
|
15
|
+
super(*a, &b)
|
16
|
+
on_attached.succeed
|
17
|
+
end
|
18
|
+
|
19
|
+
# EM::DefaultDeferrable that will be called back when our em_connection has been detached
|
20
|
+
# and we've completed the close operation
|
21
|
+
def on_close(&block)
|
22
|
+
@on_close.callback(&block) if block
|
23
|
+
@on_close
|
24
|
+
end
|
25
|
+
|
26
|
+
# called after we've successfully registered our selectable_io to be
|
27
|
+
# managed by the EM reactor
|
28
|
+
def on_attached(&block)
|
29
|
+
@on_attached.callback(&block) if block
|
30
|
+
@on_attached
|
31
|
+
end
|
32
|
+
|
33
|
+
def dispatch_next_callback(hash)
|
34
|
+
EM.schedule do
|
35
|
+
if running? and not closed?
|
36
|
+
logger.debug { "#{self.class}##{__method__} dispatch_next_callback: #{hash.inspect}: reactor_thread? #{EM.reactor_thread?}, running? #{running?}, closed? #{closed?}" }
|
37
|
+
super(hash)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def close(&block)
|
43
|
+
on_close(&block)
|
44
|
+
super()
|
45
|
+
on_close.succeed
|
46
|
+
end
|
47
|
+
|
48
|
+
# Because eventmachine is single-threaded, and events are dispatched on the
|
49
|
+
# reactor thread we just delegate this to EM.reactor_thread?
|
50
|
+
def event_dispatch_thread?
|
51
|
+
EM.reactor_thread?
|
52
|
+
end
|
53
|
+
end # Client
|
54
|
+
end # ZookeeperEM
|
55
|
+
|
data/lib/zookeeper/exceptions.rb
CHANGED
@@ -27,7 +27,7 @@ module ZookeeperExceptions
|
|
27
27
|
ZNOTHING = -117
|
28
28
|
ZSESSIONMOVED = -118
|
29
29
|
|
30
|
-
class ZookeeperException <
|
30
|
+
class ZookeeperException < StandardError
|
31
31
|
class EverythingOk < ZookeeperException; end
|
32
32
|
class SystemError < ZookeeperException; end
|
33
33
|
class RunTimeInconsistency < ZookeeperException; end
|
@@ -52,7 +52,15 @@ module ZookeeperExceptions
|
|
52
52
|
class Closing < ZookeeperException; end
|
53
53
|
class Nothing < ZookeeperException; end
|
54
54
|
class SessionMoved < ZookeeperException; end
|
55
|
-
|
55
|
+
|
56
|
+
# these are Ruby client exceptions
|
57
|
+
class ConnectionClosed < ZookeeperException; end
|
58
|
+
class NotConnected < ZookeeperException; end
|
59
|
+
class ShuttingDownException < ZookeeperException; end
|
60
|
+
class DataTooLargeException < ZookeeperException; end
|
61
|
+
|
62
|
+
# yes, make an alias, this is the way zookeeper refers to it
|
63
|
+
ExpiredSession = SessionExpired
|
56
64
|
|
57
65
|
def self.by_code(code)
|
58
66
|
case code
|
data/lib/zookeeper/stat.rb
CHANGED
@@ -1,12 +1,21 @@
|
|
1
1
|
module ZookeeperStat
|
2
2
|
class Stat
|
3
|
-
attr_reader :version, :exists, :czxid, :mzxid, :ctime, :mtime, :
|
3
|
+
attr_reader :version, :exists, :czxid, :mzxid, :ctime, :mtime, :cversion, :aversion, :ephemeralOwner, :dataLength, :numChildren, :pzxid
|
4
|
+
|
5
|
+
alias :ephemeral_owner :ephemeralOwner
|
6
|
+
alias :num_children :numChildren
|
7
|
+
alias :data_length :dataLength
|
8
|
+
|
4
9
|
def initialize(val)
|
5
10
|
@exists = !!val
|
6
11
|
@czxid, @mzxid, @ctime, @mtime, @version, @cversion, @aversion,
|
7
12
|
@ephemeralOwner, @dataLength, @numChildren, @pzxid = val if val.is_a?(Array)
|
8
13
|
val.each { |k,v| instance_variable_set "@#{k}", v } if val.is_a?(Hash)
|
9
|
-
raise ArgumentError unless (val.is_a?(Hash) or val.is_a?(Array) or val
|
14
|
+
raise ArgumentError unless (val.is_a?(Hash) or val.is_a?(Array) or val.nil?)
|
15
|
+
end
|
16
|
+
|
17
|
+
def exists?
|
18
|
+
@exists
|
10
19
|
end
|
11
20
|
end
|
12
21
|
end
|