zk 0.8.9 → 0.9.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/.dotfiles/rvmrc +1 -1
- data/.yardopts +1 -0
- data/Gemfile +11 -1
- data/README.markdown +19 -10
- data/RELEASES.markdown +14 -0
- data/lib/z_k/client/base.rb +171 -46
- data/lib/z_k/client/continuation_proxy.rb +99 -0
- data/lib/z_k/client/conveniences.rb +10 -13
- data/lib/z_k/client/drop_box.rb +98 -0
- data/lib/z_k/client/multiplexed.rb +28 -0
- data/lib/z_k/client/threaded.rb +43 -13
- data/lib/z_k/client/unixisms.rb +74 -14
- data/lib/z_k/client.rb +3 -0
- data/lib/z_k/event_handler.rb +5 -45
- data/lib/z_k/event_handler_subscription.rb +13 -6
- data/lib/z_k/exceptions.rb +22 -2
- data/lib/z_k/extensions.rb +10 -2
- data/lib/z_k/find.rb +5 -2
- data/lib/z_k/locker.rb +9 -4
- data/lib/z_k/pool.rb +24 -6
- data/lib/z_k/version.rb +1 -1
- data/lib/z_k.rb +1 -2
- data/spec/shared/client_contexts.rb +15 -0
- data/spec/shared/client_examples.rb +155 -0
- data/spec/spec_helper.rb +9 -53
- data/spec/support/logging.rb +27 -0
- data/spec/support/special_happy_funtime_error.rb +6 -0
- data/spec/support/wait_watchers.rb +48 -0
- data/spec/watch_spec.rb +19 -4
- data/spec/z_k/client/drop_box_spec.rb +90 -0
- data/spec/z_k/client/locking_and_session_death_spec.rb +108 -0
- data/spec/z_k/client/multiplexed_spec.rb +20 -0
- data/spec/z_k/client_spec.rb +3 -231
- data/spec/z_k/election_spec.rb +12 -11
- data/spec/z_k/locker_spec.rb +69 -3
- data/spec/zookeeper_spec.rb +3 -3
- data/zk.gemspec +1 -2
- metadata +28 -8
@@ -1,5 +1,8 @@
|
|
1
1
|
module ZK
|
2
2
|
module Client
|
3
|
+
# EXTENSIONS
|
4
|
+
#
|
5
|
+
# convenience methods for dealing with zookeeper (rm -rf, mkdir -p, etc)
|
3
6
|
module Conveniences
|
4
7
|
# Queue an operation to be run on an internal threadpool. You may either
|
5
8
|
# provide an object that responds_to?(:call) or pass a block. There is no
|
@@ -9,10 +12,13 @@ module ZK
|
|
9
12
|
#
|
10
13
|
# An ArgumentError will be raised if +callable+ does not <tt>respond_to?(:call)</tt>
|
11
14
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
+
# @param [#call] callable an object that `respond_to?(:call)`, takes
|
16
|
+
# precedence over a given block
|
17
|
+
#
|
18
|
+
# @yield [] the block that should be run in the threadpool, if `callable`
|
19
|
+
# isn't given
|
15
20
|
#
|
21
|
+
# @private
|
16
22
|
def defer(callable=nil, &block)
|
17
23
|
@threadpool.defer(callable, &block)
|
18
24
|
end
|
@@ -28,18 +34,9 @@ module ZK
|
|
28
34
|
false
|
29
35
|
end
|
30
36
|
|
31
|
-
|
32
|
-
#--
|
33
|
-
#
|
34
|
-
# EXTENSIONS
|
35
|
-
#
|
36
|
-
# convenience methods for dealing with zookeeper (rm -rf, mkdir -p, etc)
|
37
|
-
#
|
38
|
-
#++
|
39
|
-
|
40
37
|
# creates a new locker based on the name you send in
|
41
38
|
#
|
42
|
-
# see ZK::Locker::ExclusiveLocker
|
39
|
+
# @see ZK::Locker::ExclusiveLocker
|
43
40
|
#
|
44
41
|
# returns a ZK::Locker::ExclusiveLocker instance using this Client and provided
|
45
42
|
# lock name
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module ZK
|
2
|
+
module Client
|
3
|
+
# A simple threadsafe way of having a thread deliver a single value
|
4
|
+
# to another thread.
|
5
|
+
#
|
6
|
+
# Each thread making requests will have a thread-local continuation
|
7
|
+
# that can be accessed via DropBox.current and one can use
|
8
|
+
# DropBox.with_current that will clear the result once the given block
|
9
|
+
# exits (allowing for reuse)
|
10
|
+
#
|
11
|
+
# (this class is in no way related to dropbox.com or Dropbox Inc.)
|
12
|
+
# @private
|
13
|
+
class DropBox
|
14
|
+
UNDEFINED = Object.new unless defined?(UNDEFINED)
|
15
|
+
KILL_TOKEN = Object.new unless defined?(KILL_TOKEN)
|
16
|
+
IMPOSSIBLE_TO_CONTINUE = Object.new unless defined?(IMPOSSIBLE_TO_CONTINUE)
|
17
|
+
|
18
|
+
# represents an exception to raise. if the pop method sees the value as an instance
|
19
|
+
# of this class, it will call the raise! method
|
20
|
+
class ExceptionValue
|
21
|
+
def initialize(exception_class, message)
|
22
|
+
@exception_class, @message = exception_class, message
|
23
|
+
end
|
24
|
+
|
25
|
+
def raise!
|
26
|
+
raise @exception_class, @message
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
THREAD_LOCAL_KEY = :__zk_client_continuation_current__ unless defined?(THREAD_LOCAL_KEY)
|
31
|
+
|
32
|
+
# @private
|
33
|
+
attr_reader :value
|
34
|
+
|
35
|
+
# sets the thread-local instance to nil, used by tests
|
36
|
+
# @private
|
37
|
+
def self.remove_current
|
38
|
+
Thread.current[THREAD_LOCAL_KEY] = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# access the thread-local DropBox instance for the current thread
|
42
|
+
def self.current
|
43
|
+
Thread.current[THREAD_LOCAL_KEY] ||= self.new()
|
44
|
+
end
|
45
|
+
|
46
|
+
# yields the current thread's DropBox instance and clears its value
|
47
|
+
# after the block returns
|
48
|
+
def self.with_current
|
49
|
+
yield current
|
50
|
+
ensure
|
51
|
+
current.clear
|
52
|
+
end
|
53
|
+
|
54
|
+
def initialize
|
55
|
+
@mutex = Mutex.new
|
56
|
+
@cond = ConditionVariable.new
|
57
|
+
@value = UNDEFINED # allows us to return nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def push(obj)
|
61
|
+
@mutex.synchronize do
|
62
|
+
@value = obj
|
63
|
+
@cond.signal
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def pop
|
68
|
+
@mutex.synchronize do
|
69
|
+
@cond.wait(@mutex)
|
70
|
+
@value.kind_of?(ExceptionValue) ? @value.raise! : @value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def clear
|
75
|
+
@mutex.synchronize do
|
76
|
+
@value = UNDEFINED
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# we are done if value is defined, use clear to reset
|
81
|
+
def done?
|
82
|
+
@value != UNDEFINED
|
83
|
+
end
|
84
|
+
|
85
|
+
# called when you need the waiting thread to receive an exception
|
86
|
+
# returns nil if a value has already been set
|
87
|
+
def oh_noes!(exception_class, message)
|
88
|
+
@mutex.synchronize do
|
89
|
+
return if done?
|
90
|
+
@value = ExceptionValue.new(exception_class, message)
|
91
|
+
@cond.signal
|
92
|
+
end
|
93
|
+
true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ZK
|
2
|
+
module Client
|
3
|
+
# This client is an experimental implementation of a threaded and
|
4
|
+
# multiplexed client. The idea is that each synchronous request represents
|
5
|
+
# a continuation. This way, you can have multiple requests pending with the
|
6
|
+
# server simultaneously, and the responses will be delivered on the event
|
7
|
+
# thread (but run in the calling thread). This allows for higher throughput
|
8
|
+
# for multi-threaded applications.
|
9
|
+
#
|
10
|
+
# Asynchronous requests are not supported through this client.
|
11
|
+
#
|
12
|
+
class Multiplexed < Threaded
|
13
|
+
def close!
|
14
|
+
@cnx.connection_closed!
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
def create_connection(*args)
|
20
|
+
ContinuationProxy.new.tap do |cp|
|
21
|
+
on_expired_session { cp.expired_session! } # hook up client's session expired event listener
|
22
|
+
cp.zookeeper_cnx = super(*args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
data/lib/z_k/client/threaded.rb
CHANGED
@@ -2,35 +2,65 @@ module ZK
|
|
2
2
|
module Client
|
3
3
|
# This is the default client that ZK will use. In the zk-eventmachine gem,
|
4
4
|
# there is an Evented client.
|
5
|
+
#
|
5
6
|
class Threaded < Base
|
6
7
|
include StateMixin
|
7
8
|
include Unixisms
|
8
9
|
include Conveniences
|
9
10
|
|
10
|
-
|
11
|
+
DEFAULT_THREADPOOL_SIZE = 1
|
12
|
+
|
13
|
+
# @note The `:timeout` argument here is *not* the session_timeout for the
|
14
|
+
# connection. rather it is the amount of time we wait for the connection
|
15
|
+
# to be established. The session timeout exchanged with the server is
|
16
|
+
# set to 10s by default in the C implemenation, and as of version 0.8.0
|
17
|
+
# of slyphon-zookeeper has yet to be exposed as an option. That feature
|
18
|
+
# is planned.
|
19
|
+
#
|
20
|
+
# @param [String] host (see ZK::Client::Base#initialize)
|
21
|
+
#
|
22
|
+
# @option opts [Fixnum] :threadpool_size the size of the threadpool that
|
23
|
+
# should be used to deliver events. In ZK 0.8.x this was set to 5, which
|
24
|
+
# means that events could be delivered concurrently. As of 0.9, this will
|
25
|
+
# be set to 1, so it's very important to _not block the event thread_.
|
11
26
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# prefix to all paths supplied.
|
27
|
+
# @option opts [Fixnum] :timeout how long we will wait for the connection
|
28
|
+
# to be established.
|
15
29
|
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
30
|
+
# @yield [self] calls the block with the new instance after the event
|
31
|
+
# handler has been set up, but before any connections have been made.
|
32
|
+
# This allows the client to register watchers for session events like
|
33
|
+
# `connected`. You *cannot* perform any other operations with the client
|
34
|
+
# as you will get a NoMethodError (the underlying connection is nil).
|
19
35
|
#
|
20
|
-
def initialize(host, opts={})
|
21
|
-
|
36
|
+
def initialize(host, opts={}, &b)
|
37
|
+
super(host, opts)
|
38
|
+
|
39
|
+
@session_timeout = opts.fetch(:timeout, DEFAULT_TIMEOUT) # maybe move this into superclass?
|
40
|
+
@event_handler = EventHandler.new(self)
|
41
|
+
|
22
42
|
yield self if block_given?
|
23
|
-
|
24
|
-
@
|
43
|
+
|
44
|
+
@cnx = create_connection(host, @session_timeout, @event_handler.get_default_watcher_block)
|
45
|
+
|
46
|
+
tp_size = opts.fetch(:threadpool_size, DEFAULT_THREADPOOL_SIZE)
|
47
|
+
|
48
|
+
@threadpool = Threadpool.new(tp_size)
|
25
49
|
end
|
26
50
|
|
27
|
-
#
|
51
|
+
# @see ZK::Client::Base#close!
|
28
52
|
def close!
|
29
53
|
@threadpool.shutdown
|
30
54
|
super
|
31
55
|
nil
|
32
56
|
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
# allows for the Mutliplexed client to wrap the connection in its ContinuationProxy
|
60
|
+
# @private
|
61
|
+
def create_connection(*args)
|
62
|
+
::Zookeeper.new(*args)
|
63
|
+
end
|
33
64
|
end
|
34
65
|
end
|
35
66
|
end
|
36
|
-
|
data/lib/z_k/client/unixisms.rb
CHANGED
@@ -5,11 +5,10 @@ module ZK
|
|
5
5
|
|
6
6
|
# Creates all parent paths and 'path' in zookeeper as persistent nodes with
|
7
7
|
# zero data.
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# ==== Examples
|
8
|
+
#
|
9
|
+
# @param [String] path An absolute znode path to create
|
10
|
+
#
|
11
|
+
# @example
|
13
12
|
#
|
14
13
|
# zk.exists?('/path')
|
15
14
|
# # => false
|
@@ -17,10 +16,10 @@ module ZK
|
|
17
16
|
# zk.mkdir_p('/path/to/blah')
|
18
17
|
# # => "/path/to/blah"
|
19
18
|
#
|
20
|
-
#--
|
21
|
-
# TODO: write a non-recursive version of this. ruby doesn't have TCO, so
|
22
|
-
# this could get expensive w/ psychotically long paths
|
23
19
|
def mkdir_p(path)
|
20
|
+
# TODO: write a non-recursive version of this. ruby doesn't have TCO, so
|
21
|
+
# this could get expensive w/ psychotically long paths
|
22
|
+
|
24
23
|
create(path, '', :mode => :persistent)
|
25
24
|
rescue Exceptions::NodeExists
|
26
25
|
return
|
@@ -49,23 +48,84 @@ module ZK
|
|
49
48
|
end
|
50
49
|
end
|
51
50
|
|
52
|
-
#
|
51
|
+
# Acts in a similar way to ruby's Find class. Performs a depth-first
|
52
|
+
# traversal of every node under the given paths, and calls the given
|
53
|
+
# block with each path found. Like the ruby Find class, you can call
|
54
|
+
# {ZK::Find.prune} to avoid descending further into a given sub-tree
|
55
|
+
#
|
56
|
+
# @example list the paths under a given node
|
57
|
+
#
|
58
|
+
# zk = ZK.new
|
59
|
+
#
|
60
|
+
# paths = %w[
|
61
|
+
# /root
|
62
|
+
# /root/alpha
|
63
|
+
# /root/bravo
|
64
|
+
# /root/charlie
|
65
|
+
# /root/charlie/rose
|
66
|
+
# /root/charlie/manson
|
67
|
+
# /root/charlie/manson/family
|
68
|
+
# /root/charlie/manson/murders
|
69
|
+
# /root/charlie/brown
|
70
|
+
# /root/delta
|
71
|
+
# /root/delta/blues
|
72
|
+
# /root/delta/force
|
73
|
+
# /root/delta/burke
|
74
|
+
# ]
|
75
|
+
#
|
76
|
+
# paths.each { |p| zk.create(p) }
|
77
|
+
#
|
78
|
+
# zk.find('/root') do |path|
|
79
|
+
# puts path
|
80
|
+
#
|
81
|
+
# ZK::Find.prune if path == '/root/charlie/manson'
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# # this produces the output:
|
85
|
+
#
|
86
|
+
# # /root
|
87
|
+
# # /root/alpha
|
88
|
+
# # /root/bravo
|
89
|
+
# # /root/charlie
|
90
|
+
# # /root/charlie/brown
|
91
|
+
# # /root/charlie/manson
|
92
|
+
# # /root/charlie/rose
|
93
|
+
# # /root/delta
|
94
|
+
# # /root/delta/blues
|
95
|
+
# # /root/delta/burke
|
96
|
+
# # /root/delta/force
|
97
|
+
#
|
98
|
+
# @param [Array[String]] paths a list of paths to recursively
|
99
|
+
# yield the sub-paths of
|
100
|
+
#
|
101
|
+
# @see ZK::Find#find
|
53
102
|
def find(*paths, &block)
|
54
103
|
ZK::Find.find(self, *paths, &block)
|
55
104
|
end
|
56
105
|
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
106
|
+
# Will _safely_ block the caller until `abs_node_path` has been removed.
|
107
|
+
# This is trickier than it first appears. This method will wake the caller
|
108
|
+
# if a session event occurs that would ensure the event would never be
|
109
|
+
# delivered, and also checks to make sure that the caller is not calling
|
110
|
+
# from the event distribution thread (which would cause a deadlock).
|
61
111
|
#
|
62
112
|
# @note this is dangerous to use in callbacks! there is only one
|
63
113
|
# event-delivery thread, so if you use this method in a callback or
|
64
114
|
# watcher, you *will* deadlock!
|
115
|
+
#
|
116
|
+
# @raise [Exceptions::InterruptedSession] If a session event occurs while we're
|
117
|
+
# blocked waiting for the node to be deleted, an exception that
|
118
|
+
# mixes in the InterruptedSession module will be raised.
|
119
|
+
#
|
65
120
|
def block_until_node_deleted(abs_node_path)
|
66
|
-
queue = Queue.new
|
67
121
|
subs = []
|
68
122
|
|
123
|
+
assert_we_are_not_on_the_event_dispatch_thread!
|
124
|
+
|
125
|
+
raise ArgumentError, "argument must be String-ish, not: #{abs_node_path.inspect}" unless abs_node_path
|
126
|
+
|
127
|
+
queue = Queue.new
|
128
|
+
|
69
129
|
node_deletion_cb = lambda do |event|
|
70
130
|
if event.node_deleted?
|
71
131
|
queue.enq(:deleted)
|
data/lib/z_k/client.rb
CHANGED
@@ -24,9 +24,12 @@ module ZK
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
require 'z_k/client/drop_box'
|
27
28
|
require 'z_k/client/state_mixin'
|
28
29
|
require 'z_k/client/unixisms'
|
29
30
|
require 'z_k/client/conveniences'
|
30
31
|
require 'z_k/client/base'
|
31
32
|
require 'z_k/client/threaded'
|
33
|
+
require 'z_k/client/continuation_proxy'
|
34
|
+
require 'z_k/client/multiplexed'
|
32
35
|
|
data/lib/z_k/event_handler.rb
CHANGED
@@ -32,51 +32,7 @@ module ZK
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
# register
|
36
|
-
#
|
37
|
-
# your block will be called with all events on that path.
|
38
|
-
#
|
39
|
-
# @note All watchers are one-shot handlers. After an event is delivered to
|
40
|
-
# your handler, you *must* re-watch the node to receive more events. This
|
41
|
-
# leads to a pattern you will find throughout ZK code that avoids races,
|
42
|
-
# see the example below "avoiding a race"
|
43
|
-
#
|
44
|
-
# @example avoiding a race waiting for a node to be deleted
|
45
|
-
#
|
46
|
-
# # we expect that '/path/to/node' exists currently and want to be notified
|
47
|
-
# # when it's deleted
|
48
|
-
#
|
49
|
-
# # register a handler that will be called back when an event occurs on
|
50
|
-
# # node
|
51
|
-
# #
|
52
|
-
# node_subscription = zk.event_handler.register('/path/to/node') do |event|
|
53
|
-
# if event.node_deleted?
|
54
|
-
# do_something_when_node_deleted
|
55
|
-
# end
|
56
|
-
# end
|
57
|
-
#
|
58
|
-
# # check to see if our condition is true *while* setting a watch on the node
|
59
|
-
# # if our condition happens to be true while setting the watch
|
60
|
-
# #
|
61
|
-
# unless exists?('/path/to/node', :watch => true)
|
62
|
-
# node_subscription.unsubscribe # cancel the watch
|
63
|
-
# do_something_when_node_deleted # call the callback
|
64
|
-
# end
|
65
|
-
#
|
66
|
-
#
|
67
|
-
# @param [String] path the path you want to listen to
|
68
|
-
#
|
69
|
-
# @param [Block] block the block to execute when a watch event happpens
|
70
|
-
#
|
71
|
-
# @yield [event] We will call your block with the watch event object (which
|
72
|
-
# has the connection the event occurred on as its #zk attribute)
|
73
|
-
#
|
74
|
-
# @return [ZooKeeper::EventHandlerSubscription] the subscription object
|
75
|
-
# you can use to to unsubscribe from an event
|
76
|
-
#
|
77
|
-
# @see ZooKeeper::WatcherEvent
|
78
|
-
# @see ZK::EventHandlerSubscription
|
79
|
-
#
|
35
|
+
# @see ZK::Client::Base#register
|
80
36
|
def register(path, &block)
|
81
37
|
# logger.debug { "EventHandler#register path=#{path.inspect}" }
|
82
38
|
EventHandlerSubscription.new(self, path, block).tap do |subscription|
|
@@ -136,6 +92,10 @@ module ZK
|
|
136
92
|
alias :unsubscribe :unregister
|
137
93
|
|
138
94
|
# called from the client-registered callback when an event fires
|
95
|
+
#
|
96
|
+
# @note this is *ONLY* dealing with asynchronous callbacks! watchers
|
97
|
+
# and session events go through here, NOT anything else!!
|
98
|
+
#
|
139
99
|
# @private
|
140
100
|
def process(event)
|
141
101
|
# logger.debug { "EventHandler#process dispatching event: #{event.inspect}" }# unless event.type == -1
|
@@ -1,29 +1,36 @@
|
|
1
1
|
module ZK
|
2
2
|
# the subscription object that is passed back from subscribing
|
3
3
|
# to events.
|
4
|
-
# @see
|
4
|
+
# @see ZK::Client::Base#register
|
5
5
|
class EventHandlerSubscription
|
6
|
-
|
6
|
+
# the event handler associated with this subscription
|
7
|
+
# @return [EventHandler]
|
8
|
+
attr_accessor :event_handler
|
9
|
+
|
10
|
+
# the path this subscription is for
|
11
|
+
# @return [String]
|
12
|
+
attr_accessor :path
|
13
|
+
|
14
|
+
# the block associated with the path
|
15
|
+
# @return [Proc]
|
16
|
+
attr_accessor :callback
|
7
17
|
|
8
18
|
# @private
|
9
|
-
# :nodoc:
|
10
19
|
def initialize(event_handler, path, callback)
|
11
20
|
@event_handler, @path, @callback = event_handler, path, callback
|
12
21
|
end
|
13
22
|
|
14
23
|
# unsubscribe from the path or state you were watching
|
15
|
-
# @see
|
24
|
+
# @see ZK::Client::Base#register
|
16
25
|
def unsubscribe
|
17
26
|
@event_handler.unregister(self)
|
18
27
|
end
|
19
28
|
alias :unregister :unsubscribe
|
20
29
|
|
21
30
|
# @private
|
22
|
-
# :nodoc:
|
23
31
|
def call(event)
|
24
32
|
callback.call(event)
|
25
33
|
end
|
26
|
-
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
data/lib/z_k/exceptions.rb
CHANGED
@@ -35,10 +35,15 @@ module ZK
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
+
# This module is mixed into the session-related exceptions to allow
|
39
|
+
# one to rescue that group of exceptions. It is also mixed into the related
|
40
|
+
# ZookeeperException objects
|
41
|
+
module InterruptedSession
|
42
|
+
end
|
43
|
+
|
38
44
|
class SystemError < KeeperException; end
|
39
45
|
class RunTimeInconsistency < KeeperException; end
|
40
46
|
class DataInconsistency < KeeperException; end
|
41
|
-
class ConnectionLoss < KeeperException; end
|
42
47
|
class MarshallingError < KeeperException; end
|
43
48
|
class Unimplemented < KeeperException; end
|
44
49
|
class OperationTimeOut < KeeperException; end
|
@@ -50,11 +55,23 @@ module ZK
|
|
50
55
|
class NoChildrenForEphemerals < KeeperException; end
|
51
56
|
class NodeExists < KeeperException; end
|
52
57
|
class NotEmpty < KeeperException; end
|
53
|
-
class SessionExpired < KeeperException; end
|
54
58
|
class InvalidCallback < KeeperException; end
|
55
59
|
class InvalidACL < KeeperException; end
|
56
60
|
class AuthFailed < KeeperException; end
|
57
61
|
|
62
|
+
class ConnectionLoss < KeeperException
|
63
|
+
include InterruptedSession
|
64
|
+
end
|
65
|
+
|
66
|
+
class SessionExpired < KeeperException
|
67
|
+
include InterruptedSession
|
68
|
+
end
|
69
|
+
|
70
|
+
# mixes in InterruptedSession, and can be raised on its own
|
71
|
+
class InterruptedSessionException < KeeperException
|
72
|
+
include InterruptedSession
|
73
|
+
end
|
74
|
+
|
58
75
|
ERROR_MAP = {
|
59
76
|
SYSTEMERROR => SystemError,
|
60
77
|
RUNTIMEINCONSISTENCY => RunTimeInconsistency,
|
@@ -100,6 +117,9 @@ module ZK
|
|
100
117
|
# raised for certain operations when using a chrooted connection, but the
|
101
118
|
# root doesn't exist.
|
102
119
|
class NonExistentRootError < ZKError; end
|
120
|
+
|
121
|
+
# raised when someone performs a blocking ZK operation on the event dispatch thread.
|
122
|
+
class EventDispatchThreadException < ZKError; end
|
103
123
|
end
|
104
124
|
end
|
105
125
|
|
data/lib/z_k/extensions.rb
CHANGED
@@ -17,7 +17,7 @@ module ZK
|
|
17
17
|
#
|
18
18
|
# *args, if given, will be passed on *after* the callback
|
19
19
|
#
|
20
|
-
# example
|
20
|
+
# @example
|
21
21
|
#
|
22
22
|
# WatcherCallback.create do |cb|
|
23
23
|
# puts "watcher callback called with argument: #{cb.inspect}"
|
@@ -116,7 +116,6 @@ module ZK
|
|
116
116
|
MEMBERS.all? { |m| self.__send__(m) == other.__send__(m) }
|
117
117
|
end
|
118
118
|
end
|
119
|
-
|
120
119
|
end # Extensions
|
121
120
|
end # ZK
|
122
121
|
|
@@ -125,6 +124,15 @@ ZookeeperCallbacks::Callback.send(:include, ZK::Extensions::Callbacks::Callback)
|
|
125
124
|
ZookeeperCallbacks::WatcherCallback.send(:include, ZK::Extensions::Callbacks::WatcherCallbackExt)
|
126
125
|
ZookeeperStat::Stat.send(:include, ZK::Extensions::Stat)
|
127
126
|
|
127
|
+
# Include the InterruptedSession module in key ZookeeperExceptions to allow
|
128
|
+
# clients to catch a single error type when waiting on a node (for example)
|
129
|
+
|
130
|
+
[:ConnectionClosed, :NotConnected, :SessionExpired, :SessionMoved, :ConnectionLoss].each do |class_name|
|
131
|
+
ZookeeperExceptions::ZookeeperException.const_get(class_name).tap do |klass|
|
132
|
+
klass.__send__(:include, ZK::Exceptions::InterruptedSession)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
128
136
|
class ::Exception
|
129
137
|
unless method_defined?(:to_std_format)
|
130
138
|
def to_std_format
|
data/lib/z_k/find.rb
CHANGED
@@ -3,7 +3,10 @@ module ZK
|
|
3
3
|
# like ruby's Find module, will call the given block with each _absolute_ znode path
|
4
4
|
# under +paths+. you can call ZK::Find.prune if you want to not recurse
|
5
5
|
# deeper under the current directory path.
|
6
|
-
|
6
|
+
#
|
7
|
+
# @yield [String] each znode path under the list of paths given.
|
8
|
+
#
|
9
|
+
def find(zk, *paths)
|
7
10
|
paths.collect!{|d| d.dup}
|
8
11
|
|
9
12
|
while p = paths.shift
|
@@ -11,7 +14,7 @@ module ZK
|
|
11
14
|
yield p.dup.taint
|
12
15
|
next unless zk.exists?(p)
|
13
16
|
|
14
|
-
zk.children(p).each do |ch|
|
17
|
+
zk.children(p).sort.reverse.each do |ch|
|
15
18
|
paths.unshift ZK.join(p, ch).untaint
|
16
19
|
end
|
17
20
|
end
|
data/lib/z_k/locker.rb
CHANGED
@@ -28,16 +28,21 @@ module ZK
|
|
28
28
|
class LockerBase
|
29
29
|
include ZK::Logging
|
30
30
|
|
31
|
-
|
31
|
+
# @private
|
32
|
+
attr_accessor :zk
|
32
33
|
|
33
34
|
# our absolute lock node path
|
34
35
|
#
|
35
36
|
# ex. '/_zklocking/foobar/__blah/lock000000007'
|
36
|
-
|
37
|
+
#
|
38
|
+
# @private
|
39
|
+
attr_reader :lock_path
|
37
40
|
|
38
|
-
|
41
|
+
# @private
|
42
|
+
attr_reader :root_lock_path
|
39
43
|
|
40
|
-
|
44
|
+
# @private
|
45
|
+
def self.digit_from_lock_path(path)
|
41
46
|
path[/0*(\d+)$/, 1].to_i
|
42
47
|
end
|
43
48
|
|