zk 0.6.4
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 +7 -0
- data/Gemfile +10 -0
- data/Rakefile +13 -0
- data/lib/z_k/client.rb +906 -0
- data/lib/z_k/election.rb +411 -0
- data/lib/z_k/event_handler.rb +202 -0
- data/lib/z_k/event_handler_subscription.rb +29 -0
- data/lib/z_k/exceptions.rb +101 -0
- data/lib/z_k/extensions.rb +144 -0
- data/lib/z_k/locker.rb +254 -0
- data/lib/z_k/logging.rb +15 -0
- data/lib/z_k/message_queue.rb +143 -0
- data/lib/z_k/mongoid.rb +172 -0
- data/lib/z_k/pool.rb +254 -0
- data/lib/z_k/threadpool.rb +109 -0
- data/lib/z_k/version.rb +3 -0
- data/lib/z_k.rb +73 -0
- data/lib/zk.rb +2 -0
- data/spec/client_pool_spec.rb +329 -0
- data/spec/client_spec.rb +102 -0
- data/spec/election_spec.rb +301 -0
- data/spec/locker_spec.rb +386 -0
- data/spec/log4j.properties +17 -0
- data/spec/message_queue_spec.rb +55 -0
- data/spec/mongoid_spec.rb +330 -0
- data/spec/spec_helper.rb +96 -0
- data/spec/support/bogus_mongoid.rb +11 -0
- data/spec/support/queuey_thread.rb +11 -0
- data/spec/test_file.txt +4 -0
- data/spec/threadpool_spec.rb +71 -0
- data/spec/watch_spec.rb +118 -0
- data/spec/zookeeper_spec.rb +176 -0
- data/zk.gemspec +24 -0
- metadata +176 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
2
|
+
|
3
|
+
describe ZK::MessageQueue do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@zk = ZK.new("localhost:#{ZK_TEST_PORT}", :watcher => :default)
|
7
|
+
@zk2 = ZK.new("localhost:#{ZK_TEST_PORT}", :watcher => :default)
|
8
|
+
wait_until{ @zk.connected? && @zk2.connected? }
|
9
|
+
@queue_name = "_specQueue"
|
10
|
+
@consume_queue = @zk.queue(@queue_name)
|
11
|
+
@publish_queue = @zk2.queue(@queue_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
after(:each) do
|
15
|
+
@consume_queue.destroy!
|
16
|
+
@zk.close!
|
17
|
+
@zk2.close!
|
18
|
+
wait_until{ !@zk.connected? && !@zk2.connected? }
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should be able to receive a published message" do
|
22
|
+
message_received = false
|
23
|
+
@consume_queue.subscribe do |title, data|
|
24
|
+
data.should == 'mydata'
|
25
|
+
message_received = true
|
26
|
+
end
|
27
|
+
@publish_queue.publish("mydata")
|
28
|
+
wait_until {message_received }
|
29
|
+
message_received.should be_true
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should be able to receive a custom message title" do
|
33
|
+
message_title = false
|
34
|
+
@consume_queue.subscribe do |title, data|
|
35
|
+
title.should == 'title'
|
36
|
+
message_title = true
|
37
|
+
end
|
38
|
+
@publish_queue.publish("data", "title")
|
39
|
+
wait_until { message_title }
|
40
|
+
message_title.should be_true
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should work even after processing a message from before" do
|
44
|
+
@publish_queue.publish("data1", "title")
|
45
|
+
message_times = 0
|
46
|
+
@consume_queue.subscribe do |title, data|
|
47
|
+
title.should == "title"
|
48
|
+
message_times += 1
|
49
|
+
end
|
50
|
+
|
51
|
+
@publish_queue.publish("data2", "title")
|
52
|
+
wait_until { message_times == 2 }
|
53
|
+
message_times.should == 2
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,330 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
require 'tracer'
|
4
|
+
|
5
|
+
describe ZK::Mongoid::Locking do
|
6
|
+
before do
|
7
|
+
ZK::Mongoid::Locking.zk_lock_pool = ZK.new_pool('localhost:2181', :min_clients => 1, :max_clients => 5)
|
8
|
+
|
9
|
+
@doc = BogusMongoid.new
|
10
|
+
@other_doc = BogusMongoid.new
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
th = Thread.new do
|
15
|
+
ZK::Mongoid::Locking.zk_lock_pool.close_all!
|
16
|
+
end
|
17
|
+
|
18
|
+
unless th.join(5) == th
|
19
|
+
logger.warn { "Forcing pool closed!" }
|
20
|
+
ZK::Mongoid::Locking.zk_lock_pool.force_close!
|
21
|
+
th.join(5).should == th
|
22
|
+
end
|
23
|
+
|
24
|
+
ZK::Mongoid::Locking.zk_lock_pool = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
describe :with_shared_lock do
|
28
|
+
it %[should grab a shared lock] do
|
29
|
+
@lock_state = nil
|
30
|
+
|
31
|
+
th = Thread.new do
|
32
|
+
@doc.with_shared_lock do
|
33
|
+
@lock_state = @doc.locked_for_share?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
th.join_until { !@lock_state.nil? }
|
38
|
+
@lock_state.should_not be_nil
|
39
|
+
@lock_state.should be_true
|
40
|
+
end
|
41
|
+
|
42
|
+
it %[should allow another thread to enter the shared lock] do
|
43
|
+
@counter = 0
|
44
|
+
@queue = Queue.new
|
45
|
+
|
46
|
+
begin
|
47
|
+
@th1 = Thread.new do
|
48
|
+
@doc.with_shared_lock do
|
49
|
+
@counter += 1
|
50
|
+
@queue.pop
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
@th1.join_until { @counter > 0 }
|
55
|
+
@counter.should > 0
|
56
|
+
|
57
|
+
@th1.join_until { @queue.num_waiting > 0 }
|
58
|
+
@queue.num_waiting.should > 0
|
59
|
+
|
60
|
+
@th2 = Thread.new do
|
61
|
+
@other_doc.with_shared_lock do
|
62
|
+
@counter += 1
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
@th2.join_until { @counter == 2 }
|
67
|
+
@counter.should == 2
|
68
|
+
|
69
|
+
@th2.join(2).should == @th2
|
70
|
+
ensure
|
71
|
+
@queue << :unlock
|
72
|
+
|
73
|
+
unless @th1.join(2)
|
74
|
+
$stderr.puts "UH OH! @th1 IS HUNG!!"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it %[should block an exclusive lock from entering] do
|
80
|
+
begin
|
81
|
+
q1 = Queue.new
|
82
|
+
q2 = Queue.new
|
83
|
+
|
84
|
+
@got_exclusive_lock = nil
|
85
|
+
|
86
|
+
@th1 = Thread.new do
|
87
|
+
@doc.with_shared_lock do
|
88
|
+
q1 << :have_shared_lock
|
89
|
+
q2.pop
|
90
|
+
logger.debug { "@th1 releasing shared lock" }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
@th2 = Thread.new do
|
95
|
+
q1.pop
|
96
|
+
logger.debug { "@th1 has the shared lock" }
|
97
|
+
|
98
|
+
@other_doc.lock_for_update do
|
99
|
+
logger.debug { "@th2 got an exclusive lock" }
|
100
|
+
@got_exclusive_lock = true
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
@th1.join_until { q2.num_waiting >= 1 }
|
105
|
+
q2.num_waiting.should >= 1
|
106
|
+
|
107
|
+
@th2.join_until { q1.size == 0 }
|
108
|
+
q1.size.should be_zero
|
109
|
+
|
110
|
+
@got_exclusive_lock.should_not be_true
|
111
|
+
|
112
|
+
q2.enq(:release)
|
113
|
+
|
114
|
+
@th1.join_until { q2.size == 0 }
|
115
|
+
q2.size.should be_zero
|
116
|
+
|
117
|
+
@th2.join_until(5) { @got_exclusive_lock }
|
118
|
+
@got_exclusive_lock.should be_true
|
119
|
+
|
120
|
+
rescue Exception => e
|
121
|
+
$stderr.puts e.to_std_format
|
122
|
+
raise e
|
123
|
+
ensure
|
124
|
+
q2 << :release
|
125
|
+
|
126
|
+
unless @th1.join(2)
|
127
|
+
$stderr.puts "UH OH! @th1 IS HUNG!!"
|
128
|
+
end
|
129
|
+
|
130
|
+
unless @th2.join(2)
|
131
|
+
$stderr.puts "UH OH! @th2 IS HUNG!!"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe :lock_for_update do
|
138
|
+
it %[should be locked_for_update? inside the block] do
|
139
|
+
@lock_state = nil
|
140
|
+
|
141
|
+
th = Thread.new do
|
142
|
+
@doc.lock_for_update do
|
143
|
+
@lock_state = @doc.locked_for_update?
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
th.join_until { !@lock_state.nil? }
|
148
|
+
@lock_state.should_not be_nil
|
149
|
+
@lock_state.should be_true
|
150
|
+
end
|
151
|
+
|
152
|
+
it %[should allow the same thread to re-enter the lock] do
|
153
|
+
@counter = 0
|
154
|
+
|
155
|
+
th = Thread.new do
|
156
|
+
@doc.lock_for_update do
|
157
|
+
@counter += 1
|
158
|
+
logger.debug { "we are locked for update, trying to lock again" }
|
159
|
+
|
160
|
+
@doc.lock_for_update do
|
161
|
+
logger.debug { "locked again" }
|
162
|
+
@counter += 1
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
th.join_until { @counter >= 2 }
|
168
|
+
@counter.should == 2
|
169
|
+
end
|
170
|
+
|
171
|
+
it %[should block another thread from entering the lock] do
|
172
|
+
@counter = 0
|
173
|
+
queue = Queue.new
|
174
|
+
@other_doc_got_lock = false
|
175
|
+
|
176
|
+
th1 = Thread.new do
|
177
|
+
@doc.lock_for_update do
|
178
|
+
@counter += 1
|
179
|
+
queue.pop
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
th1.join_until { @counter == 1 }
|
184
|
+
@counter.should == 1
|
185
|
+
|
186
|
+
th1.zk_mongoid_lock_registry[:exclusive].should include(@doc.zk_lock_name)
|
187
|
+
|
188
|
+
th2 = Thread.new do
|
189
|
+
@other_doc.lock_for_update do
|
190
|
+
@other_doc_got_lock = true
|
191
|
+
@counter += 1
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
th2.join(0.1)
|
196
|
+
|
197
|
+
# this is not a deterministic check of whether or not th2 ran and did not
|
198
|
+
# get the lock but probably close enough
|
199
|
+
|
200
|
+
@counter.should == 1
|
201
|
+
@other_doc_got_lock.should == false
|
202
|
+
th2.zk_mongoid_lock_registry[:exclusive].should_not include(@other_doc.zk_lock_name)
|
203
|
+
|
204
|
+
queue << :release_lock
|
205
|
+
th1.join(5).should == th1
|
206
|
+
|
207
|
+
th2.join_until { @counter == 2 }
|
208
|
+
@counter.should == 2
|
209
|
+
@other_doc_got_lock.should be_true
|
210
|
+
end
|
211
|
+
|
212
|
+
describe :with_name do
|
213
|
+
before do
|
214
|
+
@queue = Queue.new
|
215
|
+
end
|
216
|
+
|
217
|
+
after do
|
218
|
+
if @queue.num_waiting > 0
|
219
|
+
@queue << :bogus
|
220
|
+
@th1.join(5).should == @th1
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
it %[should block another thread using the same name] do
|
225
|
+
@counter = 0
|
226
|
+
@queue = Queue.new
|
227
|
+
@other_doc_got_lock = false
|
228
|
+
@name = 'peanuts'
|
229
|
+
|
230
|
+
@th1 = Thread.new do
|
231
|
+
@doc.lock_for_update(@name) do
|
232
|
+
@counter += 1
|
233
|
+
@queue.pop
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
@th1.join_until { @counter == 1 }
|
238
|
+
@counter.should == 1
|
239
|
+
|
240
|
+
@th1.zk_mongoid_lock_registry[:exclusive].should include(@doc.zk_lock_name(@name))
|
241
|
+
|
242
|
+
@th2 = Thread.new do
|
243
|
+
@other_doc.lock_for_update(@name) do
|
244
|
+
@other_doc_got_lock = true
|
245
|
+
@counter += 1
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
@th2.join(0.1)
|
250
|
+
|
251
|
+
# this is not a deterministic check of whether or not @th2 ran and did not
|
252
|
+
# get the lock but probably close enough
|
253
|
+
|
254
|
+
@counter.should == 1
|
255
|
+
@other_doc_got_lock.should == false
|
256
|
+
|
257
|
+
@queue << :release_lock
|
258
|
+
@th1.join(5).should == @th1
|
259
|
+
|
260
|
+
@th2.join_until { @counter == 2 }
|
261
|
+
@counter.should == 2
|
262
|
+
@other_doc_got_lock.should be_true
|
263
|
+
end
|
264
|
+
|
265
|
+
it %[should not affect another thread using a different name] do
|
266
|
+
@counter = 0
|
267
|
+
@queue = Queue.new
|
268
|
+
@other_doc_got_lock = false
|
269
|
+
@name = 'peanuts'
|
270
|
+
|
271
|
+
@th1 = Thread.new do
|
272
|
+
@doc.lock_for_update(@name) do
|
273
|
+
@counter += 1
|
274
|
+
@queue.pop
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
@th1.join_until { @counter == 1 }
|
279
|
+
@counter.should == 1
|
280
|
+
|
281
|
+
@th1.zk_mongoid_lock_registry[:exclusive].should include(@doc.zk_lock_name(@name))
|
282
|
+
|
283
|
+
@th2 = Thread.new do
|
284
|
+
@other_doc.lock_for_update do
|
285
|
+
@other_doc_got_lock = true
|
286
|
+
@counter += 1
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
@th2.join_until { @other_doc_got_lock }
|
291
|
+
@other_doc_got_lock.should be_true
|
292
|
+
|
293
|
+
@counter.should == 2
|
294
|
+
|
295
|
+
@queue << :release_lock
|
296
|
+
@th1.join(2).should == @th1
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
describe :assert_locked_for_update! do
|
302
|
+
it %[should raise MustBeExclusivelyLockedException if the current thread does not hold the lock] do
|
303
|
+
lambda { @doc.assert_locked_for_update! }.should raise_error(ZK::Exceptions::MustBeExclusivelyLockedException)
|
304
|
+
end
|
305
|
+
|
306
|
+
it %[should not raise an exception if the current thread holds the lock] do
|
307
|
+
lambda do
|
308
|
+
@doc.lock_for_update do
|
309
|
+
@doc.assert_locked_for_update!
|
310
|
+
end
|
311
|
+
end.should_not raise_error
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
describe :assert_locked_for_share! do
|
316
|
+
it %[should raise MustBeShareLockedException if the current thread does not hold a shared lock] do
|
317
|
+
lambda { @doc.assert_locked_for_share! }.should raise_error(ZK::Exceptions::MustBeShareLockedException)
|
318
|
+
end
|
319
|
+
|
320
|
+
it %[should not raise an exception if the current thread holds a shared lock] do
|
321
|
+
lambda do
|
322
|
+
@doc.with_shared_lock do
|
323
|
+
@doc.assert_locked_for_share!
|
324
|
+
end
|
325
|
+
end.should_not raise_error
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
5
|
+
|
6
|
+
require 'zk'
|
7
|
+
require 'benchmark'
|
8
|
+
|
9
|
+
ZK_TEST_PORT = 2181
|
10
|
+
|
11
|
+
LOG_FILE = File.open(File.join(ZK::ZK_ROOT, 'test.log'), 'a').tap { |f| f.sync = true }
|
12
|
+
|
13
|
+
ZK.logger = Logger.new(LOG_FILE).tap { |log| log.level = Logger::DEBUG }
|
14
|
+
#Zookeeper.logger = ZK.logger
|
15
|
+
|
16
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
17
|
+
# in spec/support/ and its subdirectories.
|
18
|
+
Dir[File.expand_path("../support/**/*.rb", __FILE__)].each {|f| require f}
|
19
|
+
|
20
|
+
$stderr.sync = true
|
21
|
+
|
22
|
+
# COMMENT THESE LINES FOR REMOTE DEBUGGING
|
23
|
+
require 'ruby-debug'
|
24
|
+
require 'flexmock'
|
25
|
+
|
26
|
+
RSpec.configure do |config|
|
27
|
+
config.mock_with :flexmock
|
28
|
+
config.include(FlexMock::ArgumentTypes)
|
29
|
+
|
30
|
+
# config.before(:all) do
|
31
|
+
# unless $did_debug
|
32
|
+
# $did_debug = true
|
33
|
+
# $stderr.puts "debugger started? is #{Debugger.started?.inspect}"
|
34
|
+
|
35
|
+
# Debugger.wait_connection = true
|
36
|
+
# $stderr.puts "run 'rdebug -c -p #{Debugger::PORT}'"
|
37
|
+
# Debugger.start_remote
|
38
|
+
|
39
|
+
# config.debug = true
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
end
|
43
|
+
|
44
|
+
def logger
|
45
|
+
ZK.logger
|
46
|
+
end
|
47
|
+
|
48
|
+
# method to wait until block passed returns true or timeout (default is 2 seconds) is reached
|
49
|
+
def wait_until(timeout=2)
|
50
|
+
time_to_stop = Time.now + timeout
|
51
|
+
|
52
|
+
until yield
|
53
|
+
break if Time.now > time_to_stop
|
54
|
+
Thread.pass
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def wait_while(timeout=2)
|
59
|
+
time_to_stop = Time.now + timeout
|
60
|
+
|
61
|
+
while yield
|
62
|
+
break if Time.now > time_to_stop
|
63
|
+
Thread.pass
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
class ::Thread
|
69
|
+
# join with thread until given block is true, the thread joins successfully,
|
70
|
+
# or timeout seconds have passed
|
71
|
+
#
|
72
|
+
def join_until(timeout=2)
|
73
|
+
time_to_stop = Time.now + timeout
|
74
|
+
|
75
|
+
until yield
|
76
|
+
break if Time.now > time_to_stop
|
77
|
+
break if join(0.1)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def join_while(timeout=2)
|
82
|
+
time_to_stop = Time.now + timeout
|
83
|
+
|
84
|
+
while yield
|
85
|
+
break if Time.now > time_to_stop
|
86
|
+
break if join(0.1)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def report_realtime(what)
|
92
|
+
t = Benchmark.realtime { yield }
|
93
|
+
$stderr.puts "#{what}: %0.3f" % [t.to_f]
|
94
|
+
end
|
95
|
+
|
96
|
+
|
data/spec/test_file.txt
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
2
|
+
|
3
|
+
describe ZK::Threadpool do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@threadpool = ZK::Threadpool.new
|
7
|
+
end
|
8
|
+
|
9
|
+
after do
|
10
|
+
@threadpool.shutdown
|
11
|
+
end
|
12
|
+
|
13
|
+
describe :new do
|
14
|
+
it %[should be running] do
|
15
|
+
@threadpool.should be_running
|
16
|
+
end
|
17
|
+
|
18
|
+
it %[should use the default size] do
|
19
|
+
@threadpool.size.should == ZK::Threadpool.default_size
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
describe :defer do
|
25
|
+
it %[should run the given block on a thread in the threadpool] do
|
26
|
+
@th = nil
|
27
|
+
|
28
|
+
@threadpool.defer { @th = Thread.current }
|
29
|
+
|
30
|
+
wait_until(2) { @th }
|
31
|
+
|
32
|
+
@th.should_not == Thread.current
|
33
|
+
end
|
34
|
+
|
35
|
+
it %[should barf if the argument is not callable] do
|
36
|
+
bad_obj = mock(:not_callable)
|
37
|
+
bad_obj.should_not respond_to(:call)
|
38
|
+
|
39
|
+
lambda { @threadpool.defer(bad_obj) }.should raise_error(ArgumentError)
|
40
|
+
end
|
41
|
+
|
42
|
+
it %[should barf if the threadpool is not running] do
|
43
|
+
@threadpool.shutdown
|
44
|
+
lambda { @threadpool.defer { "hai!" } }.should raise_error(ZK::Exceptions::ThreadpoolIsNotRunningException)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe :shutdown do
|
49
|
+
it %[should set running to false] do
|
50
|
+
@threadpool.shutdown
|
51
|
+
@threadpool.should_not be_running
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe :start! do
|
56
|
+
it %[should be able to start a threadpool that had previously been shutdown (reuse)] do
|
57
|
+
@threadpool.shutdown
|
58
|
+
@threadpool.start!
|
59
|
+
|
60
|
+
@threadpool.should be_running
|
61
|
+
|
62
|
+
@rval = nil
|
63
|
+
|
64
|
+
@threadpool.defer { @rval = true }
|
65
|
+
wait_until(2) { @rval }
|
66
|
+
@rval.should be_true
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
data/spec/watch_spec.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
2
|
+
|
3
|
+
describe ZK do
|
4
|
+
describe do
|
5
|
+
before do
|
6
|
+
@cnx_str = "localhost:#{ZK_TEST_PORT}"
|
7
|
+
@zk = ZK.new(@cnx_str)
|
8
|
+
|
9
|
+
@path = "/_testWatch"
|
10
|
+
wait_until { @zk.connected? }
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
if @zk.connected?
|
15
|
+
@zk.close!
|
16
|
+
wait_until { !@zk.connected? }
|
17
|
+
end
|
18
|
+
|
19
|
+
ZK.open(@cnx_str) { |zk| zk.rm_rf(@path) }
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should call back to path registers" do
|
23
|
+
locker = Mutex.new
|
24
|
+
callback_called = false
|
25
|
+
|
26
|
+
@zk.watcher.register(@path) do |event|
|
27
|
+
locker.synchronize do
|
28
|
+
callback_called = true
|
29
|
+
end
|
30
|
+
event.path.should == @path
|
31
|
+
end
|
32
|
+
|
33
|
+
@zk.exists?(@path, :watch => true)
|
34
|
+
@zk.create(@path, "", :mode => :ephemeral)
|
35
|
+
|
36
|
+
wait_until(5) { locker.synchronize { callback_called } }
|
37
|
+
callback_called.should be_true
|
38
|
+
end
|
39
|
+
|
40
|
+
it %[should only deliver an event once to each watcher registered for exists?] do
|
41
|
+
events = []
|
42
|
+
|
43
|
+
sub = @zk.watcher.register(@path) do |ev|
|
44
|
+
logger.debug "got event #{ev}"
|
45
|
+
events << ev
|
46
|
+
end
|
47
|
+
|
48
|
+
2.times do
|
49
|
+
@zk.exists?(@path, :watch => true).should_not be_true
|
50
|
+
end
|
51
|
+
|
52
|
+
@zk.create(@path, '', :mode => :ephemeral)
|
53
|
+
|
54
|
+
wait_until { events.length >= 2 }
|
55
|
+
events.length.should == 1
|
56
|
+
end
|
57
|
+
|
58
|
+
it %[should only deliver an event once to each watcher registered for get] do
|
59
|
+
events = []
|
60
|
+
|
61
|
+
@zk.create(@path, 'one', :mode => :ephemeral)
|
62
|
+
|
63
|
+
sub = @zk.watcher.register(@path) do |ev|
|
64
|
+
logger.debug "got event #{ev}"
|
65
|
+
events << ev
|
66
|
+
end
|
67
|
+
|
68
|
+
2.times do
|
69
|
+
data, stat = @zk.get(@path, :watch => true)
|
70
|
+
data.should == 'one'
|
71
|
+
end
|
72
|
+
|
73
|
+
@zk.set(@path, 'two')
|
74
|
+
|
75
|
+
wait_until { events.length >= 2 }
|
76
|
+
events.length.should == 1
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
it %[should only deliver an event once to each watcher registered for children] do
|
81
|
+
events = []
|
82
|
+
|
83
|
+
@zk.create(@path, '')
|
84
|
+
|
85
|
+
sub = @zk.watcher.register(@path) do |ev|
|
86
|
+
logger.debug "got event #{ev}"
|
87
|
+
events << ev
|
88
|
+
end
|
89
|
+
|
90
|
+
2.times do
|
91
|
+
children = @zk.children(@path, :watch => true)
|
92
|
+
children.should be_empty
|
93
|
+
end
|
94
|
+
|
95
|
+
@zk.create("#{@path}/pfx", '', :mode => :ephemeral_sequential)
|
96
|
+
|
97
|
+
wait_until { events.length >= 2 }
|
98
|
+
events.length.should == 1
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'state watcher' do
|
103
|
+
before do
|
104
|
+
@event = nil
|
105
|
+
@cnx_str = "localhost:#{ZK_TEST_PORT}"
|
106
|
+
|
107
|
+
@zk = ZK.new(@cnx_str) do |zk|
|
108
|
+
@cnx_reg = zk.on_connected { |event| @event = event }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it %[should fire the registered callback] do
|
113
|
+
wait_while { @event.nil? }
|
114
|
+
@event.should_not be_nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|