@@ -68,13 +68,67 @@ function test_old_api(t, conn)
68
68
t :ok (conn :close (), " close" )
69
69
end
70
70
71
- function test_gc (t , p )
72
- t :plan (1 )
73
- p :get ()
74
- local c = p :get ()
75
- c = nil
76
- collectgarbage (' collect' )
77
- t :is (p .queue :count (), p .size , ' gc connections' )
71
+ function test_gc (test , pool )
72
+ test :plan (3 )
73
+
74
+ -- Case: verify that a pool tracks connections that are not
75
+ -- put back, but were collected by GC.
76
+ test :test (' loss a healthy connection' , function (test )
77
+ test :plan (1 )
78
+
79
+ assert (pool .size >= 2 , ' test case precondition fails' )
80
+
81
+ -- Loss one connection.
82
+ pool :get ()
83
+
84
+ -- Loss another one.
85
+ local conn = pool :get ()
86
+ conn = nil
87
+
88
+ -- Collect lost connections.
89
+ collectgarbage (' collect' )
90
+
91
+ -- Verify that a pool is aware of collected connections.
92
+ test :is (pool .queue :count (), pool .size , ' all connections are put back' )
93
+ end )
94
+
95
+ -- Case: the same, but for broken connection.
96
+ test :test (' loss a broken connection' , function (test )
97
+ test :plan (2 )
98
+
99
+ assert (pool .size >= 1 , ' test case precondition fails' )
100
+
101
+ -- Get a connection, make a bad query and loss the
102
+ -- connection.
103
+ local conn = pool :get ()
104
+ local ok = pcall (conn .execute , conn , ' bad query' )
105
+ test :ok (not ok , ' a query actually fails' )
106
+ conn = nil
107
+
108
+ -- Collect the lost connection.
109
+ collectgarbage (' collect' )
110
+
111
+ -- Verify that a pool is aware of collected connections.
112
+ test :is (pool .queue :count (), pool .size , ' all connections are put back' )
113
+ end )
114
+
115
+ -- Case: the same, but for closed connection.
116
+ test :test (' loss a closed connection' , function (test )
117
+ test :plan (1 )
118
+
119
+ assert (pool .size >= 1 , ' test case precondition fails' )
120
+
121
+ -- Get a connection, close it and loss the connection.
122
+ local conn = pool :get ()
123
+ conn :close ()
124
+ conn = nil
125
+
126
+ -- Collect the lost connection.
127
+ collectgarbage (' collect' )
128
+
129
+ -- Verify that a pool is aware of collected connections.
130
+ test :is (pool .queue :count (), pool .size , ' all connections are put back' )
131
+ end )
78
132
end
79
133
80
134
function test_conn_fiber1 (c , q )
@@ -116,8 +170,247 @@ function test_mysql_int64(t, p)
116
170
p :put (conn )
117
171
end
118
172
173
+ local function test_connection_pool (test , pool )
174
+ test :plan (5 )
175
+
176
+ -- {{{ Case group: all connections are consumed initially.
177
+
178
+ assert (pool .queue :is_full (), ' case group precondition fails' )
179
+
180
+ -- Grab all connections from a pool.
181
+ local connections = {}
182
+ for i = 1 , pool .size do
183
+ table.insert (connections , pool :get ())
184
+ end
185
+
186
+ -- Case: get and put connections from / to a pool.
187
+ test :test (' pool:get({}) and pool:put()' , function (test )
188
+ test :plan (2 )
189
+ assert (pool .queue :is_empty (), ' test case precondition fails' )
190
+
191
+ -- Verify that we're unable to get one more connection.
192
+ local latch = fiber .channel (1 )
193
+ local conn
194
+ fiber .create (function ()
195
+ conn = pool :get ()
196
+ latch :put (true )
197
+ end )
198
+ local res = latch :get (1 )
199
+ test :is (res , nil , ' unable to get more connections then a pool size' )
200
+
201
+ -- Give a connection back and verify that now the fiber
202
+ -- above gets this connection.
203
+ pool :put (table.remove (connections ))
204
+ latch :get ()
205
+ test :ok (conn ~= nil , ' able to get a connection when it was given back' )
206
+
207
+ -- Restore everything as it was.
208
+ table.insert (connections , conn )
209
+ conn = nil
210
+
211
+ assert (pool .queue :is_empty (), ' test case postcondition fails' )
212
+ end )
213
+
214
+ -- Give all connections back to the poll.
215
+ for _ , conn in ipairs (connections ) do
216
+ pool :put (conn )
217
+ end
218
+
219
+ assert (pool .queue :is_full (), ' case group postcondition fails' )
220
+
221
+ -- }}}
222
+
223
+ -- {{{ Case group: all connections are ready initially.
224
+
225
+ assert (pool .queue :is_full (), ' case group precondition fails' )
226
+
227
+ -- XXX: Maybe the cases below will look better if rewrite them
228
+ -- in a declarative way, like so:
229
+ --
230
+ -- local cases = {
231
+ -- {
232
+ -- 'case name',
233
+ -- after_get = function(test, context)
234
+ -- -- * Do nothing.
235
+ -- -- * Or make a bad query (and assert that
236
+ -- -- :execute() fails).
237
+ -- -- * Or close the connection.
238
+ -- end,
239
+ -- after_put = function(test, context)
240
+ -- -- * Do nothing.
241
+ -- -- * Or loss `context.conn` and trigger
242
+ -- -- GC.
243
+ -- end,
244
+ -- }
245
+ -- }
246
+ --
247
+ -- Or so:
248
+ --
249
+ -- local cases = {
250
+ -- {
251
+ -- 'case name',
252
+ -- after_get = function(test, context)
253
+ -- -- * Do nothing.
254
+ -- -- * Or make a bad query (and assert that
255
+ -- -- :execute() fails).
256
+ -- -- * Or close the connection.
257
+ -- end,
258
+ -- loss_after_put = <boolean>,
259
+ -- }
260
+ -- }
261
+ --
262
+ -- `loss_after_put` will do the following after put (see
263
+ -- comments in cases below):
264
+ --
265
+ -- context.conn = nil
266
+ -- collectgarbage('collect')
267
+ -- assert(pool.queue:is_full(), <...>)
268
+ -- local item = pool.queue:get()
269
+ -- pool.queue:put(item)
270
+ -- test:ok(true, <...>)
271
+
272
+ -- Case: get a connection and put it back.
273
+ test :test (' get and put a connection' , function (test )
274
+ test :plan (1 )
275
+
276
+ assert (pool .size >= 1 , ' test case precondition fails' )
277
+
278
+ -- Get a connection.
279
+ local conn = pool :get ()
280
+
281
+ -- Put the connection back and verify that the pool is full.
282
+ pool :put (conn )
283
+ test :ok (pool .queue :is_full (), ' a broken connection was given back' )
284
+ end )
285
+
286
+ -- Case: the same, but loss and collect a connection after
287
+ -- put.
288
+ test :test (' get, put and loss a connection' , function (test )
289
+ test :plan (2 )
290
+
291
+ assert (pool .size >= 1 , ' test case precondition fails' )
292
+
293
+ -- Get a connection.
294
+ local conn = pool :get ()
295
+
296
+ -- Put the connection back, loss it and trigger GC.
297
+ pool :put (conn )
298
+ conn = nil
299
+ collectgarbage (' collect' )
300
+
301
+ -- Verify that the pool is full.
302
+ test :ok (pool .queue :is_full (), ' a broken connection was given back' )
303
+
304
+ -- Verify the pool will not be populated by a connection's
305
+ -- GC callback. Otherwise :put() will hang.
306
+ local item = pool .queue :get ()
307
+ pool .queue :put (item )
308
+ test :ok (true , ' GC callback does not put back a connection that was ' ..
309
+ ' put manually' )
310
+ end )
311
+
312
+ -- Case: get a connection, broke it and put back.
313
+ test :test (' get, broke and put a connection' , function (test )
314
+ test :plan (2 )
315
+
316
+ assert (pool .size >= 1 , ' test case precondition fails' )
317
+
318
+ -- Get a connection and make a bad query.
319
+ local conn = pool :get ()
320
+ local ok = pcall (conn .execute , conn , ' bad query' )
321
+ test :ok (not ok , ' a query actually fails' )
322
+
323
+ -- Put the connection back and verify that the pool is full.
324
+ pool :put (conn )
325
+ test :ok (pool .queue :is_full (), ' a broken connection was given back' )
326
+ end )
327
+
328
+ -- Case: the same, but loss and collect a connection after
329
+ -- put.
330
+ test :test (' get, broke, put and loss a connection' , function (test )
331
+ test :plan (3 )
332
+
333
+ assert (pool .size >= 1 , ' test case precondition fails' )
334
+
335
+ -- Get a connection and make a bad query.
336
+ local conn = pool :get ()
337
+ local ok = pcall (conn .execute , conn , ' bad query' )
338
+ test :ok (not ok , ' a query actually fails' )
339
+
340
+ -- Put the connection back, loss it and trigger GC.
341
+ pool :put (conn )
342
+ conn = nil
343
+ collectgarbage (' collect' )
344
+
345
+ -- Verify that the pool is full
346
+ test :ok (pool .queue :is_full (), ' a broken connection was given back' )
347
+
348
+ -- Verify the pool will not be populated by a connection's
349
+ -- GC callback. Otherwise :put() will hang.
350
+ local item = pool .queue :get ()
351
+ pool .queue :put (item )
352
+ test :ok (true , ' GC callback does not put back a connection that was ' ..
353
+ ' put manually' )
354
+ end )
355
+
356
+ --[[
357
+
358
+ -- It is unclear for now whether putting of closed connection
359
+ -- should be allowed. The second case, where GC collects lost
360
+ -- connection after :put(), does not work at the moment. See
361
+ -- gh-33.
362
+
363
+ -- Case: get a connection, close it and put back.
364
+ test:test('get, close and put a connection', function(test)
365
+ test:plan(1)
366
+
367
+ assert(pool.size >= 1, 'test case precondition fails')
368
+
369
+ -- Get a connection and close it.
370
+ local conn = pool:get()
371
+ conn:close()
372
+
373
+ -- Put a connection back and verify that the pool is full.
374
+ pool:put(conn)
375
+ test:ok(pool.queue:is_full(), 'a broken connection was given back')
376
+ end)
377
+
378
+ -- Case: the same, but loss and collect a connection after
379
+ -- put.
380
+ test:test('get, close, put and loss a connection', function(test)
381
+ test:plan(2)
382
+
383
+ assert(pool.size >= 1, 'test case precondition fails')
384
+
385
+ -- Get a connection and close it.
386
+ local conn = pool:get()
387
+ conn:close()
388
+
389
+ -- Put the connection back, loss it and trigger GC.
390
+ pool:put(conn)
391
+ conn = nil
392
+ collectgarbage('collect')
393
+
394
+ -- Verify that the pool is full
395
+ test:ok(pool.queue:is_full(), 'a broken connection was given back')
396
+
397
+ -- Verify the pool will not be populated by a connection's
398
+ -- GC callback. Otherwise :put() will hang.
399
+ local item = pool.queue:get()
400
+ pool.queue:put(item)
401
+ test:ok(true, 'GC callback does not put back a connection that was ' ..
402
+ 'put manually')
403
+ end)
404
+
405
+ --]]
406
+
407
+ assert (pool .queue :is_full (), ' case group postcondition fails' )
408
+
409
+ -- }}}
410
+ end
411
+
119
412
local test = tap .test (' mysql connector' )
120
- test :plan (5 )
413
+ test :plan (6 )
121
414
122
415
test :test (' connection old api' , test_old_api , conn )
123
416
local pool_conn = p :get ()
@@ -126,6 +419,7 @@ p:put(pool_conn)
126
419
test :test (' garbage collection' , test_gc , p )
127
420
test :test (' concurrent connections' , test_conn_concurrent , p )
128
421
test :test (' int64' , test_mysql_int64 , p )
422
+ test :test (' connection pool' , test_connection_pool , p )
129
423
p :close ()
130
424
131
425
os.exit (test :check () and 0 or 1 )
0 commit comments