zk 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +5 -0
- data/Gemfile +2 -3
- data/README.markdown +7 -7
- data/RELEASES.markdown +18 -0
- data/Rakefile +6 -2
- data/lib/zk/client/conveniences.rb +47 -37
- data/lib/zk/client/threaded.rb +15 -4
- data/lib/zk/client/unixisms.rb +23 -14
- data/lib/zk/client.rb +1 -0
- data/lib/zk/event_handler.rb +1 -1
- data/lib/zk/exceptions.rb +24 -21
- data/lib/zk/locker/exclusive_locker.rb +75 -0
- data/lib/zk/locker/locker_base.rb +161 -0
- data/lib/zk/locker/shared_locker.rb +101 -0
- data/lib/zk/locker.rb +109 -240
- data/lib/zk/message_queue.rb +16 -6
- data/lib/zk/pool.rb +5 -4
- data/lib/zk/version.rb +1 -1
- data/spec/zk/locker_spec.rb +46 -102
- data/spec/zk/pool_spec.rb +1 -1
- metadata +7 -4
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -6,7 +6,7 @@ gem 'rake', :group => [:development, :test]
|
|
6
6
|
gem 'pry', :group => [:development]
|
7
7
|
|
8
8
|
group :docs do
|
9
|
-
gem 'yard', '~> 0.
|
9
|
+
gem 'yard', '~> 0.8.0'
|
10
10
|
|
11
11
|
platform :mri_19 do
|
12
12
|
gem 'redcarpet'
|
@@ -16,8 +16,7 @@ end
|
|
16
16
|
group :test do
|
17
17
|
gem 'rspec', '~> 2.8.0'
|
18
18
|
gem 'flexmock', '~> 0.8.10'
|
19
|
-
|
20
|
-
gem 'zk-server', '~> 0.9.1'
|
19
|
+
gem 'zk-server', '~> 1.0.1'
|
21
20
|
end
|
22
21
|
|
23
22
|
# Specify your gem's dependencies in zk.gemspec
|
data/README.markdown
CHANGED
@@ -18,6 +18,13 @@ Development is sponsored by [Snapfish][] and has been generously released to the
|
|
18
18
|
[MIT]: http://www.gnu.org/licenses/license-list.html#Expat "MIT (Expat) License"
|
19
19
|
[Snapfish]: http://www.snapfish.com/ "Snapfish"
|
20
20
|
|
21
|
+
## Contacting the author
|
22
|
+
|
23
|
+
* I'm usually hanging out in IRC on freenode.net in the BRAND NEW #zk-gem channel.
|
24
|
+
* if you really want to, you can also reach me via twitter [@slyphon][]
|
25
|
+
|
26
|
+
[@slyphon]: https://twitter.com/#!/slyphon
|
27
|
+
|
21
28
|
## New in 1.1 !! ##
|
22
29
|
|
23
30
|
* NEW! Thread-per-Callback event delivery model! [Read all about it!](https://github.com/slyphon/zk/wiki/EventDeliveryModel). Provides a simple, sane way to increase the concurrency in your ZK-based app while maintaining the ordering guarantees ZooKeeper makes. Each callback can perform whatever work it needs to without blocking other callbacks from receiving events. Inspired by [Celluloid's](https://github.com/celluloid/celluloid) actor model.
|
@@ -161,11 +168,4 @@ ZK strives to be a complete, correct, and convenient way of interacting with Zoo
|
|
161
168
|
[szk-jar-gem]: https://rubygems.org/gems/slyphon-zookeeper_jar
|
162
169
|
[szk-jar-repo]: https://github.com/slyphon/zookeeper_jar
|
163
170
|
|
164
|
-
## Contacting the author
|
165
|
-
|
166
|
-
* Send me a github message (slyphon)
|
167
|
-
* I'm usually hanging out in IRC on freenode.net in #ruby-lang and in #zookeeper
|
168
|
-
* if you really want to, you can also reach me via twitter [@slyphon][]
|
169
|
-
|
170
|
-
[@slyphon]: https://twitter.com/#!/slyphon
|
171
171
|
|
data/RELEASES.markdown
CHANGED
@@ -1,4 +1,22 @@
|
|
1
1
|
This file notes feature differences and bugfixes contained between releases.
|
2
|
+
### v1.1.1 ###
|
3
|
+
|
4
|
+
* Documentation for Locker and ilk
|
5
|
+
|
6
|
+
* Documentation cleanup
|
7
|
+
|
8
|
+
* Fixes for Locker tests so that we can run specs against all supported ruby implementations on travis (relies on in-process zookeeper server in the zk-server-1.0.1 gem)
|
9
|
+
|
10
|
+
* Support for 1.8.7 will be continued
|
11
|
+
|
12
|
+
## v1.1.0 ##
|
13
|
+
|
14
|
+
(forgot to put this here, put it in the readme though)
|
15
|
+
|
16
|
+
* NEW! Thread-per-Callback event delivery model! [Read all about it!](https://github.com/slyphon/zk/wiki/EventDeliveryModel). Provides a simple, sane way to increase the concurrency in your ZK-based app while maintaining the ordering guarantees ZooKeeper makes. Each callback can perform whatever work it needs to without blocking other callbacks from receiving events. Inspired by [Celluloid's](https://github.com/celluloid/celluloid) actor model.
|
17
|
+
|
18
|
+
* Use the [zk-server](https://github.com/slyphon/zk-server) gem to run a standalone ZooKeeper server for tests (`rake SPAWN_ZOOKEEPER=1`). Makes live-fire testing of any project that uses ZK easy to run anywhere!
|
19
|
+
|
2
20
|
|
3
21
|
### v1.0.0 ###
|
4
22
|
|
data/Rakefile
CHANGED
@@ -4,7 +4,7 @@ gemset_name = 'zk'
|
|
4
4
|
|
5
5
|
GEMSPEC_NAME = 'zk.gemspec'
|
6
6
|
|
7
|
-
%w[1.8.7 1.9.2 jruby rbx 1.9.3].each do |ns_name|
|
7
|
+
%w[1.8.7 1.9.2 jruby rbx ree 1.9.3].each do |ns_name|
|
8
8
|
rvm_ruby = (ns_name == 'rbx') ? "rbx-2.0.testing" : ns_name
|
9
9
|
|
10
10
|
ruby_with_gemset = "#{rvm_ruby}@#{gemset_name}"
|
@@ -59,6 +59,10 @@ namespace :yard do
|
|
59
59
|
task :server => :clean do
|
60
60
|
sh "yard server --reload"
|
61
61
|
end
|
62
|
+
|
63
|
+
task :gems do
|
64
|
+
sh 'yard server --gems --port=8809'
|
65
|
+
end
|
62
66
|
end
|
63
67
|
|
64
68
|
task :clean => 'yard:clean'
|
@@ -70,7 +74,7 @@ namespace :spec do
|
|
70
74
|
require 'rspec/core/rake_task'
|
71
75
|
|
72
76
|
RSpec::Core::RakeTask.new('spec:runner') do |t|
|
73
|
-
t.rspec_opts = '-f d'
|
77
|
+
t.rspec_opts = '-f d' if ENV['TRAVIS']
|
74
78
|
end
|
75
79
|
end
|
76
80
|
|
@@ -1,8 +1,10 @@
|
|
1
1
|
module ZK
|
2
2
|
module Client
|
3
|
-
#
|
3
|
+
# Convenience methods for creating instances of the cluster coordination
|
4
|
+
# objects ZK provides, using the current connection.
|
5
|
+
#
|
6
|
+
# Mixed into {ZK::Client::Threaded}
|
4
7
|
#
|
5
|
-
# convenience methods for dealing with zookeeper (rm -rf, mkdir -p, etc)
|
6
8
|
module Conveniences
|
7
9
|
# Queue an operation to be run on an internal threadpool. You may either
|
8
10
|
# provide an object that responds_to?(:call) or pass a block. There is no
|
@@ -18,6 +20,7 @@ module ZK
|
|
18
20
|
# @yield [] the block that should be run in the threadpool, if `callable`
|
19
21
|
# isn't given
|
20
22
|
#
|
23
|
+
# @private
|
21
24
|
def defer(callable=nil, &block)
|
22
25
|
@threadpool.defer(callable, &block)
|
23
26
|
end
|
@@ -33,55 +36,53 @@ module ZK
|
|
33
36
|
false
|
34
37
|
end
|
35
38
|
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# @see ZK::Locker::ExclusiveLocker
|
39
|
-
#
|
40
|
-
# returns a ZK::Locker::ExclusiveLocker instance using this Client and provided
|
41
|
-
# lock name
|
39
|
+
# Creates a new locker based on the name you provide, using this client
|
40
|
+
# as the connection.
|
42
41
|
#
|
43
|
-
#
|
44
|
-
#
|
42
|
+
# @param name [String] the name of the lock you wish to use. see
|
43
|
+
# {ZK::Locker} for a description of how the name is used to generate a
|
44
|
+
# key.
|
45
45
|
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
# zk.locker("blah")
|
49
|
-
# # => #<ZK::Locker::ExclusiveLocker:0x102034cf8 ...>
|
46
|
+
# @return [Locker::ExclusiveLocker] instance using this Client and
|
47
|
+
# provided lock name.
|
50
48
|
#
|
51
49
|
def locker(name)
|
52
50
|
Locker.exclusive_locker(self, name)
|
53
51
|
end
|
52
|
+
alias exclusive_locker locker
|
54
53
|
|
55
54
|
# create a new shared locking instance based on the name given
|
56
55
|
#
|
57
|
-
#
|
58
|
-
# lock name
|
59
|
-
#
|
60
|
-
# ==== Arguments
|
61
|
-
# * <tt>name</tt> name of the lock you wish to use
|
62
|
-
#
|
63
|
-
# ==== Examples
|
56
|
+
# @param name (see #locker)
|
64
57
|
#
|
65
|
-
#
|
66
|
-
#
|
58
|
+
# @return [Locker::SharedLocker] instance using this Client and provided
|
59
|
+
# lock name.
|
67
60
|
#
|
68
61
|
def shared_locker(name)
|
69
62
|
Locker.shared_locker(self, name)
|
70
63
|
end
|
71
64
|
|
72
65
|
# Convenience method for acquiring a lock then executing a code block. This
|
73
|
-
# will block the caller until the lock is acquired
|
66
|
+
# will block the caller until the lock is acquired, and release the lock
|
67
|
+
# when the block is exited.
|
74
68
|
#
|
75
|
-
#
|
76
|
-
# * <tt>name</tt>: the name of the lock to use
|
77
|
-
# * <tt>:mode</tt>: either :shared or :exclusive, defaults to :exclusive
|
69
|
+
# @param name (see #locker)
|
78
70
|
#
|
79
|
-
#
|
71
|
+
# @option opts [:shared,:exclusive] :mode (:exclusive) the type of lock
|
72
|
+
# to create and then call with_lock on
|
73
|
+
#
|
74
|
+
# @return the return value of the given block
|
75
|
+
#
|
76
|
+
# @yield calls the block once the lock has been acquired
|
77
|
+
#
|
78
|
+
# @example
|
80
79
|
#
|
81
80
|
# zk.with_lock('foo') do
|
82
81
|
# # this code is executed while holding the lock
|
83
82
|
# end
|
84
83
|
#
|
84
|
+
# @raise [ArgumentError] if `opts[:mode]` is not one of the expected values
|
85
|
+
#
|
85
86
|
def with_lock(name, opts={}, &b)
|
86
87
|
mode = opts[:mode] || :exclusive
|
87
88
|
|
@@ -94,29 +95,38 @@ module ZK
|
|
94
95
|
end
|
95
96
|
end
|
96
97
|
|
97
|
-
#
|
98
|
-
#
|
98
|
+
# Constructs an {Election::Candidate} object using self as the connection
|
99
|
+
#
|
100
|
+
# @param [String] name the name of the election to participate in
|
101
|
+
# @param [String] data the data we will write to the leadership node if/when we win
|
99
102
|
#
|
103
|
+
# @return [Election::Candidate] the candidate instance using self as a connection
|
100
104
|
def election_candidate(name, data, opts={})
|
101
105
|
opts = opts.merge(:data => data)
|
102
106
|
ZK::Election::Candidate.new(self, name, opts)
|
103
107
|
end
|
104
108
|
|
105
|
-
#
|
106
|
-
#
|
109
|
+
# Constructs an {Election::Observer} object using self as the connection
|
110
|
+
#
|
111
|
+
# @param name (see #election_candidate)
|
107
112
|
#
|
113
|
+
# @return [Election::Observer] the candidate instance using self as a connection
|
108
114
|
def election_observer(name, opts={})
|
109
115
|
ZK::Election::Observer.new(self, name, opts)
|
110
116
|
end
|
111
117
|
|
112
|
-
# creates a new message queue of name
|
118
|
+
# creates a new message queue of name `name`
|
119
|
+
#
|
120
|
+
# @note The message queue has some scalability limitations. For
|
121
|
+
# heavy-duty message processing, the author recommends investigating
|
122
|
+
# a purpose-built solution.
|
113
123
|
#
|
114
|
-
#
|
124
|
+
# @return [MessageQueue] the new instance using self as its
|
125
|
+
# client
|
115
126
|
#
|
116
|
-
#
|
117
|
-
# * <tt>name</tt> the name of the queue
|
127
|
+
# @param [String] name the name of the queue
|
118
128
|
#
|
119
|
-
#
|
129
|
+
# @example
|
120
130
|
#
|
121
131
|
# zk.queue("blah").publish({:some_data => "that is yaml serializable"})
|
122
132
|
#
|
data/lib/zk/client/threaded.rb
CHANGED
@@ -3,7 +3,7 @@ module ZK
|
|
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
|
-
# If you want to register `on_*` callbacks (see ZK::Client::StateMixin)
|
6
|
+
# If you want to register `on_*` callbacks (see {ZK::Client::StateMixin})
|
7
7
|
# then you should pass a block, which will be called before the
|
8
8
|
# connection is set up (this way you can get the `on_connected` event). See
|
9
9
|
# the 'Register on_connected callback' example.
|
@@ -26,7 +26,7 @@ module ZK
|
|
26
26
|
# # the nice thing about this pattern is that in the case of a call to #reopen
|
27
27
|
# # all your watches will be re-established
|
28
28
|
#
|
29
|
-
# ZK::Client::Threaded.new('
|
29
|
+
# ZK::Client::Threaded.new('localhost:2181') do |zk|
|
30
30
|
# # do not do anything in here except register callbacks
|
31
31
|
#
|
32
32
|
# zk.on_connected do |event|
|
@@ -45,6 +45,12 @@ module ZK
|
|
45
45
|
|
46
46
|
# Construct a new threaded client.
|
47
47
|
#
|
48
|
+
# Pay close attention to the `:threaded` option, and have a look at the
|
49
|
+
# [EventDeliveryModel](https://github.com/slyphon/zk/wiki/EventDeliveryModel)
|
50
|
+
# page in the wiki for a discussion of the relative advantages and
|
51
|
+
# disadvantages of the choices available. The default is safe, but the
|
52
|
+
# alternative will likely provide better performance.
|
53
|
+
#
|
48
54
|
# @note The `:timeout` argument here is *not* the session_timeout for the
|
49
55
|
# connection. rather it is the amount of time we wait for the connection
|
50
56
|
# to be established. The session timeout exchanged with the server is
|
@@ -68,12 +74,15 @@ module ZK
|
|
68
74
|
# [this wiki article](https://github.com/slyphon/zk/wiki/EventDeliveryModel) for more
|
69
75
|
# information and a demonstration.
|
70
76
|
#
|
71
|
-
# @param
|
77
|
+
# @param host (see Base#initialize)
|
72
78
|
#
|
73
79
|
# @option opts [true,false] :reconnect (true) if true, we will register
|
74
80
|
# the equivalent of `on_session_expired { zk.reopen }` so that in the
|
75
81
|
# case of an expired session, we will keep trying to reestablish the
|
76
|
-
# connection.
|
82
|
+
# connection. You *almost definately* want to leave this at the default.
|
83
|
+
# The only reason not to is if you already have a handler registered
|
84
|
+
# that does something application specific, and you want to avoid a
|
85
|
+
# conflict.
|
77
86
|
#
|
78
87
|
# @option opts [:single,:per_callback] :thread (:single) choose your event
|
79
88
|
# delivery model:
|
@@ -112,6 +121,8 @@ module ZK
|
|
112
121
|
# operations with the client as you will get a NoMethodError (the
|
113
122
|
# underlying connection is nil).
|
114
123
|
#
|
124
|
+
# @return [Threaded] a new client instance
|
125
|
+
#
|
115
126
|
# @see Base#initialize
|
116
127
|
def initialize(host, opts={}, &b)
|
117
128
|
super(host, opts)
|
data/lib/zk/client/unixisms.rb
CHANGED
@@ -2,6 +2,7 @@ module ZK
|
|
2
2
|
module Client
|
3
3
|
module Unixisms
|
4
4
|
include ZookeeperConstants
|
5
|
+
include Exceptions
|
5
6
|
|
6
7
|
# Creates all parent paths and 'path' in zookeeper as persistent nodes with
|
7
8
|
# zero data.
|
@@ -21,12 +22,12 @@ module ZK
|
|
21
22
|
# this could get expensive w/ psychotically long paths
|
22
23
|
|
23
24
|
create(path, '', :mode => :persistent)
|
24
|
-
rescue
|
25
|
+
rescue NodeExists
|
25
26
|
return
|
26
|
-
rescue
|
27
|
+
rescue NoNode
|
27
28
|
if File.dirname(path) == '/'
|
28
29
|
# ok, we're screwed, blow up
|
29
|
-
raise
|
30
|
+
raise NonExistentRootError, "could not create '/', are you chrooted into a non-existent path?", caller
|
30
31
|
end
|
31
32
|
|
32
33
|
mkdir_p(File.dirname(path))
|
@@ -43,7 +44,7 @@ module ZK
|
|
43
44
|
|
44
45
|
delete(path)
|
45
46
|
nil
|
46
|
-
rescue
|
47
|
+
rescue NoNode
|
47
48
|
end
|
48
49
|
end
|
49
50
|
end
|
@@ -103,19 +104,27 @@ module ZK
|
|
103
104
|
ZK::Find.find(self, *paths, &block)
|
104
105
|
end
|
105
106
|
|
106
|
-
# Will _safely_ block the caller until `abs_node_path` has been removed
|
107
|
-
#
|
108
|
-
# if a session event occurs that would ensure the event would
|
109
|
-
#
|
110
|
-
# from the event distribution thread (which would cause a deadlock).
|
111
|
-
#
|
112
|
-
# @note this is dangerous to use in callbacks! there is only one
|
113
|
-
# event-delivery thread, so if you use this method in a callback or
|
114
|
-
# watcher, you *will* deadlock!
|
107
|
+
# Will _safely_ block the caller until `abs_node_path` has been removed
|
108
|
+
# (this is trickier than it appears at first). This method will wake the
|
109
|
+
# caller if a session event occurs that would ensure the event would
|
110
|
+
# never be delivered.
|
115
111
|
#
|
116
112
|
# @raise [Exceptions::InterruptedSession] If a session event occurs while we're
|
117
113
|
# blocked waiting for the node to be deleted, an exception that
|
118
|
-
# mixes in the InterruptedSession module will be raised
|
114
|
+
# mixes in the InterruptedSession module will be raised, so for convenience,
|
115
|
+
# users can just rescue {InterruptedSession}.
|
116
|
+
#
|
117
|
+
# @raise [ZookeeperExceptions::ZookeeperException::SessionExpired] raised
|
118
|
+
# when we receive `ZOO_EXPIRED_SESSION_STATE` while blocking waiting for
|
119
|
+
# a deleted event. Includes the {InterruptedSession} module.
|
120
|
+
#
|
121
|
+
# @raise [ZookeeperExceptions::ZookeeperException::NotConnected] raised
|
122
|
+
# when we receive `ZOO_CONNECTING_STATE` while blocking waiting for
|
123
|
+
# a deleted event. Includes the {InterruptedSession} module.
|
124
|
+
#
|
125
|
+
# @raise [ZookeeperExceptions::ZookeeperException::ConnectionClosed] raised
|
126
|
+
# when we receive `ZOO_CLOSED_STATE` while blocking waiting for
|
127
|
+
# a deleted event. Includes the {InterruptedSession} module.
|
119
128
|
#
|
120
129
|
def block_until_node_deleted(abs_node_path)
|
121
130
|
subs = []
|
data/lib/zk/client.rb
CHANGED
data/lib/zk/event_handler.rb
CHANGED
data/lib/zk/exceptions.rb
CHANGED
@@ -74,27 +74,30 @@ module ZK
|
|
74
74
|
include InterruptedSession
|
75
75
|
end
|
76
76
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
77
|
+
silence_warnings do
|
78
|
+
# @private
|
79
|
+
ERROR_MAP = {
|
80
|
+
SYSTEMERROR => SystemError,
|
81
|
+
RUNTIMEINCONSISTENCY => RunTimeInconsistency,
|
82
|
+
DATAINCONSISTENCY => DataInconsistency,
|
83
|
+
CONNECTIONLOSS => ConnectionLoss,
|
84
|
+
MARSHALLINGERROR => MarshallingError,
|
85
|
+
UNIMPLEMENTED => Unimplemented,
|
86
|
+
OPERATIONTIMEOUT => OperationTimeOut,
|
87
|
+
BADARGUMENTS => BadArguments,
|
88
|
+
APIERROR => ApiError,
|
89
|
+
NONODE => NoNode,
|
90
|
+
NOAUTH => NoAuth,
|
91
|
+
BADVERSION => BadVersion,
|
92
|
+
NOCHILDRENFOREPHEMERALS => NoChildrenForEphemerals,
|
93
|
+
NODEEXISTS => NodeExists,
|
94
|
+
NOTEMPTY => NotEmpty,
|
95
|
+
SESSIONEXPIRED => SessionExpired,
|
96
|
+
INVALIDCALLBACK => InvalidCallback,
|
97
|
+
INVALIDACL => InvalidACL,
|
98
|
+
AUTHFAILED => AuthFailed,
|
99
|
+
}.freeze
|
100
|
+
end
|
98
101
|
|
99
102
|
# base class of ZK generated errors (not driver-level errors)
|
100
103
|
class ZKError < StandardError; end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module ZK
|
2
|
+
module Locker
|
3
|
+
# An exclusive lock implementation
|
4
|
+
#
|
5
|
+
# If the name 'dingus' is given, then in the case of an exclusive lock, the
|
6
|
+
# algorithm works like:
|
7
|
+
#
|
8
|
+
# * lock_path = `zk.create("/_zklocking/dingus/ex", :sequential => true, :ephemeral => true)`
|
9
|
+
# * extract the digit from the lock path
|
10
|
+
# * of all the children under '/_zklocking/dingus', do we have the lowest digit?
|
11
|
+
# * __yes__: then we hold the lock, if we're non-blocking, return true
|
12
|
+
# * __no__: is the lock blocking?
|
13
|
+
# * __yes__: then set a watch on the next-to-lowest node and sleep the current thread until that node has been deleted
|
14
|
+
# * __no__: return false, you lose
|
15
|
+
#
|
16
|
+
class ExclusiveLocker < LockerBase
|
17
|
+
# obtain an exclusive lock.
|
18
|
+
#
|
19
|
+
# @param blocking (see SharedLocker#lock!)
|
20
|
+
# @return (see SharedLocker#lock!)
|
21
|
+
#
|
22
|
+
# @raise [InterruptedSession] raised when blocked waiting for a lock and
|
23
|
+
# the underlying client's session is interrupted.
|
24
|
+
#
|
25
|
+
# @see ZK::Client::Unixisms#block_until_node_deleted more about possible execptions
|
26
|
+
#
|
27
|
+
def lock!(blocking=false)
|
28
|
+
return true if @locked
|
29
|
+
create_lock_path!(EXCLUSIVE_LOCK_PREFIX)
|
30
|
+
|
31
|
+
if got_write_lock?
|
32
|
+
@locked = true
|
33
|
+
elsif blocking
|
34
|
+
in_waiting_status do
|
35
|
+
block_until_write_lock!
|
36
|
+
end
|
37
|
+
else
|
38
|
+
cleanup_lock_path!
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
# the node that is next-lowest in sequence number to ours, the one we
|
45
|
+
# watch for updates to
|
46
|
+
# @private
|
47
|
+
def next_lowest_node
|
48
|
+
ary = ordered_lock_children()
|
49
|
+
my_idx = ary.index(lock_basename)
|
50
|
+
|
51
|
+
raise WeAreTheLowestLockNumberException if my_idx == 0
|
52
|
+
|
53
|
+
ary[(my_idx - 1)]
|
54
|
+
end
|
55
|
+
|
56
|
+
# @private
|
57
|
+
def got_write_lock?
|
58
|
+
ordered_lock_children.first == lock_basename
|
59
|
+
end
|
60
|
+
|
61
|
+
# @private
|
62
|
+
def block_until_write_lock!
|
63
|
+
begin
|
64
|
+
path = [root_lock_path, next_lowest_node].join('/')
|
65
|
+
logger.debug { "SharedLocker#block_until_write_lock! path=#{path.inspect}" }
|
66
|
+
@zk.block_until_node_deleted(path)
|
67
|
+
rescue WeAreTheLowestLockNumberException
|
68
|
+
end
|
69
|
+
|
70
|
+
@locked = true
|
71
|
+
end
|
72
|
+
end # ExclusiveLocker
|
73
|
+
end # Locker
|
74
|
+
end # ZK
|
75
|
+
|