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.
@@ -0,0 +1,301 @@
1
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
2
+
3
+ describe ZK::Election do
4
+ before do
5
+ ZK.open('localhost:2181') do |cnx|
6
+ ZK.logger.debug { "REMOVING /_zkelection" }
7
+ cnx.rm_rf('/_zkelection')
8
+ end
9
+
10
+ @zk = ZK.new('localhost:2181')
11
+ @zk2 = ZK.new('localhost:2181')
12
+ @election_name = '2012'
13
+ @data1 = 'obama'
14
+ @data2 = 'palin'
15
+ end
16
+
17
+ after do
18
+ @zk.close!
19
+ @zk2.close!
20
+
21
+ ZK.open('localhost:2181') { |cnx| cnx.rm_rf('/_zkelection') }
22
+ end
23
+
24
+ describe 'Candidate', 'following next_node' do
25
+ before do
26
+ @obama = ZK::Election::Candidate.new(@zk, @election_name, :data => @data1)
27
+ @palin = ZK::Election::Candidate.new(@zk2, @election_name, :data => @data2)
28
+ end
29
+
30
+ describe 'vote!' do
31
+ describe 'loser' do
32
+ it %[should wait until the leader has acked before firing loser callbacks] do
33
+ queue = Queue.new
34
+ @do_ack = false
35
+
36
+ @obama_won = nil
37
+ @palin_lost = nil
38
+
39
+ @obama_waiting = nil
40
+
41
+ @obama.on_winning_election do
42
+ @obama_waiting = true
43
+
44
+ # wait for us to signal
45
+ queue.pop
46
+
47
+ $stderr.puts "obama on_winning_election entered"
48
+ @obama_won = true
49
+ end
50
+
51
+ @palin.on_losing_election do
52
+ @obama_won.should be_true
53
+ @palin.leader_acked?.should be_true
54
+ @palin_lost = true
55
+ end
56
+
57
+ oth = Thread.new do
58
+ @obama.vote!
59
+ @palin.vote!
60
+ end
61
+ oth.run
62
+
63
+ wait_until { @obama_waiting }
64
+ @obama_waiting.should be_true
65
+
66
+ # palin's callbacks haven't fired
67
+ @palin_lost.should be_nil
68
+
69
+ queue << :ok
70
+
71
+ wait_until { @obama_won }
72
+ @obama_won.should be_true
73
+
74
+ lambda { oth.join(1).should == oth }.should_not raise_error
75
+
76
+ wait_until { @palin_lost }
77
+
78
+ @palin_lost.should be_true
79
+ end
80
+ end
81
+
82
+ describe do
83
+ before do
84
+ @obama_won = @obama_lost = @palin_won = @palin_lost = nil
85
+
86
+ @obama.on_winning_election do
87
+ logger.debug { "obama on_winning_election fired" }
88
+ @obama_won = true
89
+ end
90
+
91
+ @obama.on_losing_election do
92
+ logger.debug { "obama on_losing_election fired" }
93
+ @obama_lost = true
94
+ end
95
+
96
+ @palin.on_winning_election do
97
+ logger.debug { "palin on_winning_election fired" }
98
+ @palin_won = true
99
+ end
100
+
101
+ @palin.on_losing_election do
102
+ logger.debug { "palin on_losing_election fired" }
103
+ @palin_lost = true
104
+ end
105
+
106
+ @obama.vote!
107
+ @palin.vote!
108
+
109
+ wait_until { @obama_won }
110
+ wait_until { @palin_lost }
111
+ end
112
+
113
+ describe 'winner' do
114
+ it %[should fire the on_winning_election callbacks] do
115
+ @obama_won.should be_true
116
+ end
117
+
118
+ it %[should not fire the on_losing_election callbacks] do
119
+ @obama_lost.should be_nil
120
+ end
121
+
122
+ it %[should acknowledge completion of winning callbacks] do
123
+ @zk.exists?(@obama.leader_ack_path).should be_true
124
+ end
125
+
126
+ it %[should write its data to the leader_ack node] do
127
+ @zk.get(@obama.leader_ack_path).first.should == @data1
128
+ end
129
+
130
+ it %[should know it's the leader] do
131
+ @obama.should be_leader
132
+ end
133
+ end
134
+
135
+ describe 'loser' do # gets a talk show on Fox News? I KEED! I KEED!
136
+ it %[should know it isn't the leader] do
137
+ @palin.should_not be_leader
138
+ end
139
+
140
+ it %[should not fire the winning callbacks] do
141
+ @palin_won.should_not be_true
142
+ end
143
+
144
+ it %[should fire the losing callbacks] do
145
+ @palin_lost.should be_true
146
+ end
147
+
148
+ it %[should take over as leader when the current leader goes away] do
149
+ @obama.zk.close!
150
+ wait_until { @palin_won }
151
+
152
+ @palin_won.should be_true # god forbid
153
+
154
+ wait_until { @zk2.exists?(@palin.leader_ack_path) }
155
+
156
+ @zk2.exists?(@palin.leader_ack_path).should be_true
157
+
158
+ @zk2.get(@palin.leader_ack_path).first.should == @data2
159
+ end
160
+
161
+ it %[should remain leader if the original leader comes back] do
162
+ @obama.zk.close!
163
+ wait_until { @palin_won }
164
+
165
+ zk = ZK.new('localhost:2181')
166
+ newbama = ZK::Election::Candidate.new(zk, @election_name, :data => @data1)
167
+
168
+ win_again = false
169
+
170
+ newbama.on_winning_election do
171
+ win_again = true
172
+ end
173
+
174
+ newbama.vote!
175
+ wait_until { newbama.voted? }
176
+
177
+ newbama.should be_voted
178
+ win_again.should be_false
179
+ newbama.should_not be_leader
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+
186
+ describe :Observer do
187
+ before do
188
+ @zk3 = ZK.new('localhost:2181')
189
+
190
+ @zk3.exists?('/_zkelection/2012/leader_ack').should be_false
191
+
192
+ @obama = ZK::Election::Candidate.new(@zk, @election_name, :data => @data1)
193
+ @palin = ZK::Election::Candidate.new(@zk2, @election_name, :data => @data2)
194
+
195
+ @zk3.exists?('/_zkelection/2012/leader_ack').should be_false
196
+
197
+ @observer = ZK::Election::Observer.new(@zk3, @election_name)
198
+ end
199
+
200
+ after do
201
+ @zk3.close!
202
+ end
203
+
204
+ describe 'initial state' do
205
+ describe 'no leader' do
206
+ before do
207
+ @events = []
208
+
209
+ @observer.on_leaders_death { @events << :death }
210
+ @observer.on_new_leader { @events << :life }
211
+
212
+ @observer.observe!
213
+ wait_until { !@observer.leader_alive.nil? }
214
+ @observer.leader_alive.should_not be_nil
215
+ @zk3.exists?(@observer.root_election_node).should be_false
216
+ end
217
+
218
+ it %[should set leader_alive to false] do
219
+ @observer.leader_alive.should be_false
220
+ end
221
+
222
+ it %[should fire death callbacks] do
223
+ @events.length.should == 1
224
+ @events.first.should == :death
225
+ end
226
+ end
227
+
228
+ describe 'leader exists before' do
229
+ before do
230
+ @obama.vote!
231
+ @palin.vote!
232
+
233
+ wait_until { @obama.leader? }
234
+
235
+ @got_life_event = @got_death_event = false
236
+
237
+ @observer.on_leaders_death { @got_death_event = true }
238
+ @observer.on_new_leader { @got_life_event = true }
239
+
240
+ @observer.observe!
241
+
242
+ wait_until { !@observer.leader_alive.nil? }
243
+ end
244
+
245
+ it %[should be obama that won] do
246
+ @obama.should be_leader
247
+ end
248
+
249
+ it %[should be palin that lost] do
250
+ @palin.should_not be_leader
251
+ end
252
+
253
+ it %[should set leader_alive to true] do
254
+ @observer.leader_alive.should be_true
255
+ end
256
+
257
+ it %[should fire the new leader callbacks] do
258
+ @got_life_event.should be_true
259
+ end
260
+ end
261
+
262
+ describe 'leadership transition' do
263
+ before do
264
+ @obama.vote!
265
+ @palin.vote!
266
+
267
+ wait_until { @obama.leader? }
268
+
269
+ @palin.should_not be_leader
270
+
271
+ @got_life_event = @got_death_event = false
272
+
273
+ @observer.on_leaders_death { @got_death_event = true }
274
+ @observer.on_new_leader { @got_life_event = true }
275
+
276
+ @observer.observe!
277
+
278
+ wait_until { !@observer.leader_alive.nil? }
279
+
280
+ @observer.leader_alive.should be_true
281
+ @zk.close!
282
+ wait_until { !@zk.connected? && @palin.leader? && @palin.leader_acked? }
283
+ end
284
+
285
+ it %[should be palin who is leader] do
286
+ @palin.should be_leader
287
+ end
288
+
289
+ it %[should have seen both the death and life events] do
290
+ @got_life_event.should be_true
291
+ @got_death_event.should be_true
292
+ end
293
+
294
+ it %[should see the data of the new leader] do
295
+ @observer.leader_data.should == 'palin'
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end
301
+
@@ -0,0 +1,386 @@
1
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
2
+
3
+ # this is a remnant of the old Locker class, but a good test of what's expected
4
+ # from ZK::Client#locker
5
+ #
6
+ describe 'ZK::Client#locker' do
7
+
8
+ before(:each) do
9
+ @zk = ZK.new("localhost:#{ZK_TEST_PORT}")
10
+ @zk2 = ZK.new("localhost:#{ZK_TEST_PORT}")
11
+ @zk3 = ZK.new("localhost:#{ZK_TEST_PORT}")
12
+ @connections = [@zk, @zk2, @zk3]
13
+ wait_until { @connections.all? { |c| c.connected? } }
14
+ @path_to_lock = "/lock_tester"
15
+ end
16
+
17
+ after(:each) do
18
+ @zk.close!
19
+ @zk2.close!
20
+ @zk3.close!
21
+ wait_until{ @connections.all? { |c| !c.connected? } }
22
+ end
23
+
24
+ it "should be able to acquire the lock if no one else is locking it" do
25
+ @zk.locker(@path_to_lock).lock!.should be_true
26
+ end
27
+
28
+ it "should not be able to acquire the lock if someone else is locking it" do
29
+ @zk.locker(@path_to_lock).lock!.should be_true
30
+ @zk2.locker(@path_to_lock).lock!.should be_false
31
+ end
32
+
33
+ it "should be able to acquire the lock after the first one releases it" do
34
+ lock1 = @zk.locker(@path_to_lock)
35
+ lock2 = @zk2.locker(@path_to_lock)
36
+
37
+ lock1.lock!.should be_true
38
+ lock2.lock!.should be_false
39
+ lock1.unlock!
40
+ lock2.lock!.should be_true
41
+ end
42
+
43
+ it "should be able to acquire the lock if the first locker goes away" do
44
+ lock1 = @zk.locker(@path_to_lock)
45
+ lock2 = @zk2.locker(@path_to_lock)
46
+
47
+ lock1.lock!.should be_true
48
+ lock2.lock!.should be_false
49
+ @zk.close!
50
+ lock2.lock!.should be_true
51
+ end
52
+
53
+ it "should be able to handle multi part path locks" do
54
+ @zk.locker("my/multi/part/path").lock!.should be_true
55
+ end
56
+
57
+ it "should blocking lock" do
58
+ array = []
59
+ first_lock = @zk.locker("mylock")
60
+ first_lock.lock!.should be_true
61
+ array << :first_lock
62
+
63
+ thread = Thread.new do
64
+ @zk.locker("mylock").with_lock do
65
+ array << :second_lock
66
+ end
67
+ array.length.should == 2
68
+ end
69
+
70
+ array.length.should == 1
71
+ first_lock.unlock!
72
+ thread.join(10)
73
+ array.length.should == 2
74
+ end
75
+ end
76
+
77
+ describe ZK::Locker do
78
+ before do
79
+ @zk = ZK.new("localhost:#{ZK_TEST_PORT}", :watcher => :default)
80
+ @zk2 = ZK.new("localhost:#{ZK_TEST_PORT}", :watcher => :default)
81
+ @zk3 = ZK.new("localhost:#{ZK_TEST_PORT}")
82
+ @connections = [@zk, @zk2, @zk3]
83
+
84
+ wait_until{ @connections.all? {|c| c.connected?} }
85
+
86
+ @path = "shlock"
87
+ @root_lock_path = "/_zklocking/#{@path}"
88
+ end
89
+
90
+ after do
91
+ @connections.each { |c| c.close! }
92
+ wait_until { @connections.all? { |c| !c.connected? } }
93
+ end
94
+
95
+
96
+ describe :SharedLocker do
97
+ before do
98
+ @shared_locker = ZK::Locker.shared_locker(@zk, @path)
99
+ @shared_locker2 = ZK::Locker.shared_locker(@zk2, @path)
100
+ end
101
+
102
+ describe :lock! do
103
+ describe 'non-blocking success' do
104
+ before do
105
+ @rval = @shared_locker.lock!
106
+ @rval2 = @shared_locker2.lock!
107
+ end
108
+
109
+ it %[should acquire the first lock] do
110
+ @rval.should be_true
111
+ @shared_locker.should be_locked
112
+ end
113
+
114
+ it %[should acquire the second lock] do
115
+ @rval2.should be_true
116
+ @shared_locker2.should be_locked
117
+ end
118
+ end
119
+
120
+ describe 'non-blocking failure' do
121
+ before do
122
+ @zk.mkdir_p(@root_lock_path)
123
+ @write_lock_path = @zk.create("#{@root_lock_path}/#{ZK::Locker::EXCLUSIVE_LOCK_PREFIX}", '', :mode => :ephemeral_sequential)
124
+ @rval = @shared_locker.lock!
125
+ end
126
+
127
+ after do
128
+ @zk.rm_rf('/_zklocking')
129
+ end
130
+
131
+ it %[should return false] do
132
+ @rval.should be_false
133
+ end
134
+
135
+ it %[should not be locked] do
136
+ @shared_locker.should_not be_locked
137
+ end
138
+ end
139
+
140
+ describe 'blocking success' do
141
+ before do
142
+ @zk.mkdir_p(@root_lock_path)
143
+ @write_lock_path = @zk.create("#{@root_lock_path}/#{ZK::Locker::EXCLUSIVE_LOCK_PREFIX}", '', :mode => :ephemeral_sequential)
144
+ $stderr.sync = true
145
+ end
146
+
147
+ it %[should acquire the lock after the write lock is released] do
148
+ ary = []
149
+
150
+ @shared_locker.lock!.should be_false
151
+
152
+ th = Thread.new do
153
+ @shared_locker.lock!(true)
154
+ ary << :locked
155
+ end
156
+
157
+ ary.should be_empty
158
+ @shared_locker.should_not be_locked
159
+
160
+ @zk.delete(@write_lock_path)
161
+
162
+ th.join(2)
163
+
164
+ wait_until(2) { !ary.empty? }
165
+ ary.length.should == 1
166
+
167
+ @shared_locker.should be_locked
168
+ end
169
+ end
170
+ end
171
+ end # SharedLocker
172
+
173
+ describe :ExclusiveLocker do
174
+ before do
175
+ @ex_locker = ZK::Locker.exclusive_locker(@zk, @path)
176
+ @ex_locker2 = ZK::Locker.exclusive_locker(@zk2, @path)
177
+ end
178
+
179
+ describe :lock! do
180
+ describe 'non-blocking' do
181
+ before do
182
+ @rval = @ex_locker.lock!
183
+ @rval2 = @ex_locker2.lock!
184
+ end
185
+
186
+ it %[should acquire the first lock] do
187
+ @rval.should be_true
188
+ end
189
+
190
+ it %[should not acquire the second lock] do
191
+ @rval2.should be_false
192
+ end
193
+
194
+ it %[should acquire the second lock after the first lock is released] do
195
+ @ex_locker.unlock!.should be_true
196
+ @ex_locker2.lock!.should be_true
197
+ end
198
+ end
199
+
200
+ describe 'blocking' do
201
+ before do
202
+ @zk.mkdir_p(@root_lock_path)
203
+ @read_lock_path = @zk.create('/_zklocking/shlock/read', '', :mode => :ephemeral_sequential)
204
+ end
205
+
206
+ it %[should block waiting for the lock] do
207
+ ary = []
208
+
209
+ @ex_locker.lock!.should be_false
210
+
211
+ th = Thread.new do
212
+ @ex_locker.lock!(true)
213
+ ary << :locked
214
+ end
215
+
216
+ th.run
217
+
218
+ ary.should be_empty
219
+ @ex_locker.should_not be_locked
220
+
221
+ @zk.delete(@read_lock_path)
222
+
223
+ th.join(2)
224
+
225
+ ary.length.should == 1
226
+ @ex_locker.should be_locked
227
+ end
228
+ end
229
+ end
230
+ end # WriteLocker
231
+
232
+ describe 'read/write interaction' do
233
+ before do
234
+ @sh_lock = ZK::Locker.shared_locker(@zk, @path)
235
+ @ex_lock = ZK::Locker.exclusive_locker(@zk2, @path)
236
+ end
237
+
238
+ describe 'shared lock acquired first' do
239
+ it %[should block exclusive locks from acquiring until released] do
240
+ q1 = Queue.new
241
+ q2 = Queue.new
242
+
243
+ th1 = Thread.new do
244
+ @sh_lock.with_lock do
245
+ q1.enq(:got_lock)
246
+ Thread.current[:got_lock] = true
247
+ q2.pop
248
+ end
249
+ end
250
+
251
+ th2 = Thread.new do
252
+ q1.pop # wait for th1 to get the shared lock
253
+
254
+ Thread.current[:acquiring_lock] = true
255
+
256
+ @ex_lock.with_lock do
257
+ Thread.current[:got_lock] = true
258
+ end
259
+ end
260
+
261
+ th1.join_until { th1[:got_lock] }
262
+ th1[:got_lock].should be_true
263
+
264
+ th2.join_until { th2[:acquiring_lock] }
265
+ th2[:acquiring_lock].should be_true
266
+
267
+ q2.num_waiting.should > 0
268
+ q2.enq(:release)
269
+
270
+ th1.join_until { q2.size == 0 }
271
+ q2.size.should == 0
272
+
273
+ th1.join(2).should == th1
274
+
275
+ th2.join_until { th2[:got_lock] }
276
+ th2[:got_lock].should be_true
277
+
278
+ th2.join(2).should == th2
279
+ end
280
+ end
281
+
282
+ describe 'exclusive lock acquired first' do
283
+ it %[should block shared lock from acquiring until released] do
284
+ # same test as above but with the thread's locks switched,
285
+ # th1 is the exclusive locker
286
+
287
+ q1 = Queue.new
288
+ q2 = Queue.new
289
+
290
+ th1 = Thread.new do
291
+ @ex_lock.with_lock do
292
+ q1.enq(:got_lock)
293
+ Thread.current[:got_lock] = true
294
+ q2.pop
295
+ end
296
+ end
297
+
298
+ th2 = Thread.new do
299
+ q1.pop # wait for th1 to get the shared lock
300
+
301
+ Thread.current[:acquiring_lock] = true
302
+
303
+ @sh_lock.with_lock do
304
+ Thread.current[:got_lock] = true
305
+ end
306
+ end
307
+
308
+ th1.join_until { th1[:got_lock] }
309
+ th1[:got_lock].should be_true
310
+
311
+ th2.join_until { th2[:acquiring_lock] }
312
+ th2[:acquiring_lock].should be_true
313
+
314
+ q2.num_waiting.should > 0
315
+ q2.enq(:release)
316
+
317
+ th1.join_until { q2.size == 0 }
318
+ q2.size.should == 0
319
+
320
+ th1.join(2).should == th1
321
+
322
+ th2.join_until { th2[:got_lock] }
323
+ th2[:got_lock].should be_true
324
+
325
+ th2.join(2).should == th2
326
+ end
327
+ end
328
+
329
+ describe 'shared-exclusive-shared' do
330
+ before do
331
+ @zk3.should_not be_nil
332
+ @sh_lock2 = ZK::Locker.shared_locker(@zk3, @path)
333
+ end
334
+
335
+ it %[should act something like a queue] do
336
+ @array = []
337
+
338
+ @sh_lock.lock!.should be_true
339
+ @sh_lock.should be_locked
340
+
341
+ ex_th = Thread.new do
342
+ begin
343
+ @ex_lock.lock!(true) # blocking lock
344
+ Thread.current[:got_lock] = true
345
+ @array << :ex_lock
346
+ ensure
347
+ @ex_lock.unlock!
348
+ end
349
+ end
350
+
351
+ ex_th.join_until { @ex_lock.waiting? }
352
+ @ex_lock.should be_waiting
353
+ @ex_lock.should_not be_locked
354
+
355
+ # this is the important one, does the second shared lock get blocked by
356
+ # the exclusive lock
357
+ @sh_lock2.lock!.should_not be_true
358
+
359
+ sh2_th = Thread.new do
360
+ begin
361
+ @sh_lock2.lock!(true)
362
+ Thread.current[:got_lock] = true
363
+ @array << :sh_lock2
364
+ ensure
365
+ @sh_lock2.unlock!
366
+ end
367
+ end
368
+
369
+ sh2_th.join_until { @sh_lock2.waiting? }
370
+ @sh_lock2.should be_waiting
371
+
372
+ @sh_lock.unlock!.should be_true
373
+
374
+ ex_th.join_until { ex_th[:got_lock] }
375
+ ex_th[:got_lock].should be_true
376
+
377
+ sh2_th.join_until { sh2_th[:got_lock] }
378
+ sh2_th[:got_lock].should be_true
379
+
380
+ @array.length.should == 2
381
+ @array.should == [:ex_lock, :sh_lock2]
382
+ end
383
+ end
384
+ end
385
+ end
386
+
@@ -0,0 +1,17 @@
1
+ log4j.rootLogger = DEBUG,S
2
+
3
+ log4j.appender.S = org.apache.log4j.FileAppender
4
+ log4j.appender.S.File = /tmp/zk-test.log
5
+
6
+ log4j.appender.S.layout = org.apache.log4j.PatternLayout
7
+ log4j.appender.S.layout.ConversionPattern = %5p [%t] (%c:%F:%L) - %m%n
8
+
9
+ log4j.appender.C = org.apache.log4j.FileAppender
10
+ log4j.appender.C.File = /tmp/zk-test-client.log
11
+
12
+ log4j.appender.C.layout = org.apache.log4j.PatternLayout
13
+ log4j.appender.C.layout.ConversionPattern = %5p [%t] (%c:%F:%L) - %m%n
14
+
15
+ log4j.org.apache.zookeeper.ZooKeeper = DEBUG,C
16
+ log4j.org.apache.zookeeper.server = DEBUG,S
17
+