zk 1.1.1 → 1.2.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/.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
|