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 CHANGED
@@ -7,4 +7,3 @@ pkg/*
7
7
  .rspec
8
8
  *.log
9
9
  .yardoc
10
- doc
data/Gemfile CHANGED
@@ -1,7 +1,9 @@
1
- source ENV['MBOX_BUNDLER_SOURCE'] if ENV['MBOX_BUNDLER_SOURCE']
1
+ source 'http://localhost:50000'
2
2
  source "http://rubygems.org"
3
3
 
4
+
4
5
  # Specify your gem's dependencies in zk-em.gemspec
5
6
  gemspec
6
7
 
8
+
7
9
  # vim:ft=ruby
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
-
@@ -70,7 +70,7 @@ module ZK
70
70
  #
71
71
  # @param [Hash] hash the result of the async call
72
72
  #
73
- # @return [true, false] for success, failure
73
+ # @returns [true, false] for success, failure
74
74
  def success?(hash)
75
75
  hash[:rc] == Zookeeper::ZOK
76
76
  end
@@ -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
- # If we get a ZK::Exceptions::ConnectionLoss exeption back from any call,
11
- # or a EXPIRED_SESSION_STATE event, we will call back any handlers registered
12
- # here with the exception instance as the argument.
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
- @event_handler = EventHandlerEM.new(self)
59
- @closing = false
60
- register_default_event_handlers!
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
- # @return [Callback] returns a Callback which is an EM::Deferred (so you
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
- reset_connection_lost_event.succeed(exc)
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,5 +1,5 @@
1
1
  module ZK
2
2
  module ZKEventMachine
3
- VERSION = "0.1.13"
3
+ VERSION = "0.2.0.beta.1"
4
4
  end
5
5
  end
@@ -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
- require 'deferred'
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
@@ -1,19 +1,14 @@
1
1
  require 'logger'
2
2
 
3
- log_file = File.open(File.expand_path('../../../test.log', __FILE__), 'a').tap { |f| f.sync = true }
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