zk 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.gitmodules +3 -0
- data/.travis.yml +5 -1
- data/.yardopts +0 -1
- data/Gemfile +15 -0
- data/README.markdown +46 -76
- data/RELEASES.markdown +18 -0
- data/Rakefile +46 -65
- data/lib/zk.rb +5 -0
- data/lib/zk/client/base.rb +33 -20
- data/lib/zk/client/unixisms.rb +7 -7
- data/lib/zk/core_ext.rb +26 -0
- data/lib/zk/event.rb +5 -5
- data/lib/zk/event_handler.rb +2 -2
- data/lib/zk/event_handler_subscription/actor.rb +1 -13
- data/lib/zk/event_handler_subscription/base.rb +15 -15
- data/lib/zk/exceptions.rb +3 -0
- data/lib/zk/extensions.rb +8 -37
- data/lib/zk/locker.rb +8 -0
- data/lib/zk/locker/exclusive_locker.rb +25 -12
- data/lib/zk/locker/locker_base.rb +124 -21
- data/lib/zk/locker/shared_locker.rb +34 -22
- data/lib/zk/stat.rb +2 -2
- data/lib/zk/subscription.rb +65 -0
- data/lib/zk/version.rb +1 -1
- data/notes/documentation-notes +20 -0
- data/notes/voting-notes.txt +39 -0
- data/spec/event_catcher_spec.rb +5 -2
- data/spec/informal/close-in-event-thread.rb +1 -1
- data/spec/shared/client_examples.rb +1 -1
- data/spec/spec_helper.rb +8 -1
- data/spec/support/event_catcher.rb +6 -6
- data/spec/support/latch.rb +23 -0
- data/spec/support/logging.rb +1 -1
- data/spec/watch_spec.rb +1 -1
- data/spec/zk/client/locking_and_session_death_spec.rb +7 -7
- data/spec/zk/locker_spec.rb +177 -62
- data/spec/zookeeper_spec.rb +2 -2
- data/zk.gemspec +3 -1
- metadata +34 -10
data/lib/zk/client/unixisms.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module ZK
|
2
2
|
module Client
|
3
3
|
module Unixisms
|
4
|
-
include
|
4
|
+
include Zookeeper::Constants
|
5
5
|
include Exceptions
|
6
6
|
|
7
7
|
# Creates all parent paths and 'path' in zookeeper as persistent nodes with
|
@@ -114,15 +114,15 @@ module ZK
|
|
114
114
|
# mixes in the InterruptedSession module will be raised, so for convenience,
|
115
115
|
# users can just rescue {InterruptedSession}.
|
116
116
|
#
|
117
|
-
# @raise [
|
117
|
+
# @raise [Zookeeper::Exceptions::SessionExpired] raised
|
118
118
|
# when we receive `ZOO_EXPIRED_SESSION_STATE` while blocking waiting for
|
119
119
|
# a deleted event. Includes the {InterruptedSession} module.
|
120
120
|
#
|
121
|
-
# @raise [
|
121
|
+
# @raise [Zookeeper::Exceptions::NotConnected] raised
|
122
122
|
# when we receive `ZOO_CONNECTING_STATE` while blocking waiting for
|
123
123
|
# a deleted event. Includes the {InterruptedSession} module.
|
124
124
|
#
|
125
|
-
# @raise [
|
125
|
+
# @raise [Zookeeper::Exceptions::ConnectionClosed] raised
|
126
126
|
# when we receive `ZOO_CLOSED_STATE` while blocking waiting for
|
127
127
|
# a deleted event. Includes the {InterruptedSession} module.
|
128
128
|
#
|
@@ -162,11 +162,11 @@ module ZK
|
|
162
162
|
when :deleted
|
163
163
|
true
|
164
164
|
when ZOO_EXPIRED_SESSION_STATE
|
165
|
-
raise
|
165
|
+
raise Zookeeper::Exceptions::SessionExpired
|
166
166
|
when ZOO_CONNECTING_STATE
|
167
|
-
raise
|
167
|
+
raise Zookeeper::Exceptions::NotConnected
|
168
168
|
when ZOO_CLOSED_STATE
|
169
|
-
raise
|
169
|
+
raise Zookeeper::Exceptions::ConnectionClosed
|
170
170
|
else
|
171
171
|
raise "Hit unexpected case in block_until_node_deleted"
|
172
172
|
end
|
data/lib/zk/core_ext.rb
CHANGED
@@ -73,3 +73,29 @@ module ::Kernel
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
+
# @private
|
77
|
+
class ::Module
|
78
|
+
unless method_defined?(:alias_method_chain)
|
79
|
+
def alias_method_chain(target, feature)
|
80
|
+
# Strip out punctuation on predicates or bang methods since
|
81
|
+
# e.g. target?_without_feature is not a valid method name.
|
82
|
+
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
|
83
|
+
yield(aliased_target, punctuation) if block_given?
|
84
|
+
|
85
|
+
with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
|
86
|
+
|
87
|
+
alias_method without_method, target
|
88
|
+
alias_method target, with_method
|
89
|
+
|
90
|
+
case
|
91
|
+
when public_method_defined?(without_method)
|
92
|
+
public target
|
93
|
+
when protected_method_defined?(without_method)
|
94
|
+
protected target
|
95
|
+
when private_method_defined?(without_method)
|
96
|
+
private target
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
data/lib/zk/event.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module ZK
|
2
2
|
# Provides most of the functionality ZK uses around events. Base class is actually
|
3
|
-
# [
|
3
|
+
# [Zookeeper::Callbacks::WatcherCallback](http://rubydoc.info/gems/slyphon-zookeeper/Zookeeper::Callbacks/WatcherCallback),
|
4
4
|
# but this module is mixed in and provides a lot of useful syntactic sugar.
|
5
5
|
#
|
6
6
|
module Event
|
7
|
-
include
|
7
|
+
include Zookeeper::Constants
|
8
8
|
|
9
9
|
# unless defined? apparently messes up yard's ability to see the @private
|
10
10
|
silence_warnings do
|
@@ -26,10 +26,10 @@ module ZK
|
|
26
26
|
EVENT_TYPES = %w[created deleted changed child session notwatching].freeze
|
27
27
|
end
|
28
28
|
|
29
|
-
# for testing, create a new
|
29
|
+
# for testing, create a new Zookeeper::Callbacks::WatcherCallback and return it
|
30
30
|
# @private
|
31
31
|
def self.new(hash)
|
32
|
-
|
32
|
+
Zookeeper::Callbacks::WatcherCallback.new.tap do |wc|
|
33
33
|
wc.call(hash)
|
34
34
|
end
|
35
35
|
end
|
@@ -170,7 +170,7 @@ module ZK
|
|
170
170
|
end
|
171
171
|
|
172
172
|
# @private
|
173
|
-
|
173
|
+
Zookeeper::Callbacks::WatcherCallback.class_eval do
|
174
174
|
include ::ZK::Event
|
175
175
|
end
|
176
176
|
|
data/lib/zk/event_handler.rb
CHANGED
@@ -257,7 +257,7 @@ module ZK
|
|
257
257
|
protected
|
258
258
|
# @private
|
259
259
|
def watcher_callback
|
260
|
-
|
260
|
+
Zookeeper::Callbacks::WatcherCallback.create { |event| process(event) }
|
261
261
|
end
|
262
262
|
|
263
263
|
# @private
|
@@ -265,7 +265,7 @@ module ZK
|
|
265
265
|
int =
|
266
266
|
case arg
|
267
267
|
when String, Symbol
|
268
|
-
|
268
|
+
Zookeeper::Constants.const_get(:"ZOO_#{arg.to_s.upcase}_STATE")
|
269
269
|
when Integer
|
270
270
|
arg
|
271
271
|
else
|
@@ -16,19 +16,7 @@ module ZK
|
|
16
16
|
# guarantees), just perhaps at different times.
|
17
17
|
#
|
18
18
|
class Actor < Base
|
19
|
-
|
20
|
-
|
21
|
-
def_delegators :@threaded_callback, :call
|
22
|
-
|
23
|
-
def initialize(*a)
|
24
|
-
super
|
25
|
-
@threaded_callback = ThreadedCallback.new(@callback)
|
26
|
-
end
|
27
|
-
|
28
|
-
def unsubscribe
|
29
|
-
@threaded_callback.shutdown
|
30
|
-
super
|
31
|
-
end
|
19
|
+
include Subscription::ActorStyle
|
32
20
|
|
33
21
|
def async?
|
34
22
|
true
|
@@ -1,19 +1,15 @@
|
|
1
1
|
module ZK
|
2
2
|
module EventHandlerSubscription
|
3
|
-
class Base
|
3
|
+
class Base < Subscription::Base
|
4
4
|
include ZK::Logging
|
5
5
|
|
6
|
-
# the event handler associated with this subscription
|
7
|
-
# @return [EventHandler]
|
8
|
-
attr_accessor :event_handler
|
9
|
-
|
10
6
|
# the path this subscription is for
|
11
7
|
# @return [String]
|
12
8
|
attr_accessor :path
|
13
9
|
|
14
10
|
# the block associated with the path
|
15
11
|
# @return [Proc]
|
16
|
-
|
12
|
+
# attr_accessor :callback
|
17
13
|
|
18
14
|
# an array of what kinds of events this handler is interested in receiving
|
19
15
|
# this is the :only option, essentially
|
@@ -24,26 +20,30 @@ module ZK
|
|
24
20
|
# @private
|
25
21
|
attr_accessor :interests
|
26
22
|
|
23
|
+
alias event_handler parent
|
24
|
+
alias callback callable
|
25
|
+
|
27
26
|
ALL_EVENTS = [:created, :deleted, :changed, :child].freeze unless defined?(ALL_EVENTS)
|
28
27
|
ALL_EVENT_SET = Set.new(ALL_EVENTS).freeze unless defined?(ALL_EVENT_SET)
|
29
28
|
|
30
29
|
# @private
|
31
30
|
def initialize(event_handler, path, callback, opts={})
|
32
|
-
|
31
|
+
super(event_handler, callback)
|
32
|
+
@path = path
|
33
33
|
@interests = prep_interests(opts[:only])
|
34
34
|
end
|
35
35
|
|
36
36
|
# unsubscribe from the path or state you were watching
|
37
37
|
# @see ZK::Client::Base#register
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
# def unsubscribe
|
39
|
+
# @event_handler.unregister(self)
|
40
|
+
# end
|
41
|
+
# alias :unregister :unsubscribe
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
# # @private
|
44
|
+
# def call(event)
|
45
|
+
# callback.call(event)
|
46
|
+
# end
|
47
47
|
|
48
48
|
# the Actor returns true for this
|
49
49
|
# @private
|
data/lib/zk/exceptions.rb
CHANGED
@@ -126,6 +126,9 @@ module ZK
|
|
126
126
|
# raised when someone performs a blocking ZK operation on the event dispatch thread.
|
127
127
|
class EventDispatchThreadException < ZKError; end
|
128
128
|
|
129
|
+
# raised when someone calls lock.assert! but they do not hold the lock
|
130
|
+
class LockAssertionFailedError < ZKError; end
|
131
|
+
|
129
132
|
# raised when a chrooted conection is requested but the root doesn't exist
|
130
133
|
class ChrootPathDoesNotExistError < NoNode
|
131
134
|
def initialize(host_string, chroot_path)
|
data/lib/zk/extensions.rb
CHANGED
@@ -30,49 +30,20 @@ module ZK
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
33
|
-
|
34
|
-
module Extensions
|
35
|
-
# some extensions to the ZookeeperCallbacks classes, mainly convenience
|
36
|
-
# interrogators
|
37
|
-
module Callbacks
|
38
|
-
module Callback
|
39
|
-
extend Concern
|
40
|
-
|
41
|
-
# allow access to the connection that fired this callback
|
42
|
-
attr_accessor :zk
|
43
|
-
|
44
|
-
module ClassMethods
|
45
|
-
# allows for easier construction of a user callback block that will be
|
46
|
-
# called with the callback object itself as an argument.
|
47
|
-
#
|
48
|
-
# *args, if given, will be passed on *after* the callback
|
49
|
-
#
|
50
|
-
# @example
|
51
|
-
#
|
52
|
-
# WatcherCallback.create do |cb|
|
53
|
-
# puts "watcher callback called with argument: #{cb.inspect}"
|
54
|
-
# end
|
55
|
-
#
|
56
|
-
# "watcher callback called with argument: #<ZookeeperCallbacks::WatcherCallback:0x1018a3958 @state=3, @type=1, ...>"
|
57
|
-
#
|
58
|
-
#
|
59
|
-
def create(*args, &block)
|
60
|
-
cb_inst = new { block.call(cb_inst) }
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end # Callback
|
64
|
-
end # Callbacks
|
65
|
-
end # Extensions
|
66
33
|
end # ZK
|
67
34
|
|
68
|
-
|
69
|
-
|
35
|
+
Zookeeper::Callbacks::Base.class_eval do
|
36
|
+
# allows us to stick a reference to the connection associated with the event
|
37
|
+
# on the event
|
38
|
+
attr_accessor :zk
|
39
|
+
end
|
40
|
+
|
70
41
|
|
71
|
-
# Include the InterruptedSession module in key
|
42
|
+
# Include the InterruptedSession module in key Zookeeper::Exceptions to allow
|
72
43
|
# clients to catch a single error type when waiting on a node (for example)
|
73
44
|
|
74
45
|
[:ConnectionClosed, :NotConnected, :SessionExpired, :SessionMoved, :ConnectionLoss].each do |class_name|
|
75
|
-
|
46
|
+
Zookeeper::Exceptions.const_get(class_name).tap do |klass|
|
76
47
|
klass.__send__(:include, ZK::Exceptions::InterruptedSession)
|
77
48
|
end
|
78
49
|
end
|
data/lib/zk/locker.rb
CHANGED
@@ -93,6 +93,14 @@ module ZK
|
|
93
93
|
SHARED_LOCK_PREFIX = 'sh'.freeze
|
94
94
|
EXCLUSIVE_LOCK_PREFIX = 'ex'.freeze
|
95
95
|
|
96
|
+
@default_root_lock_node = '/_zklocking'.freeze unless @default_root_lock_node
|
97
|
+
|
98
|
+
class << self
|
99
|
+
# the default root path we will use when a value is not given to a
|
100
|
+
# constructor
|
101
|
+
attr_accessor :default_root_lock_node
|
102
|
+
end
|
103
|
+
|
96
104
|
# Create a {SharedLocker} instance
|
97
105
|
#
|
98
106
|
# @param client (see LockerBase#initialize)
|
@@ -14,17 +14,10 @@ module ZK
|
|
14
14
|
# * __no__: return false, you lose
|
15
15
|
#
|
16
16
|
class ExclusiveLocker < LockerBase
|
17
|
+
# (see LockerBase#lock)
|
17
18
|
# obtain an exclusive lock.
|
18
19
|
#
|
19
|
-
|
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)
|
20
|
+
def lock(blocking=false)
|
28
21
|
return true if @locked
|
29
22
|
create_lock_path!(EXCLUSIVE_LOCK_PREFIX)
|
30
23
|
|
@@ -40,10 +33,31 @@ module ZK
|
|
40
33
|
end
|
41
34
|
end
|
42
35
|
|
36
|
+
# (see LockerBase#assert!)
|
37
|
+
#
|
38
|
+
# checks that we:
|
39
|
+
#
|
40
|
+
# * we have obtained the lock (i.e. {#locked?} is true)
|
41
|
+
# * have a lock path
|
42
|
+
# * our lock path still exists
|
43
|
+
# * there are no locks, _exclusive or shared_, with lower numbers than ours
|
44
|
+
#
|
45
|
+
def assert!
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
# (see LockerBase#acquirable?)
|
50
|
+
def acquirable?
|
51
|
+
return true if locked?
|
52
|
+
stat = zk.stat(root_lock_path)
|
53
|
+
!stat.exists? or stat.num_children == 0
|
54
|
+
rescue Exceptions::NoNode
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
43
58
|
protected
|
44
59
|
# the node that is next-lowest in sequence number to ours, the one we
|
45
60
|
# watch for updates to
|
46
|
-
# @private
|
47
61
|
def next_lowest_node
|
48
62
|
ary = ordered_lock_children()
|
49
63
|
my_idx = ary.index(lock_basename)
|
@@ -53,12 +67,11 @@ module ZK
|
|
53
67
|
ary[(my_idx - 1)]
|
54
68
|
end
|
55
69
|
|
56
|
-
# @private
|
57
70
|
def got_write_lock?
|
58
71
|
ordered_lock_children.first == lock_basename
|
59
72
|
end
|
73
|
+
alias got_lock? got_write_lock?
|
60
74
|
|
61
|
-
# @private
|
62
75
|
def block_until_write_lock!
|
63
76
|
begin
|
64
77
|
path = [root_lock_path, next_lowest_node].join('/')
|
@@ -9,6 +9,7 @@ module ZK
|
|
9
9
|
#
|
10
10
|
class LockerBase
|
11
11
|
include ZK::Logging
|
12
|
+
include ZK::Exceptions
|
12
13
|
|
13
14
|
# @private
|
14
15
|
attr_accessor :zk
|
@@ -42,14 +43,15 @@ module ZK
|
|
42
43
|
# holding the same lock.
|
43
44
|
#
|
44
45
|
# @param [String] root_lock_node the root path on the server under which all
|
45
|
-
# locks will be generated
|
46
|
+
# locks will be generated, the default is Locker.default_root_lock_node
|
46
47
|
#
|
47
|
-
def initialize(client, name, root_lock_node
|
48
|
+
def initialize(client, name, root_lock_node=nil)
|
48
49
|
@zk = client
|
49
|
-
@root_lock_node = root_lock_node
|
50
|
+
@root_lock_node = root_lock_node || Locker.default_root_lock_node
|
50
51
|
@path = name
|
51
52
|
@locked = false
|
52
53
|
@waiting = false
|
54
|
+
@lock_path = nil
|
53
55
|
@root_lock_path = "#{@root_lock_node}/#{@path.gsub("/", "__")}"
|
54
56
|
end
|
55
57
|
|
@@ -58,10 +60,10 @@ module ZK
|
|
58
60
|
# there is no non-blocking version of this method
|
59
61
|
#
|
60
62
|
def with_lock
|
61
|
-
lock
|
63
|
+
lock(true)
|
62
64
|
yield
|
63
65
|
ensure
|
64
|
-
unlock
|
66
|
+
unlock
|
65
67
|
end
|
66
68
|
|
67
69
|
# the basename of our lock path
|
@@ -79,9 +81,36 @@ module ZK
|
|
79
81
|
lock_path and File.basename(lock_path)
|
80
82
|
end
|
81
83
|
|
82
|
-
#
|
83
|
-
|
84
|
-
|
84
|
+
# returns our current idea of whether or not we hold the lock, which does
|
85
|
+
# not actually check the state on the server.
|
86
|
+
#
|
87
|
+
# The reason for the equivocation around _thinking_ we hold the lock is
|
88
|
+
# to contrast our current state and the actual state on the server. If you
|
89
|
+
# want to make double-triple certain of the state of the lock, use {#assert!}
|
90
|
+
#
|
91
|
+
# @return [true] if we hold the lock
|
92
|
+
# @return [false] if we don't hold the lock
|
93
|
+
#
|
94
|
+
def locked?(check_if_any=false)
|
95
|
+
false|@locked
|
96
|
+
end
|
97
|
+
|
98
|
+
# * If this instance holds the lock {#locked? is true} we return true (as
|
99
|
+
# we have already succeeded in acquiring the lock)
|
100
|
+
# * If this instance doesn't hold the lock, we'll do a check on the server
|
101
|
+
# to see if there are any participants _who hold the lock and would
|
102
|
+
# prevent us from acquiring the lock_.
|
103
|
+
# * If this instance could acquire the lock we will return true.
|
104
|
+
# * If another client would prevent us from acquiring the lock, we return false.
|
105
|
+
#
|
106
|
+
# @note It should be obvious, but there is no way to guarantee that
|
107
|
+
# between the time this method checks the server and taking any action to
|
108
|
+
# acquire the lock, another client may grab the lock before us (or
|
109
|
+
# converseley, another client may release the lock). This is simply meant
|
110
|
+
# as an advisory, and may be useful in some cases.
|
111
|
+
#
|
112
|
+
def acquirable?
|
113
|
+
raise NotImplementedError
|
85
114
|
end
|
86
115
|
|
87
116
|
# @return [true] if we held the lock and this method has
|
@@ -89,7 +118,7 @@ module ZK
|
|
89
118
|
#
|
90
119
|
# @return [false] we did not own the lock
|
91
120
|
#
|
92
|
-
def unlock
|
121
|
+
def unlock
|
93
122
|
if @locked
|
94
123
|
cleanup_lock_path!
|
95
124
|
@locked = false
|
@@ -99,6 +128,38 @@ module ZK
|
|
99
128
|
end
|
100
129
|
end
|
101
130
|
|
131
|
+
# (see #unlock)
|
132
|
+
# @deprecated the use of unlock! is deprecated and may be removed or have
|
133
|
+
# its semantics changed in a future release
|
134
|
+
def unlock!
|
135
|
+
unlock
|
136
|
+
end
|
137
|
+
|
138
|
+
# @param blocking [true,false] if true we block the caller until we can obtain
|
139
|
+
# a lock on the resource
|
140
|
+
#
|
141
|
+
# @return [true] if we're already obtained a shared lock, or if we were able to
|
142
|
+
# obtain the lock in non-blocking mode.
|
143
|
+
#
|
144
|
+
# @return [false] if we did not obtain the lock in non-blocking mode
|
145
|
+
#
|
146
|
+
# @return [void] if we obtained the lock in blocking mode.
|
147
|
+
#
|
148
|
+
# @raise [InterruptedSession] raised when blocked waiting for a lock and
|
149
|
+
# the underlying client's session is interrupted.
|
150
|
+
#
|
151
|
+
# @see ZK::Client::Unixisms#block_until_node_deleted more about possible execptions
|
152
|
+
def lock(blocking=false)
|
153
|
+
raise NotImplementedError
|
154
|
+
end
|
155
|
+
|
156
|
+
# (see #lock)
|
157
|
+
# @deprecated the use of lock! is deprecated and may be removed or have
|
158
|
+
# its semantics changed in a future release
|
159
|
+
def lock!(blocking=false)
|
160
|
+
lock(blocking)
|
161
|
+
end
|
162
|
+
|
102
163
|
# returns true if this locker is waiting to acquire lock
|
103
164
|
#
|
104
165
|
# @private
|
@@ -106,8 +167,43 @@ module ZK
|
|
106
167
|
false|@waiting
|
107
168
|
end
|
108
169
|
|
170
|
+
# This is for users who wish to check that the assumption is correct
|
171
|
+
# that they actually still hold the lock. (check for session interruption,
|
172
|
+
# perhaps a lock is obtained in one method and handed to another)
|
173
|
+
#
|
174
|
+
# This, unlike {#locked?} will actually go and check the conditions
|
175
|
+
# that constitute "holding the lock" with the server.
|
176
|
+
#
|
177
|
+
# @raise [InterruptedSession] raised when the zk session has either
|
178
|
+
# closed or is in an invalid state.
|
179
|
+
#
|
180
|
+
# @raise [LockAssertionFailedError] raised if the lock is not held
|
181
|
+
#
|
182
|
+
# @example
|
183
|
+
#
|
184
|
+
# def process_jobs
|
185
|
+
# @lock.with_lock do
|
186
|
+
# @jobs.each do |j|
|
187
|
+
# @lock.assert!
|
188
|
+
# perform_job(j)
|
189
|
+
# end
|
190
|
+
# end
|
191
|
+
# end
|
192
|
+
#
|
193
|
+
# def perform_job(j)
|
194
|
+
# puts "hah! he thinks we're workin!"
|
195
|
+
# sleep(60)
|
196
|
+
# end
|
197
|
+
#
|
198
|
+
def assert!
|
199
|
+
raise LockAssertionFailedError, "have not obtained the lock yet" unless locked?
|
200
|
+
raise LockAssertionFailedError, "not connected" unless zk.connected?
|
201
|
+
raise LockAssertionFailedError, "lock_path was #{lock_path.inspect}" unless lock_path
|
202
|
+
raise LockAssertionFailedError, "the lock path #{lock_path} did not exist!" unless zk.exists?(lock_path)
|
203
|
+
raise LockAssertionFailedError, "we do not actually hold the lock" unless got_lock?
|
204
|
+
end
|
205
|
+
|
109
206
|
protected
|
110
|
-
# @private
|
111
207
|
def in_waiting_status
|
112
208
|
w, @waiting = @waiting, true
|
113
209
|
yield
|
@@ -115,46 +211,53 @@ module ZK
|
|
115
211
|
@waiting = w
|
116
212
|
end
|
117
213
|
|
118
|
-
# @private
|
119
214
|
def digit_from(path)
|
120
215
|
self.class.digit_from_lock_path(path)
|
121
216
|
end
|
122
217
|
|
123
|
-
#
|
218
|
+
# possibly lighter weight check to see if the lock path has any children
|
219
|
+
# (using stat, rather than getting the list of children).
|
220
|
+
def any_lock_children?
|
221
|
+
end
|
222
|
+
|
124
223
|
def lock_children(watch=false)
|
125
|
-
|
224
|
+
zk.children(root_lock_path, :watch => watch)
|
126
225
|
end
|
127
226
|
|
128
|
-
# @private
|
129
227
|
def ordered_lock_children(watch=false)
|
130
228
|
lock_children(watch).tap do |ary|
|
131
229
|
ary.sort! { |a,b| digit_from(a) <=> digit_from(b) }
|
132
230
|
end
|
133
231
|
end
|
134
232
|
|
135
|
-
# @private
|
136
233
|
def create_root_path!
|
137
|
-
|
234
|
+
zk.mkdir_p(@root_lock_path)
|
235
|
+
end
|
236
|
+
|
237
|
+
# performs the checks that (according to the recipe) mean that we hold
|
238
|
+
# the lock. used by (#assert!)
|
239
|
+
#
|
240
|
+
def got_lock?
|
241
|
+
raise NotImplementedError
|
138
242
|
end
|
139
243
|
|
140
244
|
# prefix is the string that will appear in front of the sequence num,
|
141
245
|
# defaults to 'lock'
|
142
246
|
#
|
143
|
-
# @private
|
144
247
|
def create_lock_path!(prefix='lock')
|
145
248
|
@lock_path = @zk.create("#{root_lock_path}/#{prefix}", "", :mode => :ephemeral_sequential)
|
146
249
|
logger.debug { "got lock path #{@lock_path}" }
|
147
250
|
@lock_path
|
148
|
-
rescue
|
251
|
+
rescue NoNode
|
149
252
|
create_root_path!
|
150
253
|
retry
|
151
254
|
end
|
152
255
|
|
153
|
-
# @private
|
154
256
|
def cleanup_lock_path!
|
155
257
|
logger.debug { "removing lock path #{@lock_path}" }
|
156
|
-
|
157
|
-
|
258
|
+
zk.delete(@lock_path)
|
259
|
+
zk.delete(root_lock_path) rescue NotEmpty
|
260
|
+
@lock_path = nil
|
158
261
|
end
|
159
262
|
end # LockerBase
|
160
263
|
end # Locker
|