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,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
|
+
|
data/spec/locker_spec.rb
ADDED
@@ -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
|
+
|