zk-eventmachine 0.1.13 → 0.2.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +0 -1
- data/Gemfile +3 -1
- data/Rakefile +0 -35
- data/lib/z_k/z_k_event_machine/callback.rb +1 -1
- data/lib/z_k/z_k_event_machine/client.rb +32 -92
- data/lib/z_k/z_k_event_machine/deferred.rb +39 -0
- data/lib/z_k/z_k_event_machine/synchrony_client.rb +111 -0
- data/lib/z_k/z_k_event_machine/unixisms.rb +0 -5
- data/lib/z_k/z_k_event_machine/version.rb +1 -1
- data/lib/z_k/z_k_event_machine.rb +4 -1
- data/spec/spec_helper.rb +2 -19
- data/spec/support/logging.rb +5 -10
- data/spec/z_k/z_k_event_machine/client_spec.rb +61 -181
- data/spec/z_k/z_k_event_machine/synchrony_client_spec.rb +339 -0
- data/zk-eventmachine.gemspec +9 -11
- metadata +86 -178
- data/.yardopts +0 -8
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,37 +1,2 @@
|
|
1
1
|
require 'bundler'
|
2
2
|
Bundler::GemHelper.install_tasks
|
3
|
-
|
4
|
-
task :yard do
|
5
|
-
Bundler.setup
|
6
|
-
require 'yard'
|
7
|
-
|
8
|
-
YARD::Rake::YardocTask.new(:run_yardoc) do |t|
|
9
|
-
t.files = ['lib/**/*.rb']
|
10
|
-
end
|
11
|
-
|
12
|
-
Rake::Task[:run_yardoc].invoke
|
13
|
-
end
|
14
|
-
|
15
|
-
%w[1.8.7 1.9.2 1.9.3 jruby].each do |rvm_ruby|
|
16
|
-
gemset_name = 'zk-em'
|
17
|
-
ruby_with_gemset = "#{rvm_ruby}@#{gemset_name}"
|
18
|
-
create_gemset_name = "mb:#{rvm_ruby}:create_gemset"
|
19
|
-
bundle_task_name = "mb:#{rvm_ruby}:bundle_install"
|
20
|
-
rspec_task_name = "mb:#{rvm_ruby}:run_rspec"
|
21
|
-
|
22
|
-
task create_gemset_name do
|
23
|
-
sh "rvm #{rvm_ruby} do rvm gemset create #{gemset_name}"
|
24
|
-
end
|
25
|
-
|
26
|
-
task bundle_task_name => create_gemset_name do
|
27
|
-
rm_f 'Gemfile.lock'
|
28
|
-
sh "rvm #{ruby_with_gemset} do bundle install"
|
29
|
-
end
|
30
|
-
|
31
|
-
task rspec_task_name => bundle_task_name do
|
32
|
-
sh "rvm #{ruby_with_gemset} do bundle exec rspec spec --fail-fast"
|
33
|
-
end
|
34
|
-
|
35
|
-
task 'mb:test_all' => rspec_task_name
|
36
|
-
end
|
37
|
-
|
@@ -1,63 +1,26 @@
|
|
1
1
|
module ZK
|
2
2
|
module ZKEventMachine
|
3
3
|
class Client < ZK::Client::Base
|
4
|
-
include Deferred::Accessors
|
5
4
|
include ZK::Logging
|
6
5
|
include Unixisms
|
7
6
|
|
8
7
|
DEFAULT_TIMEOUT = 10
|
9
8
|
|
10
|
-
#
|
11
|
-
#
|
12
|
-
|
13
|
-
#
|
14
|
-
# once this deferred has been fired, it will be replaced with a new
|
15
|
-
# deferred, so callbacks must be re-registered, and *should* be
|
16
|
-
# re-registered *within* the callback to avoid missing events
|
17
|
-
#
|
18
|
-
# @method on_connection_lost
|
19
|
-
# @return [Deferred::Default]
|
20
|
-
deferred_event :connection_lost
|
21
|
-
|
22
|
-
|
23
|
-
# Registers a one-shot callback for the ZOO_CONNECTED_STATE event.
|
24
|
-
#
|
25
|
-
# @note this is experimental currently. This may or may not fire for the *initial* connection.
|
26
|
-
# it's purpose is to warn an already-existing client with watches that a connection has been
|
27
|
-
# re-established (with session information saved). From the ZooKeeper Programmers' Guide:
|
28
|
-
#
|
29
|
-
# If you are using watches, you must look for the connected watch event.
|
30
|
-
# When a ZooKeeper client disconnects from a server, you will not receive
|
31
|
-
# notification of changes until reconnected. If you are watching for a
|
32
|
-
# znode to come into existance, you will miss the event if the znode is
|
33
|
-
# created and deleted while you are disconnected.
|
34
|
-
#
|
35
|
-
# once this deferred has been fired, it will be replaced with a new
|
36
|
-
# deferred, so callbacks must be re-registered, and *should* be
|
37
|
-
# re-registered *within* the callback to avoid missing events
|
38
|
-
#
|
39
|
-
# @method on_connected
|
40
|
-
# @return [Deferred::Default]
|
41
|
-
deferred_event :connected
|
42
|
-
|
43
|
-
# Registers a one-shot callback for the ZOO_CONNECTING_STATE event
|
44
|
-
#
|
45
|
-
# This event is triggered when we have become disconnected from the
|
46
|
-
# cluster and are in the process of reconnecting.
|
47
|
-
deferred_event :connecting
|
48
|
-
|
49
|
-
# called back once the connection has been closed.
|
50
|
-
#
|
51
|
-
# @method on_close
|
52
|
-
# @return [Deferred::Default]
|
53
|
-
deferred_event :close
|
9
|
+
# allows the synchrony client to muck with this
|
10
|
+
# @private
|
11
|
+
attr_writer :event_handler
|
54
12
|
|
55
13
|
# Takes same options as ZK::Client::Base
|
56
14
|
def initialize(host, opts={})
|
57
15
|
@host = host
|
58
|
-
@
|
59
|
-
@
|
60
|
-
|
16
|
+
@close_deferred = Deferred::Default.new
|
17
|
+
@connection_lost_deferred = Deferred::Default.new
|
18
|
+
@event_handler = EventHandlerEM.new(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_close(&blk)
|
22
|
+
@close_deferred.callback(&blk) if blk
|
23
|
+
@close_deferred
|
61
24
|
end
|
62
25
|
|
63
26
|
# open a ZK connection, attach it to the reactor.
|
@@ -65,21 +28,34 @@ module ZK
|
|
65
28
|
# ready for use
|
66
29
|
def connect(&blk)
|
67
30
|
# XXX: maybe move this into initialize, need to figure out how to schedule it properly
|
68
|
-
@cnx ||= (
|
69
|
-
ZookeeperEM::Client.new(@host, DEFAULT_TIMEOUT, event_handler.get_default_watcher_block)
|
70
|
-
)
|
31
|
+
@cnx ||= ZookeeperEM::Client.new(@host, DEFAULT_TIMEOUT, event_handler.get_default_watcher_block)
|
71
32
|
@cnx.on_attached(&blk)
|
72
33
|
end
|
73
34
|
|
74
35
|
# @private
|
36
|
+
# XXX: move this down into ZK::Client::Base
|
37
|
+
def connected?
|
38
|
+
@cnx and @cnx.connected?
|
39
|
+
end
|
40
|
+
|
41
|
+
# If we get a ZK::Exceptions::ConnectionLoss exeption back from any call,
|
42
|
+
# we will call back any handlers registered here with the exception
|
43
|
+
# instance as the argument
|
44
|
+
#
|
45
|
+
# once this deferred has been fired, it will be replaced with a new
|
46
|
+
# deferred, so callbacks must be re-registered
|
47
|
+
#
|
48
|
+
def on_connection_lost(&blk)
|
49
|
+
@connection_lost_deferred.callback(&blk) if blk
|
50
|
+
@connection_lost_deferred
|
51
|
+
end
|
52
|
+
|
75
53
|
def reopen(*a)
|
76
54
|
raise NotImplementedError, "reoopen is not implemented for the eventmachine version of the client"
|
77
55
|
end
|
78
56
|
|
79
57
|
def close!(&blk)
|
80
58
|
on_close(&blk)
|
81
|
-
return on_close if @closing
|
82
|
-
@closing = true
|
83
59
|
|
84
60
|
if @cnx
|
85
61
|
logger.debug { "#{self.class.name}: in close! clearing event_handler" }
|
@@ -101,7 +77,7 @@ module ZK
|
|
101
77
|
|
102
78
|
# get data at path, optionally enabling a watch on the node
|
103
79
|
#
|
104
|
-
# @
|
80
|
+
# @returns [Callback] returns a Callback which is an EM::Deferred (so you
|
105
81
|
# can assign callbacks/errbacks) see Callback::Base for discussion
|
106
82
|
#
|
107
83
|
def get(path, opts={}, &block)
|
@@ -168,47 +144,11 @@ module ZK
|
|
168
144
|
end
|
169
145
|
end
|
170
146
|
|
171
|
-
# @return [Fixnum] The underlying connection's session_id
|
172
|
-
def session_id
|
173
|
-
return nil unless @cnx
|
174
|
-
@cnx.session_id
|
175
|
-
end
|
176
|
-
|
177
|
-
# @return [String] The underlying connection's session passwd (an opaque value)
|
178
|
-
def session_passwd
|
179
|
-
return nil unless @cnx
|
180
|
-
@cnx.session_passwd
|
181
|
-
end
|
182
|
-
|
183
147
|
protected
|
184
|
-
# @private
|
185
|
-
def register_default_event_handlers!
|
186
|
-
@event_handler.register_state_handler(Zookeeper::ZOO_EXPIRED_SESSION_STATE, &method(:handle_expired_session_state_event!))
|
187
|
-
@event_handler.register_state_handler(Zookeeper::ZOO_CONNECTED_STATE, &method(:handle_connected_state_event!))
|
188
|
-
@event_handler.register_state_handler(Zookeeper::ZOO_CONNECTING_STATE, &method(:handle_connecting_state_event!))
|
189
|
-
end
|
190
|
-
|
191
|
-
# @private
|
192
|
-
def handle_connected_state_event!(event)
|
193
|
-
reset_connected_event.succeed(event)
|
194
|
-
end
|
195
|
-
|
196
|
-
# @private
|
197
|
-
def handle_connecting_state_event!(event)
|
198
|
-
reset_connecting_event.succeed(event)
|
199
|
-
end
|
200
|
-
|
201
|
-
# @private
|
202
|
-
def handle_expired_session_state_event!(event)
|
203
|
-
exc = ZK::Exceptions::ConnectionLoss.new("Received EXPIRED_SESSION_STATE event: #{event.inspect}")
|
204
|
-
exc.set_backtrace(caller)
|
205
|
-
connection_lost_hook(exc)
|
206
|
-
end
|
207
|
-
|
208
|
-
# @private
|
209
148
|
def connection_lost_hook(exc)
|
210
149
|
if exc and exc.kind_of?(ZK::Exceptions::ConnectionLoss)
|
211
|
-
|
150
|
+
dfr, @connection_lost_deferred = @connection_lost_deferred, Deferred::Default.new
|
151
|
+
dfr.succeed(exc)
|
212
152
|
end
|
213
153
|
end
|
214
154
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ZK
|
2
|
+
module ZKEventMachine
|
3
|
+
module Deferred
|
4
|
+
include EM::Deferrable
|
5
|
+
|
6
|
+
# slight modification to EM::Deferrable,
|
7
|
+
#
|
8
|
+
# @returns [self] to allow for chaining
|
9
|
+
#
|
10
|
+
def callback(&block)
|
11
|
+
super(&block)
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
# @see #callback
|
16
|
+
def errback(&block)
|
17
|
+
super(&block)
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
# adds the block to both the callback and errback chains
|
22
|
+
def ensure_that(&block)
|
23
|
+
callback(&block)
|
24
|
+
errback(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def chain_to(other_dfr, opts={})
|
28
|
+
other_dfr.callback { |*a| self.succeed(*a) }
|
29
|
+
other_dfr.errback { |*a| self.fail(*a) } unless opts[:ignore_errors]
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
class Default
|
34
|
+
include ZK::ZKEventMachine::Deferred
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module ZK
|
2
|
+
module ZKEventMachine
|
3
|
+
class SynchronyEventHandlerWrapper
|
4
|
+
include ZK::Logging
|
5
|
+
|
6
|
+
def initialize(event_handler)
|
7
|
+
@event_handler = event_handler
|
8
|
+
end
|
9
|
+
|
10
|
+
# registers a block to be called back within a fiber context
|
11
|
+
def register(path, &block)
|
12
|
+
new_block = proc do |*a|
|
13
|
+
Fiber.new { block.call(*a) }.resume
|
14
|
+
end
|
15
|
+
|
16
|
+
@event_handler.register(path, &new_block)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def method_missing(meth, *a, &b)
|
21
|
+
@event_handler.__send__(meth, *a, &b)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# This class is an EM::Synchrony wrapper around a ZK::ZKEventMachine::Client
|
26
|
+
#
|
27
|
+
# It should behave exactly like a ZK::Client instance (when called in a
|
28
|
+
# synchronous fashion), and one should look there for documentation about
|
29
|
+
# the various methods
|
30
|
+
#
|
31
|
+
# @note this class is implemented as a wrapper instead of a subclass of Client
|
32
|
+
# so that it can support the unixisms like rm_rf and mkdir_p. The
|
33
|
+
# synchrony pattern of aliasing the base class methods and specializing for
|
34
|
+
# synchrony didn't work in this case.
|
35
|
+
#
|
36
|
+
class SynchronyClient
|
37
|
+
include ZK::Logging
|
38
|
+
|
39
|
+
attr_reader :event_handler, :client
|
40
|
+
|
41
|
+
# @overload new(client_instance)
|
42
|
+
# Wrap an existing ZK::ZKEventMachine::Client instance in an
|
43
|
+
# EM::Synchrony compatible way
|
44
|
+
# @param [ZK::ZKEventMachine::Client] client_instance an instance of Client to wrap
|
45
|
+
# @overload new(host, opts={})
|
46
|
+
# Creates a new ZK::ZKEventMachine::Client instance to manage
|
47
|
+
# takes the same arguments as ZK::Client::Base
|
48
|
+
def initialize(host, opts={})
|
49
|
+
case host
|
50
|
+
when Client
|
51
|
+
@client = host
|
52
|
+
when String
|
53
|
+
@client = Client.new(host, opts)
|
54
|
+
else
|
55
|
+
raise ArgumentError, "argument must be either a ZK::ZKEventMachine::Client instance or a hostname:port string"
|
56
|
+
end
|
57
|
+
|
58
|
+
@event_handler = SynchronyEventHandlerWrapper.new(@client.event_handler)
|
59
|
+
end
|
60
|
+
|
61
|
+
%w[connect close close! get set create stat delete children get_acl set_acl mkdir_p rm_rf].each do |meth|
|
62
|
+
class_eval(<<-EOMETH, __FILE__, __LINE__ + 1)
|
63
|
+
def #{meth}(*args,&blk)
|
64
|
+
sync!(@client.#{meth}(*args, &blk))
|
65
|
+
end
|
66
|
+
EOMETH
|
67
|
+
end
|
68
|
+
|
69
|
+
# @deprecated for backwards compatibility only
|
70
|
+
def watcher
|
71
|
+
event_handler
|
72
|
+
end
|
73
|
+
|
74
|
+
def exists?(path, opts={})
|
75
|
+
stat(path, opts={}).exists?
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
# a modification of EM::Synchrony.sync to handle multiple callback arguments properly
|
80
|
+
def sync(df)
|
81
|
+
f = Fiber.current
|
82
|
+
|
83
|
+
xback = proc do |*args|
|
84
|
+
if f == Fiber.current
|
85
|
+
return *args
|
86
|
+
else
|
87
|
+
f.resume(*args)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
df.callback(&xback)
|
92
|
+
df.errback(&xback)
|
93
|
+
|
94
|
+
Fiber.yield
|
95
|
+
end
|
96
|
+
|
97
|
+
# like sync, but if the deferred returns an exception instance, re-raises
|
98
|
+
def sync!(deferred)
|
99
|
+
rval = sync(deferred)
|
100
|
+
raise rval if rval.kind_of?(Exception)
|
101
|
+
rval
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
def method_missing(meth, *a, &b)
|
106
|
+
@client.__send__(meth, *a, &b)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
@@ -31,18 +31,15 @@ module ZK
|
|
31
31
|
_handle_calling_convention(dfr, &blk)
|
32
32
|
end
|
33
33
|
|
34
|
-
# @private
|
35
34
|
def find(*paths, &block)
|
36
35
|
raise NotImplementedError, "Coming soon"
|
37
36
|
end
|
38
37
|
|
39
|
-
# @private
|
40
38
|
def block_until_node_deleted(abs_node_path)
|
41
39
|
raise NotImplementedError, "blocking does not make sense in EventMachine-land"
|
42
40
|
end
|
43
41
|
|
44
42
|
protected
|
45
|
-
# @private
|
46
43
|
def _handle_calling_convention(dfr, &blk)
|
47
44
|
return dfr unless blk
|
48
45
|
dfr.callback { |*a| blk.call(nil, *a) }
|
@@ -50,7 +47,6 @@ module ZK
|
|
50
47
|
dfr
|
51
48
|
end
|
52
49
|
|
53
|
-
# @private
|
54
50
|
def _rm_rf_dfr(path)
|
55
51
|
Deferred::Default.new.tap do |my_dfr|
|
56
52
|
delete(path) do |exc|
|
@@ -89,7 +85,6 @@ module ZK
|
|
89
85
|
end
|
90
86
|
end
|
91
87
|
|
92
|
-
# @private
|
93
88
|
def _mkdir_p_dfr(path)
|
94
89
|
Deferred::Default.new.tap do |my_dfr|
|
95
90
|
d = create(path, '')
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'eventmachine'
|
2
|
+
require 'em-synchrony'
|
2
3
|
|
3
4
|
require 'zookeeper'
|
4
5
|
require 'zookeeper/em_client'
|
5
6
|
|
6
7
|
require 'zk'
|
7
|
-
|
8
|
+
|
8
9
|
|
9
10
|
module ZK
|
10
11
|
module ZKEventMachine
|
@@ -15,9 +16,11 @@ end
|
|
15
16
|
$LOAD_PATH.unshift(File.expand_path('../..', __FILE__)).uniq!
|
16
17
|
|
17
18
|
require 'z_k/z_k_event_machine/iterator'
|
19
|
+
require 'z_k/z_k_event_machine/deferred'
|
18
20
|
require 'z_k/z_k_event_machine/callback'
|
19
21
|
require 'z_k/z_k_event_machine/event_handler_e_m'
|
20
22
|
require 'z_k/z_k_event_machine/unixisms'
|
21
23
|
require 'z_k/z_k_event_machine/client'
|
24
|
+
require 'z_k/z_k_event_machine/synchrony_client'
|
22
25
|
|
23
26
|
|
data/spec/spec_helper.rb
CHANGED
@@ -10,6 +10,8 @@ require 'evented-spec'
|
|
10
10
|
# in spec/support/ and its subdirectories.
|
11
11
|
Dir[File.expand_path("../support/**/*.rb", __FILE__)].each {|f| require f}
|
12
12
|
|
13
|
+
$stderr.sync = true
|
14
|
+
|
13
15
|
case `uname -s`.chomp
|
14
16
|
when 'Linux'
|
15
17
|
$stderr.puts "WARN: setting EM.epoll = true for tests"
|
@@ -19,25 +21,6 @@ when 'Darwin'
|
|
19
21
|
EM.kqueue = true
|
20
22
|
end
|
21
23
|
|
22
|
-
# method to wait until block passed returns true or timeout (default is 2 seconds) is reached
|
23
|
-
def wait_until(timeout=2)
|
24
|
-
time_to_stop = Time.now + timeout
|
25
|
-
|
26
|
-
until yield
|
27
|
-
break if Time.now > time_to_stop
|
28
|
-
Thread.pass
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def wait_while(timeout=2)
|
33
|
-
time_to_stop = Time.now + timeout
|
34
|
-
|
35
|
-
while yield
|
36
|
-
break if Time.now > time_to_stop
|
37
|
-
Thread.pass
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
24
|
RSpec.configure do |config|
|
42
25
|
config.mock_with :flexmock
|
43
26
|
end
|
data/spec/support/logging.rb
CHANGED
@@ -1,19 +1,14 @@
|
|
1
1
|
require 'logger'
|
2
2
|
|
3
|
-
|
3
|
+
logger = Logger.new(File.expand_path('../../../test.log', __FILE__)).tap {|l| l.level = Logger::DEBUG}
|
4
4
|
|
5
|
-
Logger.new(log_file).tap do |log|
|
6
|
-
log.level = Logger::DEBUG
|
7
|
-
ZK.logger = log
|
8
|
-
Zookeeper.logger = log
|
9
|
-
end
|
10
|
-
|
11
|
-
# for debugging along with C output uncomment the following
|
12
|
-
#
|
13
|
-
# $stderr.sync = true
|
14
5
|
# logger = Logger.new($stderr).tap { |l| l.level = Logger::DEBUG }
|
15
6
|
# Zookeeper.set_debug_level(4)
|
16
7
|
|
8
|
+
ZK.logger = logger
|
9
|
+
Zookeeper.logger = ZK.logger
|
10
|
+
|
11
|
+
|
17
12
|
def logger
|
18
13
|
ZK.logger
|
19
14
|
end
|