zk 0.8.9 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|