slyphon-zookeeper 0.1.7 → 0.2.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/.gitignore +1 -0
- data/ext/zookeeper_base.rb +46 -16
- data/ext/zookeeper_c.c +99 -16
- data/ext/zookeeper_lib.c +23 -7
- data/java/zookeeper_base.rb +154 -37
- data/lib/zookeeper.rb +72 -1
- data/lib/zookeeper/common.rb +7 -21
- data/lib/zookeeper/em_client.rb +135 -0
- data/slyphon-zookeeper.gemspec +2 -2
- data/spec/em_spec.rb +138 -0
- data/spec/spec_helper.rb +19 -0
- metadata +110 -86
data/lib/zookeeper.rb
CHANGED
@@ -19,6 +19,18 @@ end
|
|
19
19
|
require 'zookeeper_base'
|
20
20
|
|
21
21
|
class Zookeeper < ZookeeperBase
|
22
|
+
unless defined?(@@logger)
|
23
|
+
@@logger = Logger.new('/dev/null').tap { |l| l.level = Logger::FATAL } # UNIX: FOR GREAT JUSTICE !!
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.logger
|
27
|
+
@@logger
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.logger=(logger)
|
31
|
+
@@logger = logger
|
32
|
+
end
|
33
|
+
|
22
34
|
def reopen(timeout=10, watcher=nil)
|
23
35
|
super
|
24
36
|
end
|
@@ -130,6 +142,11 @@ class Zookeeper < ZookeeperBase
|
|
130
142
|
options[:callback] ? rv : rv.merge(:acl => acls, :stat => Stat.new(stat))
|
131
143
|
end
|
132
144
|
|
145
|
+
# close this client and any underyling connections
|
146
|
+
def close
|
147
|
+
super
|
148
|
+
end
|
149
|
+
|
133
150
|
def state
|
134
151
|
super
|
135
152
|
end
|
@@ -146,6 +163,61 @@ class Zookeeper < ZookeeperBase
|
|
146
163
|
super
|
147
164
|
end
|
148
165
|
|
166
|
+
# for expert use only. set the underlying debug level for the C layer, has no
|
167
|
+
# effect in java
|
168
|
+
#
|
169
|
+
def self.set_debug_level(val)
|
170
|
+
super
|
171
|
+
end
|
172
|
+
|
173
|
+
# DEPRECATED: use the class-level method instead
|
174
|
+
def set_debug_level(val)
|
175
|
+
super
|
176
|
+
end
|
177
|
+
|
178
|
+
# has the underlying connection been closed?
|
179
|
+
def closed?
|
180
|
+
super
|
181
|
+
end
|
182
|
+
|
183
|
+
# is the event delivery system running?
|
184
|
+
def running?
|
185
|
+
super
|
186
|
+
end
|
187
|
+
|
188
|
+
# returns an IO object that will be readable when an event is ready for dispatching
|
189
|
+
# (for internal use only)
|
190
|
+
def selectable_io
|
191
|
+
super
|
192
|
+
end
|
193
|
+
|
194
|
+
# closes the underlying connection object
|
195
|
+
# (for internal use only)
|
196
|
+
def close_handle
|
197
|
+
super
|
198
|
+
end
|
199
|
+
|
200
|
+
protected
|
201
|
+
# used during shutdown, awaken the event delivery thread if it's blocked
|
202
|
+
# waiting for the next event
|
203
|
+
def wake_event_loop!
|
204
|
+
super
|
205
|
+
end
|
206
|
+
|
207
|
+
# starts the event delivery subsystem going. after calling this method, running? will be true
|
208
|
+
def setup_dispatch_thread!
|
209
|
+
super
|
210
|
+
end
|
211
|
+
|
212
|
+
# TODO: describe what this does
|
213
|
+
def get_default_global_watcher
|
214
|
+
super
|
215
|
+
end
|
216
|
+
|
217
|
+
def logger
|
218
|
+
Zookeeper.logger
|
219
|
+
end
|
220
|
+
|
149
221
|
private
|
150
222
|
def setup_call(opts)
|
151
223
|
req_id = nil
|
@@ -175,6 +247,5 @@ private
|
|
175
247
|
def assert_open
|
176
248
|
super
|
177
249
|
end
|
178
|
-
|
179
250
|
end
|
180
251
|
|
data/lib/zookeeper/common.rb
CHANGED
@@ -4,18 +4,9 @@ module ZookeeperCommon
|
|
4
4
|
# sigh, i guess define this here?
|
5
5
|
ZKRB_GLOBAL_CB_REQ = -1
|
6
6
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
module ClassMethods
|
12
|
-
def logger
|
13
|
-
@logger ||= Logger.new('/dev/null') # UNIX: YOU MUST USE IT!
|
14
|
-
end
|
15
|
-
|
16
|
-
def logger=(logger)
|
17
|
-
@logger = logger
|
18
|
-
end
|
7
|
+
def get_next_event(blocking=true)
|
8
|
+
return nil if closed? # protect against this happening in a callback after close
|
9
|
+
super(blocking)
|
19
10
|
end
|
20
11
|
|
21
12
|
protected
|
@@ -50,13 +41,12 @@ protected
|
|
50
41
|
@req_mutex.synchronize { @completion_reqs.delete(req_id) }
|
51
42
|
end
|
52
43
|
|
44
|
+
def dispatch_next_callback(blocking=true)
|
45
|
+
hash = get_next_event(blocking)
|
46
|
+
# Zookeeper.logger.debug { "get_next_event returned: #{hash.inspect}" }
|
53
47
|
|
54
|
-
def dispatch_next_callback
|
55
|
-
hash = get_next_event
|
56
48
|
return nil unless hash
|
57
49
|
|
58
|
-
logger.debug { "dispatch_next_callback got event: #{hash.inspect}" }
|
59
|
-
|
60
50
|
is_completion = hash.has_key?(:rc)
|
61
51
|
|
62
52
|
hash[:stat] = ZookeeperStat::Stat.new(hash[:stat]) if hash.has_key?(:stat)
|
@@ -81,9 +71,9 @@ protected
|
|
81
71
|
else
|
82
72
|
logger.warn { "Duplicate event received (no handler for req_id #{hash[:req_id]}, event: #{hash.inspect}" }
|
83
73
|
end
|
74
|
+
true
|
84
75
|
end
|
85
76
|
|
86
|
-
|
87
77
|
def assert_supported_keys(args, supported)
|
88
78
|
unless (args.keys - supported).empty?
|
89
79
|
raise ZookeeperExceptions::ZookeeperException::BadArguments, # this heirarchy is kind of retarded
|
@@ -98,9 +88,5 @@ protected
|
|
98
88
|
end
|
99
89
|
end
|
100
90
|
|
101
|
-
# supplied by parent class impl.
|
102
|
-
def logger
|
103
|
-
self.class.logger
|
104
|
-
end
|
105
91
|
end
|
106
92
|
|
@@ -0,0 +1,135 @@
|
|
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
|
+
super(*a, &b)
|
15
|
+
end
|
16
|
+
|
17
|
+
# EM::DefaultDeferrable that will be called back when our em_connection has been detached
|
18
|
+
# and we've completed the close operation
|
19
|
+
def on_close(&block)
|
20
|
+
@on_close.callback(&block) if block
|
21
|
+
@on_close
|
22
|
+
end
|
23
|
+
|
24
|
+
# called after we've successfully registered our selectable_io to be
|
25
|
+
# managed by the EM reactor
|
26
|
+
def on_attached(&block)
|
27
|
+
@on_attached.callback(&block) if block
|
28
|
+
@on_attached
|
29
|
+
end
|
30
|
+
|
31
|
+
# returns a Deferrable that will be called when the Zookeeper C event loop
|
32
|
+
# has been shut down
|
33
|
+
#
|
34
|
+
# if a block is given, it will be registered as a callback when the
|
35
|
+
# connection has been closed
|
36
|
+
#
|
37
|
+
def close(&block)
|
38
|
+
on_close(&block)
|
39
|
+
|
40
|
+
logger.debug { "close called, closed? #{closed?} running? #{running?}" }
|
41
|
+
|
42
|
+
if @_running
|
43
|
+
@start_stop_mutex.synchronize do
|
44
|
+
@_running = false
|
45
|
+
end
|
46
|
+
|
47
|
+
@em_connection.detach if @em_connection
|
48
|
+
@em_connection = nil
|
49
|
+
|
50
|
+
unless @_closed
|
51
|
+
@start_stop_mutex.synchronize do
|
52
|
+
logger.debug { "closing handle" }
|
53
|
+
close_handle
|
54
|
+
end
|
55
|
+
|
56
|
+
selectable_io.close unless selectable_io.closed?
|
57
|
+
end
|
58
|
+
else
|
59
|
+
logger.debug { "we are not running, so returning on_close deferred" }
|
60
|
+
end
|
61
|
+
|
62
|
+
on_close.succeed
|
63
|
+
on_close
|
64
|
+
end
|
65
|
+
|
66
|
+
# make this public as the ZKConnection object needs to call it
|
67
|
+
public :dispatch_next_callback
|
68
|
+
|
69
|
+
protected
|
70
|
+
# instead of setting up a dispatch thread here, we instead attach
|
71
|
+
# the #selectable_io to the event loop
|
72
|
+
def setup_dispatch_thread!
|
73
|
+
EM.schedule do
|
74
|
+
if running? and not closed?
|
75
|
+
begin
|
76
|
+
@em_connection = EM.watch(selectable_io, ZKConnection, self) { |cnx| cnx.notify_readable = true }
|
77
|
+
rescue Exception => e
|
78
|
+
$stderr.puts "caught exception from EM.watch(): #{e.inspect}"
|
79
|
+
end
|
80
|
+
on_attached.succeed
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# this class is handed to EventMachine.watch to handle event dispatching
|
87
|
+
# when the queue has a message waiting. There's a pipe shared between
|
88
|
+
# the event thread managed by the queue implementation in C. It's made
|
89
|
+
# available to the ruby-space through the Zookeeper#selectable_io method.
|
90
|
+
# When the pipe is readable, that means there's an event waiting. We call
|
91
|
+
# dispatch_next_event and read a single byte off the pipe.
|
92
|
+
#
|
93
|
+
class ZKConnection < EM::Connection
|
94
|
+
|
95
|
+
def initialize(zk_client)
|
96
|
+
@zk_client = zk_client
|
97
|
+
@attached = true
|
98
|
+
end
|
99
|
+
|
100
|
+
def attached?
|
101
|
+
@attached
|
102
|
+
end
|
103
|
+
|
104
|
+
def detach
|
105
|
+
return unless @attached
|
106
|
+
@attached = false
|
107
|
+
super
|
108
|
+
logger.debug { "#{self.class.name}: detached" }
|
109
|
+
end
|
110
|
+
|
111
|
+
# we have an event waiting
|
112
|
+
def notify_readable
|
113
|
+
if @zk_client.running?
|
114
|
+
# logger.debug { "#{self.class.name}: dispatching events while #{@zk_client.running?}" }
|
115
|
+
|
116
|
+
read_io_nb if @zk_client.dispatch_next_callback(false)
|
117
|
+
|
118
|
+
elsif attached?
|
119
|
+
logger.debug { "#{self.class.name}: @zk_client was not running? and attached? #{attached?}, detaching!" }
|
120
|
+
detach
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
def read_io_nb(size=1)
|
126
|
+
@io.read_nonblock(1)
|
127
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN, IOError
|
128
|
+
end
|
129
|
+
|
130
|
+
def logger
|
131
|
+
Zookeeper.logger
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
data/slyphon-zookeeper.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "slyphon-zookeeper"
|
6
|
-
s.version = '0.
|
6
|
+
s.version = '0.2.0'
|
7
7
|
|
8
8
|
s.authors = ["Phillip Pearson", "Eric Maland", "Evan Weaver", "Brian Wickman", "Neil Conway", "Jonathan D. Simms"]
|
9
9
|
s.email = ["slyphon@gmail.com"]
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.files = `git ls-files`.split("\n")
|
17
17
|
s.require_paths = ["lib"]
|
18
18
|
|
19
|
-
if ENV['JAVA_GEM']
|
19
|
+
if ENV['JAVA_GEM'] or defined?(::JRUBY_VERSION)
|
20
20
|
s.platform = 'java'
|
21
21
|
s.add_runtime_dependency('slyphon-log4j', '= 1.2.15')
|
22
22
|
s.add_runtime_dependency('slyphon-zookeeper_jar', '= 3.3.3')
|
data/spec/em_spec.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'zookeeper/em_client'
|
3
|
+
|
4
|
+
gem 'evented-spec', '~> 0.4.1'
|
5
|
+
require 'evented-spec'
|
6
|
+
|
7
|
+
|
8
|
+
describe 'ZookeeperEM' do
|
9
|
+
describe 'Client' do
|
10
|
+
include EventedSpec::SpecHelper
|
11
|
+
default_timeout 3.0
|
12
|
+
|
13
|
+
def setup_zk
|
14
|
+
@zk = ZookeeperEM::Client.new('localhost:2181')
|
15
|
+
em do
|
16
|
+
@zk.on_attached do
|
17
|
+
yield
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def teardown_and_done
|
23
|
+
@zk.close { done }
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'selectable_io' do
|
27
|
+
it %[should return an IO object] do
|
28
|
+
setup_zk do
|
29
|
+
@zk.selectable_io.should be_instance_of(IO)
|
30
|
+
teardown_and_done
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it %[should not be closed] do
|
35
|
+
setup_zk do
|
36
|
+
@zk.selectable_io.should_not be_closed
|
37
|
+
teardown_and_done
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
before do
|
42
|
+
@data_cb = ZookeeperCallbacks::DataCallback.new do
|
43
|
+
logger.debug { "cb called: #{@data_cb.inspect}" }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it %[should be read-ready if there's an event waiting] do
|
48
|
+
setup_zk do
|
49
|
+
@zk.get(:path => "/", :callback => @data_cb)
|
50
|
+
|
51
|
+
r, *_ = IO.select([@zk.selectable_io], [], [], 2)
|
52
|
+
|
53
|
+
r.should be_kind_of(Array)
|
54
|
+
|
55
|
+
teardown_and_done
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it %[should not be read-ready if there's no event] do
|
60
|
+
pending "get this to work in jruby" if defined?(::JRUBY_VERSION)
|
61
|
+
# there's always an initial event after connect
|
62
|
+
|
63
|
+
# except in jruby
|
64
|
+
# if defined?(::JRUBY_VERSION)
|
65
|
+
# @zk.get(:path => '/', :callback => @data_cb)
|
66
|
+
# end
|
67
|
+
|
68
|
+
setup_zk do
|
69
|
+
events = 0
|
70
|
+
|
71
|
+
while true
|
72
|
+
r, *_ = IO.select([@zk.selectable_io], [], [], 0.2)
|
73
|
+
|
74
|
+
break unless r
|
75
|
+
|
76
|
+
h = @zk.get_next_event(false)
|
77
|
+
@zk.selectable_io.read(1)
|
78
|
+
|
79
|
+
events += 1
|
80
|
+
|
81
|
+
h.should be_kind_of(Hash)
|
82
|
+
end
|
83
|
+
|
84
|
+
events.should == 1
|
85
|
+
|
86
|
+
teardown_and_done
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'em_connection' do
|
92
|
+
before do
|
93
|
+
@zk = ZookeeperEM::Client.new('localhost:2181')
|
94
|
+
end
|
95
|
+
|
96
|
+
it %[should be nil before the reactor is started] do
|
97
|
+
@zk.em_connection.should be_nil
|
98
|
+
|
99
|
+
em do
|
100
|
+
teardown_and_done
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it %[should fire off the on_attached callbacks once the reactor is managing us] do
|
105
|
+
@zk.on_attached do |*|
|
106
|
+
@zk.em_connection.should_not be_nil
|
107
|
+
@zk.em_connection.should be_instance_of(ZookeeperEM::ZKConnection)
|
108
|
+
teardown_and_done
|
109
|
+
end
|
110
|
+
|
111
|
+
em do
|
112
|
+
EM.reactor_running?.should be_true
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe 'callbacks' do
|
118
|
+
it %[should be called on the reactor thread] do
|
119
|
+
cb = lambda do |h|
|
120
|
+
EM.reactor_thread?.should be_true
|
121
|
+
logger.debug { "called back on the reactor thread? #{EM.reactor_thread?}" }
|
122
|
+
teardown_and_done
|
123
|
+
end
|
124
|
+
|
125
|
+
setup_zk do
|
126
|
+
@zk.on_attached do |*|
|
127
|
+
logger.debug { "on_attached called" }
|
128
|
+
rv = @zk.get(:path => '/', :callback => cb)
|
129
|
+
logger.debug { "rv from @zk.get: #{rv.inspect}" }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
|