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,329 @@
1
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
2
+
3
+ require 'tracer'
4
+
5
+ describe ZK::Pool do
6
+ describe :Simple do
7
+
8
+ before do
9
+ report_realtime('opening pool') do
10
+ @pool_size = 2
11
+ @connection_pool = ZK::Pool::Simple.new("localhost:#{ZK_TEST_PORT}", @pool_size, :watcher => :default)
12
+ @connection_pool.should be_open
13
+ end
14
+ end
15
+
16
+ after do
17
+ report_realtime("close_all!") do
18
+ unless @connection_pool.closed?
19
+ th = Thread.new do
20
+ @connection_pool.close_all!
21
+ end
22
+
23
+ unless th.join(5) == th
24
+ logger.warn { "Forcing pool closed!" }
25
+ @connection_pool.force_close!
26
+ th.join(5).should == th
27
+ end
28
+ end
29
+ end
30
+
31
+ report_realtime("closing") do
32
+ ZK.open("localhost:#{ZK_TEST_PORT}") do |zk|
33
+ zk.delete('/test_pool') rescue ZK::Exceptions::NoNode
34
+ end
35
+ end
36
+ end
37
+
38
+ it "should allow you to execute commands on a connection" do
39
+ @connection_pool.with_connection do |zk|
40
+ zk.create("/test_pool", "", :mode => :ephemeral)
41
+ zk.exists?("/test_pool").should be_true
42
+ end
43
+ end
44
+
45
+ describe :method_missing do
46
+ it %[should allow you to execute commands on the connection pool itself] do
47
+ @connection_pool.create('/test_pool', '', :mode => :persistent)
48
+ wait_until(2) { @connection_pool.exists?('/test_pool') }
49
+ @connection_pool.exists?('/test_pool').should be_true
50
+ end
51
+ end
52
+
53
+ describe :close_all! do
54
+ it %[should shutdown gracefully] do
55
+ release_q = Queue.new
56
+
57
+ @about_to_block = false
58
+
59
+ open_th = Thread.new do
60
+ @connection_pool.with_connection do |cnx|
61
+ @about_to_block = true
62
+ # wait for signal to release our connection
63
+ release_q.pop
64
+ end
65
+ end
66
+
67
+ wait_until(2) { @about_to_block }
68
+ @about_to_block.should be_true
69
+
70
+ release_q.num_waiting.should == 1
71
+
72
+ closing_th = Thread.new do
73
+ @connection_pool.close_all!
74
+ end
75
+
76
+ wait_until(2) { @connection_pool.closing? }
77
+ @connection_pool.should be_closing
78
+
79
+ lambda { @connection_pool.with_connection { |c| } }.should raise_error(ZK::Exceptions::PoolIsShuttingDownException)
80
+
81
+ release_q << :ok_let_go
82
+
83
+ open_th.join(2).should == open_th
84
+
85
+ wait_until(2) { @connection_pool.closed? }
86
+ $stderr.puts "@connection_pool.pool_state: #{@connection_pool.pool_state.inspect}"
87
+ @connection_pool.should be_closed
88
+
89
+ lambda do
90
+ closing_th.join(1).should == closing_th
91
+ open_th.join(1).should == open_th
92
+ end.should_not raise_error
93
+ end
94
+ end
95
+
96
+ describe :force_close! do
97
+ it %[should raise PoolIsShuttingDownException in a thread blocked waiting for a connection] do
98
+ @cnx = []
99
+
100
+ until @connection_pool.available_size <= 0
101
+ @cnx << @connection_pool.checkout
102
+ end
103
+
104
+ @cnx.length.should_not be_zero
105
+
106
+ th = Thread.new do
107
+ @connection_pool.checkout(true)
108
+ end
109
+
110
+ th.join_until { @connection_pool.count_waiters > 0 }
111
+ @connection_pool.count_waiters.should > 0
112
+
113
+ @connection_pool.force_close!
114
+
115
+ lambda { th.join(2) }.should raise_error(ZK::Exceptions::PoolIsShuttingDownException)
116
+ end
117
+ end
118
+
119
+ it "should allow watchers still" do
120
+ @callback_called = false
121
+
122
+ @path = '/_testWatch'
123
+
124
+ @connection_pool.with_connection do |zk|
125
+ zk.delete(@path) rescue ZK::Exceptions::NoNode
126
+ end
127
+
128
+ @connection_pool.with_connection do |zk|
129
+ $stderr.puts "registering callback"
130
+ zk.watcher.register(@path) do |event|
131
+ $stderr.puts "callback fired! event: #{event.inspect}"
132
+
133
+ @callback_called = true
134
+ event.path.should == @path
135
+ $stderr.puts "signaling other waiters"
136
+ end
137
+
138
+ $stderr.puts "setting up watcher"
139
+ zk.exists?(@path, :watch => true).should be_false
140
+ end
141
+
142
+ @connection_pool.with_connection do |zk|
143
+ $stderr.puts "creating path"
144
+ zk.create(@path, "", :mode => :ephemeral).should == @path
145
+ end
146
+
147
+ wait_until(1) { @callback_called }
148
+
149
+ @callback_called.should be_true
150
+ end
151
+ end # Client
152
+
153
+ describe :Bounded do
154
+ before do
155
+ @min_clients = 1
156
+ @max_clients = 2
157
+ @connection_pool = ZK::Pool::Bounded.new("localhost:#{ZK_TEST_PORT}", :min_clients => @min_clients, :max_clients => @max_clients, :timeout => @timeout)
158
+ @connection_pool.should be_open
159
+ end
160
+
161
+ after do
162
+ @connection_pool.force_close! unless @connection_pool.closed?
163
+ @connection_pool.should be_closed
164
+ end
165
+
166
+ describe 'initial state' do
167
+ it %[should have initialized the minimum number of clients] do
168
+ @connection_pool.size.should == @min_clients
169
+ end
170
+ end
171
+
172
+ describe 'should grow to max_clients' do
173
+ # before do
174
+ # require 'tracer'
175
+ # Tracer.on
176
+ # end
177
+
178
+ # after do
179
+ # Tracer.off
180
+ # end
181
+
182
+ it %[should grow if it can] do
183
+ q1 = Queue.new
184
+
185
+ @connection_pool.size.should == 1
186
+
187
+ th1 = Thread.new do
188
+ @connection_pool.with_connection do |cnx|
189
+ Thread.current[:cnx] = cnx
190
+ q1.pop # block here
191
+ end
192
+ end
193
+
194
+ th1.join_until(2) { th1[:cnx] }
195
+ th1[:cnx].should_not be_nil
196
+
197
+ th2 = Thread.new do
198
+ @connection_pool.with_connection do |cnx|
199
+ Thread.current[:cnx] = cnx
200
+ q1.pop
201
+ end
202
+ end
203
+
204
+ th2.join_until(2) { th2[:cnx] }
205
+
206
+ th2[:cnx].should_not be_nil
207
+ th2[:cnx].should be_connected
208
+
209
+ @connection_pool.size.should == 2
210
+ @connection_pool.available_size.should be_zero
211
+
212
+ 2.times { q1.enq(:release_cnx) }
213
+
214
+ lambda do
215
+ th1.join(1).should == th1
216
+ th2.join(1).should == th2
217
+ end.should_not raise_error
218
+
219
+ @connection_pool.size.should == 2
220
+ @connection_pool.available_size.should == 2
221
+ end
222
+
223
+ it %[should not grow past max_clients and block] do
224
+ win_q = Queue.new
225
+ lose_q = Queue.new
226
+
227
+ threads = []
228
+
229
+ 2.times do
230
+ threads << Thread.new do
231
+ @connection_pool.with_connection do |cnx|
232
+ Thread.current[:cnx] = cnx
233
+ win_q.pop
234
+ end
235
+ end
236
+ end
237
+
238
+ wait_until(2) { threads.all? { |th| th[:cnx] } }
239
+ threads.each { |th| th[:cnx].should_not be_nil }
240
+
241
+ loser = Thread.new do
242
+ @connection_pool.with_connection do |cnx|
243
+ Thread.current[:cnx] = cnx
244
+ lose_q.pop
245
+ end
246
+ end
247
+
248
+ wait_until(2) { @connection_pool.count_waiters > 0 }
249
+ @connection_pool.count_waiters.should == 1
250
+
251
+ loser[:cnx].should be_nil
252
+
253
+ 2.times { win_q.enq(:release) }
254
+
255
+ lambda { threads.each { |th| th.join(2).should == th } }.should_not raise_error
256
+
257
+ loser.join_until(2) { loser[:cnx] }
258
+
259
+ loser[:cnx].should_not be_nil
260
+
261
+ lose_q.enq(:release)
262
+
263
+ lambda { loser.join(2).should == loser }.should_not raise_error
264
+
265
+ @connection_pool.count_waiters.should be_zero
266
+ @connection_pool.available_size.should == 2
267
+ @connection_pool.size.should == 2
268
+ end
269
+ end
270
+
271
+ describe 'health checking' do
272
+ before do
273
+ @connections = @connection_pool.connections
274
+ @connections.length.should == 1
275
+
276
+ @cnx1 = @connections.first
277
+ end
278
+
279
+ describe 'disconnected client' do
280
+ before do
281
+ flexmock(@cnx1) do |m|
282
+ m.should_receive(:connected?).and_return(false)
283
+ end
284
+
285
+ @cnx2 = nil
286
+
287
+ th = Thread.new do
288
+ @connection_pool.with_connection do |cnx|
289
+ @cnx2 = cnx
290
+ end
291
+ end
292
+
293
+ th.join_while { @cnx2.nil? }
294
+ end
295
+
296
+ it %[should create a new client and return it] do
297
+ @cnx2.should_not be_nil
298
+ @cnx2.should_not == @cnx1
299
+ end
300
+
301
+ it %[should remove the disconnected client from the pool] do
302
+ @connection_pool.available_size.should == 1
303
+ end
304
+
305
+ it %[should still have the original client in its array of all connections] do
306
+ @connections.should include(@cnx1)
307
+ end
308
+
309
+ describe 'when connected event fires' do
310
+ before do
311
+ @event = flexmock(:event) do |m|
312
+ m.should_receive(:type).and_return(-1)
313
+ m.should_receive(:zk=).with(any())
314
+ m.should_receive(:node_event?).and_return(false)
315
+ m.should_receive(:state_event?).and_return(true)
316
+ m.should_receive(:state).and_return(ZookeeperConstants::ZOO_CONNECTED_STATE)
317
+ end
318
+
319
+ @cnx1.watcher.process(@event)
320
+ end
321
+
322
+ it %[should add the connection back into the pool] do
323
+ @connection_pool.available_size.should == 2
324
+ end
325
+ end
326
+ end
327
+ end
328
+ end
329
+ end
@@ -0,0 +1,102 @@
1
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
2
+
3
+ describe ZK::Client do
4
+ before do
5
+ @zk = ZK.new("localhost:#{ZK_TEST_PORT}")
6
+ wait_until{ @zk.connected? }
7
+ @zk.rm_rf('/test')
8
+ end
9
+
10
+ after do
11
+ @zk.rm_rf('/test')
12
+ @zk.close!
13
+
14
+ wait_until(2) { @zk.closed? }
15
+ end
16
+
17
+ describe :mkdir_p do
18
+ before(:each) do
19
+ @path_ary = %w[test mkdir_p path creation]
20
+ @bogus_path = File.join('/', *@path_ary)
21
+ end
22
+
23
+ it %[should create all intermediate paths for the path givem] do
24
+ @zk.should_not be_exists(@bogus_path)
25
+ @zk.should_not be_exists(File.dirname(@bogus_path))
26
+ @zk.mkdir_p(@bogus_path)
27
+ @zk.should be_exists(@bogus_path)
28
+ end
29
+ end
30
+
31
+ describe :stat do
32
+ describe 'for a missing node' do
33
+ before do
34
+ @missing_path = '/thispathdoesnotexist'
35
+ @zk.delete(@missing_path) rescue ZK::Exceptions::NoNode
36
+ end
37
+
38
+ it %[should not raise any error] do
39
+ lambda { @zk.stat(@missing_path) }.should_not raise_error
40
+ end
41
+
42
+ it %[should return a Stat object] do
43
+ @zk.stat(@missing_path).should be_kind_of(ZookeeperStat::Stat)
44
+ end
45
+
46
+ it %[should return a stat that not exists?] do
47
+ @zk.stat(@missing_path).should_not be_exists
48
+ end
49
+ end
50
+ end
51
+
52
+ describe :block_until_node_deleted do
53
+ before do
54
+ @path = '/_bogualkjdhsna'
55
+ end
56
+
57
+ describe 'no node initially' do
58
+ before do
59
+ @zk.exists?(@path).should be_false
60
+ end
61
+
62
+ it %[should not block] do
63
+ @a = false
64
+
65
+ th = Thread.new do
66
+ @zk.block_until_node_deleted(@path)
67
+ @a = true
68
+ end
69
+
70
+ th.join(2)
71
+ @a.should be_true
72
+ end
73
+ end
74
+
75
+ describe 'node exists initially' do
76
+ before do
77
+ @zk.create(@path, '', :mode => :ephemeral)
78
+ @zk.exists?(@path).should be_true
79
+ end
80
+
81
+ it %[should block until the node is deleted] do
82
+ @a = false
83
+
84
+ th = Thread.new do
85
+ @zk.block_until_node_deleted(@path)
86
+ @a = true
87
+ end
88
+
89
+ Thread.pass
90
+ @a.should be_false
91
+
92
+ @zk.delete(@path)
93
+
94
+ wait_until(2) { @a }
95
+ @a.should be_true
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+
102
+