zookeeper 1.1.3 → 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/CHANGELOG +10 -1
- data/Guardfile +3 -1
- data/ext/c_zookeeper.rb +7 -7
- data/ext/zookeeper_base.rb +26 -2
- data/java/java_base.rb +8 -0
- data/lib/zookeeper/client_methods.rb +10 -0
- data/lib/zookeeper/common.rb +3 -3
- data/lib/zookeeper/common/queue_with_pipe.rb +61 -22
- data/lib/zookeeper/continuation.rb +20 -34
- data/lib/zookeeper/version.rb +1 -1
- data/spec/forked_connection_spec.rb +18 -7
- metadata +5 -5
data/CHANGELOG
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
-
v1.
|
1
|
+
v1.2.0 Stop the World, I Wanna fork()
|
2
|
+
|
3
|
+
* changed pause/resume methods to pause_before_fork_in_parent
|
4
|
+
and resume_after_fork_in_parent to match their ZK counterparts
|
5
|
+
|
6
|
+
* replaced the Queue in QueueWithPipe (really have to change that name)
|
7
|
+
with an Array and Mutex/ConditionVariable pair. This allows us to
|
8
|
+
have better control over the shutdown signaling, and lets us resume
|
9
|
+
operations after a pause.
|
10
|
+
|
2
11
|
|
3
12
|
v1.1.1 Cleanup after code review (h/t @eric)
|
4
13
|
|
data/Guardfile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
|
2
|
+
guard 'rspec', :version => 2, :cli => '-c -f progress --fail-fast' do
|
2
3
|
watch(%r{^spec/.+_spec.rb$})
|
3
4
|
watch(%r{^lib/(.+)\.rb$}) { |m| %w[spec/zookeeper_spec.rb spec/chrooted_connection_spec.rb] }
|
4
5
|
watch(%r{^ext/zookeeper_c.bundle}) { %w[spec/c_zookeeper_spec.rb] }
|
6
|
+
watch(%r{^ext/zookeeper_base.rb}) { "spec" }
|
5
7
|
end
|
6
8
|
|
data/ext/c_zookeeper.rb
CHANGED
@@ -139,13 +139,13 @@ class CZookeeper
|
|
139
139
|
#
|
140
140
|
# requests may still be added during this time, but they will not be
|
141
141
|
# processed until you call resume
|
142
|
-
def
|
142
|
+
def pause_before_fork_in_parent
|
143
143
|
logger.debug { "#{self.class}##{__method__}" }
|
144
144
|
@mutex.synchronize { stop_event_thread }
|
145
145
|
end
|
146
146
|
|
147
147
|
# call this if 'pause' was previously called to start the event loop again
|
148
|
-
def
|
148
|
+
def resume_after_fork_in_parent
|
149
149
|
logger.debug { "#{self.class}##{__method__}" }
|
150
150
|
|
151
151
|
@mutex.synchronize do
|
@@ -184,7 +184,7 @@ class CZookeeper
|
|
184
184
|
@reg.lock
|
185
185
|
begin
|
186
186
|
if meth == :state
|
187
|
-
@reg.
|
187
|
+
@reg.state_check << cnt
|
188
188
|
else
|
189
189
|
@reg.pending << cnt
|
190
190
|
end
|
@@ -228,7 +228,7 @@ class CZookeeper
|
|
228
228
|
@running_cond.wait(timeout)
|
229
229
|
!!@_running
|
230
230
|
ensure
|
231
|
-
@mutex.unlock
|
231
|
+
@mutex.unlock rescue nil
|
232
232
|
end
|
233
233
|
end
|
234
234
|
|
@@ -314,7 +314,7 @@ class CZookeeper
|
|
314
314
|
|
315
315
|
raise ShuttingDownException if @_shutting_down
|
316
316
|
ensure
|
317
|
-
@mutex.unlock
|
317
|
+
@mutex.unlock rescue nil
|
318
318
|
end
|
319
319
|
end
|
320
320
|
|
@@ -334,7 +334,7 @@ class CZookeeper
|
|
334
334
|
@_running = true
|
335
335
|
@running_cond.broadcast
|
336
336
|
ensure
|
337
|
-
@mutex.unlock
|
337
|
+
@mutex.unlock rescue nil
|
338
338
|
end
|
339
339
|
end
|
340
340
|
|
@@ -343,7 +343,7 @@ class CZookeeper
|
|
343
343
|
begin
|
344
344
|
@connected_cond.broadcast
|
345
345
|
ensure
|
346
|
-
@mutex.unlock
|
346
|
+
@mutex.unlock rescue nil
|
347
347
|
end
|
348
348
|
end
|
349
349
|
end
|
data/ext/zookeeper_base.rb
CHANGED
@@ -32,7 +32,7 @@ class ZookeeperBase
|
|
32
32
|
|
33
33
|
|
34
34
|
def_delegators :@czk, :get_children, :exists, :delete, :get, :set,
|
35
|
-
:set_acl, :get_acl, :client_id, :sync, :wait_until_connected
|
35
|
+
:set_acl, :get_acl, :client_id, :sync, :wait_until_connected
|
36
36
|
|
37
37
|
# some state methods need to be more paranoid about locking to ensure the correct
|
38
38
|
# state is returned
|
@@ -117,7 +117,8 @@ class ZookeeperBase
|
|
117
117
|
# if either of these happen, the user will need to renegotiate a connection via reopen
|
118
118
|
def assert_open
|
119
119
|
@mutex.synchronize do
|
120
|
-
raise Exceptions::
|
120
|
+
raise Exceptions::SessionExpired if state == ZOO_EXPIRED_SESSION_STATE
|
121
|
+
raise Exceptions::NotConnected unless connected?
|
121
122
|
if forked?
|
122
123
|
raise InheritedConnectionError, <<-EOS.gsub(/(?:^|\n)\s*/, ' ').strip
|
123
124
|
You tried to use a connection inherited from another process
|
@@ -193,6 +194,29 @@ class ZookeeperBase
|
|
193
194
|
@mutex.synchronize { !@czk or @czk.closed? }
|
194
195
|
end
|
195
196
|
|
197
|
+
def pause_before_fork_in_parent
|
198
|
+
@mutex.synchronize do
|
199
|
+
logger.debug { "ZookeeperBase#pause_before_fork_in_parent" }
|
200
|
+
|
201
|
+
# XXX: add anal-retentive state checking
|
202
|
+
raise "EXPLODERATE! @czk was nil!" unless @czk
|
203
|
+
|
204
|
+
@czk.pause_before_fork_in_parent
|
205
|
+
stop_dispatch_thread!
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def resume_after_fork_in_parent
|
210
|
+
@mutex.synchronize do
|
211
|
+
logger.debug { "ZookeeperBase#resume_after_fork_in_parent" }
|
212
|
+
|
213
|
+
raise "EXPLODERATE! @czk was nil!" unless @czk
|
214
|
+
|
215
|
+
event_queue.open
|
216
|
+
setup_dispatch_thread!
|
217
|
+
@czk.resume_after_fork_in_parent
|
218
|
+
end
|
219
|
+
end
|
196
220
|
|
197
221
|
protected
|
198
222
|
# this is a hack: to provide consistency between the C and Java drivers when
|
data/java/java_base.rb
CHANGED
@@ -435,6 +435,14 @@ class JavaBase
|
|
435
435
|
@connected_latch.release
|
436
436
|
end
|
437
437
|
|
438
|
+
def pause_before_fork_in_parent
|
439
|
+
# this is a no-op in java-land
|
440
|
+
end
|
441
|
+
|
442
|
+
def resume_after_fork_in_parent
|
443
|
+
# this is a no-op in java-land
|
444
|
+
end
|
445
|
+
|
438
446
|
protected
|
439
447
|
def jzk
|
440
448
|
@mutex.synchronize { @jzk }
|
@@ -192,6 +192,16 @@ module ClientMethods
|
|
192
192
|
super
|
193
193
|
end
|
194
194
|
|
195
|
+
# stop all underlying threads in preparation for a fork()
|
196
|
+
def pause_before_fork_in_parent
|
197
|
+
super
|
198
|
+
end
|
199
|
+
|
200
|
+
# re-start all underlying threads after performing a fork()
|
201
|
+
def resume_after_fork_in_parent
|
202
|
+
super
|
203
|
+
end
|
204
|
+
|
195
205
|
protected
|
196
206
|
# used during shutdown, awaken the event delivery thread if it's blocked
|
197
207
|
# waiting for the next event
|
data/lib/zookeeper/common.rb
CHANGED
@@ -94,7 +94,7 @@ protected
|
|
94
94
|
#
|
95
95
|
# @dispatcher will be nil when this method exits
|
96
96
|
#
|
97
|
-
def stop_dispatch_thread!
|
97
|
+
def stop_dispatch_thread!(timeout=2)
|
98
98
|
logger.debug { "#{self.class}##{__method__}" }
|
99
99
|
|
100
100
|
if @dispatcher
|
@@ -111,8 +111,8 @@ protected
|
|
111
111
|
#
|
112
112
|
@dispatch_shutdown_cond.wait
|
113
113
|
|
114
|
-
# wait for another
|
115
|
-
until @dispatcher.join(
|
114
|
+
# wait for another timeout sec for the thread to join
|
115
|
+
until @dispatcher.join(timeout)
|
116
116
|
logger.error { "Dispatch thread did not join cleanly, waiting" }
|
117
117
|
end
|
118
118
|
@dispatcher = nil
|
@@ -5,8 +5,6 @@ module Common
|
|
5
5
|
extend Forwardable
|
6
6
|
include Logger
|
7
7
|
|
8
|
-
def_delegators :@queue, :clear
|
9
|
-
|
10
8
|
# raised when close has been called, and pop() is performed
|
11
9
|
#
|
12
10
|
class ShutdownException < StandardError; end
|
@@ -15,57 +13,98 @@ module Common
|
|
15
13
|
KILL_TOKEN = Object.new unless defined?(KILL_TOKEN)
|
16
14
|
|
17
15
|
def initialize
|
18
|
-
@
|
16
|
+
@array = []
|
19
17
|
|
20
|
-
@mutex
|
21
|
-
@
|
18
|
+
@mutex = Mutex.new
|
19
|
+
@cond = ConditionVariable.new
|
20
|
+
@closed = false
|
22
21
|
@graceful = false
|
23
22
|
end
|
24
23
|
|
24
|
+
def clear
|
25
|
+
@mutex.lock
|
26
|
+
begin
|
27
|
+
@array.clear
|
28
|
+
ensure
|
29
|
+
@mutex.unlock rescue nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
25
33
|
def push(obj)
|
26
|
-
|
27
|
-
|
34
|
+
@mutex.lock
|
35
|
+
begin
|
36
|
+
# raise ShutdownException if (@closed or @graceful)
|
37
|
+
@array << obj
|
38
|
+
@cond.signal
|
39
|
+
ensure
|
40
|
+
@mutex.unlock rescue nil
|
41
|
+
end
|
28
42
|
end
|
29
43
|
|
30
44
|
def pop(non_blocking=false)
|
31
|
-
|
45
|
+
rval = nil
|
32
46
|
|
33
|
-
|
47
|
+
@mutex.lock
|
48
|
+
begin
|
34
49
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
50
|
+
begin
|
51
|
+
raise ShutdownException if @closed # this may get us in trouble
|
52
|
+
|
53
|
+
rval = @array.shift
|
54
|
+
|
55
|
+
unless rval
|
56
|
+
raise ThreadError if non_blocking # sigh, ruby's stupid behavior
|
57
|
+
raise ShutdownException if @graceful # we've processed all the remaining mesages
|
58
|
+
|
59
|
+
@cond.wait(@mutex) until (@closed or @graceful or (@array.length > 0))
|
60
|
+
end
|
61
|
+
end until rval
|
39
62
|
|
40
|
-
|
63
|
+
return rval
|
64
|
+
|
65
|
+
ensure
|
66
|
+
@mutex.unlock rescue nil
|
67
|
+
end
|
41
68
|
end
|
42
69
|
|
43
70
|
# close the queue and causes ShutdownException to be raised on waiting threads
|
44
71
|
def graceful_close!
|
45
|
-
@mutex.
|
72
|
+
@mutex.lock
|
73
|
+
begin
|
46
74
|
return if @graceful or @closed
|
47
75
|
logger.debug { "#{self.class}##{__method__} gracefully closing" }
|
48
76
|
@graceful = true
|
49
|
-
|
77
|
+
@cond.broadcast
|
78
|
+
ensure
|
79
|
+
@mutex.unlock rescue nil
|
50
80
|
end
|
51
81
|
nil
|
52
82
|
end
|
53
83
|
|
84
|
+
def open
|
85
|
+
@mutex.lock
|
86
|
+
begin
|
87
|
+
@closed = @graceful = false
|
88
|
+
@cond.broadcast
|
89
|
+
ensure
|
90
|
+
@mutex.unlock rescue nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
54
94
|
def close
|
55
|
-
@mutex.
|
95
|
+
@mutex.lock
|
96
|
+
begin
|
56
97
|
return if @closed
|
57
98
|
@closed = true
|
99
|
+
@cond.broadcast
|
100
|
+
ensure
|
101
|
+
@mutex.unlock rescue nil
|
58
102
|
end
|
59
103
|
end
|
60
104
|
|
61
105
|
def closed?
|
62
106
|
@mutex.synchronize { !!@closed }
|
63
107
|
end
|
64
|
-
|
65
|
-
private
|
66
|
-
def clear_reads_on_pop?
|
67
|
-
@clear_reads_on_pop
|
68
|
-
end
|
69
108
|
end
|
70
109
|
end
|
71
110
|
end
|
@@ -12,13 +12,13 @@ module Zookeeper
|
|
12
12
|
# `state_check` are high-priority checks that query the connection about
|
13
13
|
# its current state, they always run before other continuations
|
14
14
|
#
|
15
|
-
class Registry < Struct.new(:pending, :in_flight)
|
15
|
+
class Registry < Struct.new(:pending, :state_check, :in_flight)
|
16
16
|
extend Forwardable
|
17
17
|
|
18
18
|
def_delegators :@mutex, :lock, :unlock
|
19
19
|
|
20
20
|
def initialize
|
21
|
-
super([], {})
|
21
|
+
super([], [], {})
|
22
22
|
@mutex = Mutex.new
|
23
23
|
end
|
24
24
|
|
@@ -33,7 +33,7 @@ module Zookeeper
|
|
33
33
|
|
34
34
|
# does not lock the mutex, returns true if there are pending jobs
|
35
35
|
def anything_to_do?
|
36
|
-
|
36
|
+
(pending.length + state_check.length) > 0
|
37
37
|
end
|
38
38
|
|
39
39
|
# returns the pending continuations, resetting the list
|
@@ -41,7 +41,7 @@ module Zookeeper
|
|
41
41
|
def next_batch()
|
42
42
|
@mutex.lock
|
43
43
|
begin
|
44
|
-
pending.slice!(0,pending.length)
|
44
|
+
state_check.slice!(0, state_check.length) + pending.slice!(0,pending.length)
|
45
45
|
ensure
|
46
46
|
@mutex.unlock rescue nil
|
47
47
|
end
|
@@ -82,9 +82,6 @@ module Zookeeper
|
|
82
82
|
@mutex = Mutex.new
|
83
83
|
@cond = ConditionVariable.new
|
84
84
|
@rval = nil
|
85
|
-
|
86
|
-
# make this error reporting more robust if necessary, right now, just set to state
|
87
|
-
@error = nil
|
88
85
|
|
89
86
|
# set to true when an event occurs that would cause the caller to
|
90
87
|
# otherwise block forever
|
@@ -93,17 +90,9 @@ module Zookeeper
|
|
93
90
|
|
94
91
|
# the caller calls this method and receives the response from the async loop
|
95
92
|
def value
|
96
|
-
@mutex.
|
97
|
-
|
98
|
-
|
99
|
-
case @error
|
100
|
-
when nil
|
101
|
-
# ok, nothing to see here, carry on
|
102
|
-
when ZOO_EXPIRED_SESSION_STATE
|
103
|
-
raise Exceptions::SessionExpired, "connection has expired"
|
104
|
-
else
|
105
|
-
raise Exceptions::NotConnected, "connection state is #{STATE_NAMES[@error]}"
|
106
|
-
end
|
93
|
+
@mutex.lock
|
94
|
+
begin
|
95
|
+
@cond.wait(@mutex) until @rval
|
107
96
|
|
108
97
|
case @rval.length
|
109
98
|
when 1
|
@@ -111,6 +100,8 @@ module Zookeeper
|
|
111
100
|
else
|
112
101
|
return @rval
|
113
102
|
end
|
103
|
+
ensure
|
104
|
+
@mutex.unlock rescue nil
|
114
105
|
end
|
115
106
|
end
|
116
107
|
|
@@ -133,24 +124,13 @@ module Zookeeper
|
|
133
124
|
# implementation, but it's more important to get *something* working and
|
134
125
|
# passing specs, then refactor to make everything sane
|
135
126
|
#
|
136
|
-
#
|
137
127
|
def submit(czk)
|
138
|
-
state = czk.zkrb_state # check the state of the connection
|
139
|
-
|
140
|
-
if @meth == :state # if the method is a state call
|
141
|
-
@rval = [state] # we're done, no error
|
142
|
-
return deliver!
|
143
|
-
|
144
|
-
elsif state != ZOO_CONNECTED_STATE # otherwise, we must be connected
|
145
|
-
@error = state # so set the error
|
146
|
-
return deliver! # and we're out
|
147
|
-
end
|
148
|
-
|
149
128
|
rc, *_ = czk.__send__(:"zkrb_#{@meth}", *async_args)
|
150
129
|
|
151
|
-
if
|
152
|
-
|
153
|
-
|
130
|
+
# if this is an state call, async call, or we failed to submit it
|
131
|
+
if (@meth == :state) or user_callback? or (rc != ZOK)
|
132
|
+
@rval = [rc] # create the repsonse
|
133
|
+
deliver! # wake the caller and we're out
|
154
134
|
end
|
155
135
|
end
|
156
136
|
|
@@ -172,6 +152,9 @@ module Zookeeper
|
|
172
152
|
|
173
153
|
logger.debug { "async_args, meth: #{meth} ary: #{ary.inspect}, #{callback_arg_idx}" }
|
174
154
|
|
155
|
+
# this is not already an async call
|
156
|
+
# so we replace the req_id with the ZKRB_ASYNC_CONTN_ID so the
|
157
|
+
# event thread knows to dispatch it itself
|
175
158
|
ary[callback_arg_idx] ||= self
|
176
159
|
|
177
160
|
ary
|
@@ -182,8 +165,11 @@ module Zookeeper
|
|
182
165
|
end
|
183
166
|
|
184
167
|
def deliver!
|
185
|
-
@mutex.
|
168
|
+
@mutex.lock
|
169
|
+
begin
|
186
170
|
@cond.signal
|
171
|
+
ensure
|
172
|
+
@mutex.unlock rescue nil
|
187
173
|
end
|
188
174
|
end
|
189
175
|
end # Base
|
data/lib/zookeeper/version.rb
CHANGED
@@ -14,8 +14,15 @@ unless defined?(::JRUBY_VERSION)
|
|
14
14
|
false
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
LBORDER = ('-' * 35) << '< '
|
18
|
+
RBORDER = ' >' << ('-' * 35)
|
19
|
+
|
20
|
+
def mark(thing)
|
21
|
+
logger << "\n#{LBORDER}#{thing}#{RBORDER}\n\n"
|
22
|
+
end
|
18
23
|
|
24
|
+
before do
|
25
|
+
mark "BEFORE: START"
|
19
26
|
if defined?(::Rubinius)
|
20
27
|
pending("this test is currently broken in rbx")
|
21
28
|
# elsif ENV['TRAVIS']
|
@@ -24,11 +31,12 @@ unless defined?(::JRUBY_VERSION)
|
|
24
31
|
@zk = Zookeeper.new(connection_string)
|
25
32
|
rm_rf(@zk, path)
|
26
33
|
end
|
27
|
-
|
34
|
+
mark "BEFORE: END"
|
28
35
|
end
|
29
36
|
|
30
37
|
after do
|
31
|
-
|
38
|
+
mark "AFTER: START"
|
39
|
+
|
32
40
|
if @pid and process_alive?(@pid)
|
33
41
|
begin
|
34
42
|
Process.kill('KILL', @pid)
|
@@ -39,6 +47,8 @@ unless defined?(::JRUBY_VERSION)
|
|
39
47
|
|
40
48
|
@zk.close if @zk and !@zk.closed?
|
41
49
|
with_open_zk(connection_string) { |z| rm_rf(z, path) }
|
50
|
+
|
51
|
+
mark "AFTER: END"
|
42
52
|
end
|
43
53
|
|
44
54
|
def wait_for_child_safely(pid, timeout=5)
|
@@ -56,7 +66,8 @@ unless defined?(::JRUBY_VERSION)
|
|
56
66
|
end
|
57
67
|
|
58
68
|
it %[should do the right thing and not fail] do
|
59
|
-
|
69
|
+
mark "TEST: START"
|
70
|
+
|
60
71
|
@zk.wait_until_connected
|
61
72
|
|
62
73
|
mkdir_p(@zk, pids_root)
|
@@ -75,9 +86,9 @@ unless defined?(::JRUBY_VERSION)
|
|
75
86
|
|
76
87
|
@zk.stat(:path => "#{pids_root}/child", :watcher => cb)
|
77
88
|
|
78
|
-
|
89
|
+
@zk.pause_before_fork_in_parent
|
79
90
|
|
80
|
-
|
91
|
+
mark "FORK"
|
81
92
|
|
82
93
|
@pid = fork do
|
83
94
|
logger.debug { "reopening connection in child: #{$$}" }
|
@@ -91,7 +102,7 @@ unless defined?(::JRUBY_VERSION)
|
|
91
102
|
exit!(0)
|
92
103
|
end
|
93
104
|
|
94
|
-
@zk.
|
105
|
+
@zk.resume_after_fork_in_parent
|
95
106
|
|
96
107
|
event_waiter_th = Thread.new do
|
97
108
|
@latch.await(5) unless @event
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zookeeper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 1.
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 1.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Phillip Pearson
|
@@ -20,7 +20,7 @@ autorequire:
|
|
20
20
|
bindir: bin
|
21
21
|
cert_chain: []
|
22
22
|
|
23
|
-
date: 2012-05-
|
23
|
+
date: 2012-05-18 00:00:00 Z
|
24
24
|
dependencies:
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: backports
|