em-pg-client 0.2.1 → 0.3.0

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,655 @@
1
+ $:.unshift "lib"
2
+ gem 'eventmachine', '~> 1.0.0'
3
+ gem 'pg', ENV['EM_PG_CLIENT_TEST_PG_VERSION']
4
+ require 'eventmachine'
5
+ require 'em-synchrony'
6
+ require 'pg/em/connection_pool'
7
+ require 'connection_pool_helpers'
8
+
9
+ RSpec.configure do |c|
10
+ c.include ConnectionPoolHelpers
11
+ end
12
+
13
+ describe PG::EM::ConnectionPool do
14
+ subject { PG::EM::ConnectionPool }
15
+
16
+ let(:client) { Class.new }
17
+ let(:deferrable) { PG::EM::FeaturedDeferrable }
18
+ let(:checkpoint) { proc {} }
19
+ let(:pgerror) { PG::Error.new }
20
+ let(:fooerror) { RuntimeError.new 'foo' }
21
+ let(:timeout) { 10 }
22
+
23
+ it "should allocate one connection" do
24
+ client.should_receive(:new).with({}).once.and_return(client.allocate)
25
+ pool = subject.new connection_class: client
26
+ pool.should be_an_instance_of subject
27
+ pool.max_size.should eq subject::DEFAULT_SIZE
28
+ pool.available.length.should eq 1
29
+ pool.allocated.length.should eq 0
30
+ pool.size.should eq 1
31
+ pool.available.first.should be_an_instance_of client
32
+ pool.available.first.should_receive(:finish).once
33
+ pool.finish.should be pool
34
+ pool.max_size.should eq subject::DEFAULT_SIZE
35
+ pool.available.length.should eq 0
36
+ pool.allocated.length.should eq 0
37
+ pool.size.should eq 0
38
+ end
39
+
40
+ it "should asynchronously allocate one connection" do
41
+ checkpoint.should_receive(:call) do |pool|
42
+ pool.should be_an_instance_of subject
43
+ pool.max_size.should eq 42
44
+ pool.available.length.should eq 1
45
+ pool.allocated.length.should eq 0
46
+ pool.size.should eq 1
47
+ pool.available.first.should be_an_instance_of client
48
+ end
49
+ client.should_receive(:connect_defer).with({}).once.and_return(
50
+ deferrable.new.tap {|df| df.succeed client.allocate }
51
+ )
52
+ df = subject.connect_defer connection_class: client, size: 42
53
+ df.should be_an_instance_of deferrable
54
+ df.callback(&checkpoint)
55
+ end
56
+
57
+ it "should write to client attributes and finish" do
58
+ client.should_receive(:new) do |options|
59
+ options.should be_empty
60
+ client.allocate.tap do |conn|
61
+ conn.should_receive(:connect_timeout=).with(timeout).once
62
+ conn.should_receive(:query_timeout=).with(timeout).once
63
+ conn.should_receive(:finish).once
64
+ end
65
+ end.exactly(3)
66
+ pool = subject.new connection_class: client, size: 3
67
+ pool.should be_an_instance_of subject
68
+ pool.connect_timeout.should be_nil
69
+ pool.query_timeout.should be_nil
70
+ pool.max_size.should eq 3
71
+ pool.available.length.should eq 1
72
+ pool.allocated.length.should eq 0
73
+ pool.size.should eq 1
74
+ pool.hold do
75
+ pool.size.should eq 1
76
+ Fiber.new do
77
+ pool.hold do
78
+ pool.size.should eq 2
79
+ Fiber.new do
80
+ pool.hold do
81
+ pool.available.length.should eq 0
82
+ pool.allocated.length.should eq 3
83
+ pool.size.should eq 3
84
+ end
85
+ end.resume
86
+ pool.available.length.should eq 1
87
+ pool.allocated.length.should eq 2
88
+ pool.connect_timeout = timeout
89
+ pool.query_timeout = timeout
90
+ end
91
+ end.resume
92
+ end
93
+ pool.available.length.should eq 3
94
+ pool.allocated.length.should eq 0
95
+ pool.size.should eq 3
96
+ pool.connect_timeout.should eq timeout
97
+ pool.query_timeout.should eq timeout
98
+ pool.finish.should be pool
99
+ pool.max_size.should eq 3
100
+ pool.available.length.should eq 0
101
+ pool.allocated.length.should eq 0
102
+ pool.size.should eq 0
103
+ end
104
+
105
+ it "should allocate new connection with altered attributes" do
106
+ client.should_receive(:new).with(
107
+ {connect_timeout: timeout, query_timeout: timeout}
108
+ ).once.and_return(client.allocate)
109
+ pool = subject.new connection_class: client, size: 1, lazy: true
110
+ pool.should be_an_instance_of subject
111
+ pool.connect_timeout.should be_nil
112
+ pool.query_timeout.should be_nil
113
+ pool.connect_timeout = timeout
114
+ pool.query_timeout = timeout
115
+ pool.connect_timeout.should eq timeout
116
+ pool.query_timeout.should eq timeout
117
+ pool.max_size.should eq 1
118
+ pool.available.length.should eq 0
119
+ pool.allocated.length.should eq 0
120
+ pool.size.should eq 0
121
+ pool.hold
122
+ pool.available.length.should eq 1
123
+ pool.allocated.length.should eq 0
124
+ pool.size.should eq 1
125
+ end
126
+
127
+ it "should lazy write attributes to connecting client" do
128
+ pool = nil
129
+ client.should_receive(:new) do |options|
130
+ options.should be_empty
131
+ pool.connect_timeout = timeout
132
+ pool.query_timeout = timeout
133
+ client.allocate.tap do |conn|
134
+ conn.should_receive(:connect_timeout=).with(timeout).once
135
+ conn.should_receive(:query_timeout=).with(timeout).once
136
+ end
137
+ end.once
138
+ client.should_receive(:connect_defer) do |options|
139
+ options.should be_empty
140
+ pool.connect_timeout = timeout
141
+ pool.query_timeout = timeout
142
+ deferrable.new.tap do |df|
143
+ df.succeed(client.allocate.tap do |conn|
144
+ conn.should_receive(:connect_timeout=).with(timeout).once
145
+ conn.should_receive(:query_timeout=).with(timeout).once
146
+ end)
147
+ end
148
+ end.once
149
+ checkpoint.should_receive(:call).with(:hurray).once
150
+
151
+ pool = subject.new connection_class: client, size: 1, lazy: true
152
+ pool.should be_an_instance_of subject
153
+ pool.connect_timeout.should be_nil
154
+ pool.query_timeout.should be_nil
155
+ pool.max_size.should eq 1
156
+ pool.size.should eq 0
157
+ pool.hold
158
+ pool.connect_timeout.should eq timeout
159
+ pool.query_timeout.should eq timeout
160
+ pool.size.should eq 1
161
+
162
+ pool = subject.new connection_class: client, size: 1, lazy: true
163
+ pool.should be_an_instance_of subject
164
+ pool.connect_timeout.should be_nil
165
+ pool.query_timeout.should be_nil
166
+ pool.__send__(:hold_deferred, checkpoint) do
167
+ deferrable.new.tap { |df| df.succeed :hurray }
168
+ end
169
+ pool.connect_timeout.should eq timeout
170
+ pool.query_timeout.should eq timeout
171
+ pool.size.should eq 1
172
+ end
173
+
174
+ it "should pass missing methods to connection" do
175
+ client.should_receive(:new) do
176
+ client.allocate.tap do |conn|
177
+ conn.should_receive(:foobar).once.and_return(:ok)
178
+ end
179
+ end.once
180
+ pool = subject.new connection_class: client
181
+ pool.should be_an_instance_of subject
182
+ pool.foobar.should be :ok
183
+ pool.respond_to?(:foobar).should be_true
184
+ pool.respond_to?(:crowbar).should be_false
185
+ end
186
+
187
+ it "should hold nested commands" do
188
+ ::EM.should_not_receive(:next_tick)
189
+ client.should_receive(:new).with({}).once.and_return(client.allocate)
190
+ pool = subject.new connection_class: client
191
+ pool.should be_an_instance_of subject
192
+ checkpoint.should_receive(:check) do |conn|
193
+ pool.max_size.should eq subject::DEFAULT_SIZE
194
+ pool.available.length.should eq 0
195
+ pool.allocated.length.should eq 1
196
+ pool.size.should eq 1
197
+ end.exactly(3)
198
+ pool.hold do |conn|
199
+ conn.should be_an_instance_of client
200
+ checkpoint.check
201
+ pool.hold do |conn2|
202
+ conn2.should be conn
203
+ checkpoint.check
204
+ end
205
+ checkpoint.check
206
+ end
207
+ pool.max_size.should eq subject::DEFAULT_SIZE
208
+ pool.available.length.should eq 1
209
+ pool.allocated.length.should eq 0
210
+ pool.size.should eq 1
211
+ pool.available.first.should be_an_instance_of client
212
+ end
213
+
214
+ it "should hold commands concurrently" do
215
+ ::EM.should_not_receive(:next_tick)
216
+ client.should_receive(:new) { client.allocate }.twice
217
+ pool = subject.new connection_class: client, size: 2
218
+ pool.should be_an_instance_of subject
219
+ pool.max_size.should eq 2
220
+ pool.available.length.should eq 1
221
+ pool.allocated.length.should eq 0
222
+ pool.size.should eq 1
223
+ pool.available.first.should be_an_instance_of client
224
+ checkpoint.should_receive(:check).exactly(8)
225
+ result = []
226
+ pool.hold do |conn1|
227
+ conn1.should be_an_instance_of client
228
+ result << conn1
229
+ pool.max_size.should eq 2
230
+ pool.available.length.should eq 0
231
+ pool.allocated.length.should eq 1
232
+ pool.size.should eq 1
233
+ checkpoint.check
234
+ Fiber.new do
235
+ pool.hold do |conn2|
236
+ conn2.should be_an_instance_of client
237
+ pool.instance_variable_get(:@pending).length.should eq 0
238
+ Fiber.new do
239
+ pool.hold do |conn3|
240
+ result << conn3
241
+ conn3.should be conn2
242
+ pool.available.length.should eq 0
243
+ pool.allocated.length.should eq 2
244
+ pool.size.should eq 2
245
+ checkpoint.check
246
+ end
247
+ end.resume
248
+ pool.instance_variable_get(:@pending).length.should eq 1
249
+ Fiber.new do
250
+ pool.hold do |conn4|
251
+ result << conn4
252
+ result.should eq [conn1, conn2, conn2, conn4]
253
+ conn4.should be conn2
254
+ pool.available.length.should eq 0
255
+ pool.allocated.length.should eq 2
256
+ pool.size.should eq 2
257
+ checkpoint.check
258
+ pool.hold do |conn5|
259
+ conn5.should be conn4
260
+ pool.max_size.should eq 2
261
+ pool.available.length.should eq 0
262
+ pool.allocated.length.should eq 2
263
+ pool.size.should eq 2
264
+ checkpoint.check
265
+ end
266
+ pool.available.length.should eq 0
267
+ pool.allocated.length.should eq 2
268
+ pool.size.should eq 2
269
+ checkpoint.check
270
+ end
271
+ end.resume
272
+ pool.instance_variable_get(:@pending).length.should eq 2
273
+ result << conn2
274
+ conn2.should_not be conn1
275
+ pool.available.length.should eq 0
276
+ pool.allocated.length.should eq 2
277
+ pool.size.should eq 2
278
+ checkpoint.check
279
+ end
280
+ pool.max_size.should eq 2
281
+ pool.available.length.should eq 1
282
+ pool.allocated.length.should eq 1
283
+ pool.size.should eq 2
284
+ checkpoint.check
285
+ end.resume
286
+ pool.instance_variable_get(:@pending).length.should eq 0
287
+ end
288
+ pool.max_size.should eq 2
289
+ pool.available.length.should eq 2
290
+ pool.allocated.length.should eq 0
291
+ pool.size.should eq 2
292
+ checkpoint.check
293
+ end
294
+
295
+ it "should hold deferred commands concurrently" do
296
+ ::EM.should_not_receive(:next_tick)
297
+ client.should_not_receive(:new)
298
+ client.should_receive(:connect_defer) do
299
+ deferrable.new.tap {|d| d.succeed client.allocate }
300
+ end.twice
301
+ checkpoint.should_receive(:call).exactly(4)
302
+ checkpoint.should_receive(:check).exactly(9)
303
+ pool = subject.new connection_class: client, size: 2, lazy: true
304
+ pool.should be_an_instance_of subject
305
+ pool.max_size.should eq 2
306
+ pool.available.length.should eq 0
307
+ pool.allocated.length.should eq 0
308
+ pool.size.should eq 0
309
+ result = []
310
+ df = pool.__send__(:hold_deferred, checkpoint) do |conn1|
311
+ conn1.should be_an_instance_of client
312
+ pool.available.length.should eq 0
313
+ pool.allocated.length.should eq 1
314
+ checkpoint.check
315
+ df2 = pool.__send__(:hold_deferred, checkpoint) do |conn2|
316
+ conn2.should be_an_instance_of client
317
+ conn2.should_not be conn1
318
+ pool.available.length.should eq 0
319
+ pool.allocated.length.should eq 2
320
+ checkpoint.check
321
+ df3 = pool.__send__(:hold_deferred, checkpoint) do |conn3|
322
+ conn3.should be_an_instance_of client
323
+ conn3.should be conn2
324
+ pool.available.length.should eq 0
325
+ pool.allocated.length.should eq 2
326
+ checkpoint.check
327
+ deferrable.new.tap {|d| d.succeed :result3 }
328
+ end
329
+ df3.should be_an_instance_of deferrable
330
+ df3.should_not be df
331
+ df3.should_not be df2
332
+ df3.callback do |result3|
333
+ result << result3
334
+ result3.should eq :result3
335
+ pool.available.length.should eq 1
336
+ pool.allocated.length.should eq 1
337
+ checkpoint.check
338
+ end
339
+ pool.instance_variable_get(:@pending).length.should eq 1
340
+ deferrable.new.tap {|d| d.succeed :result2 }
341
+ end
342
+ df2.should be_an_instance_of deferrable
343
+ df2.should_not be df
344
+ df2.callback do |result2|
345
+ result << result2
346
+ result2.should eq :result2
347
+ pool.available.length.should eq 1
348
+ pool.allocated.length.should eq 1
349
+ checkpoint.check
350
+ end
351
+ pool.instance_variable_get(:@pending).length.should eq 0
352
+ deferrable.new.tap {|d| d.succeed :result1 }
353
+ end
354
+ df.should be_an_instance_of deferrable
355
+ df.callback do |result1|
356
+ result << result1
357
+ result1.should eq :result1
358
+ pool.available.length.should eq 2
359
+ pool.allocated.length.should eq 0
360
+ checkpoint.check
361
+ df4 = pool.__send__(:hold_deferred, checkpoint) do |conn4|
362
+ conn4.should be_an_instance_of client
363
+ pool.available.length.should eq 1
364
+ pool.allocated.length.should eq 1
365
+ checkpoint.check
366
+ deferrable.new.tap {|d| d.succeed :result4 }
367
+ end
368
+ df4.should be_an_instance_of deferrable
369
+ df4.should_not be df
370
+ df4.callback do |result4|
371
+ result << result4
372
+ result4.should eq :result4
373
+ pool.available.length.should eq 2
374
+ pool.allocated.length.should eq 0
375
+ checkpoint.check
376
+ end
377
+ end
378
+ pool.available.length.should eq 2
379
+ pool.allocated.length.should eq 0
380
+ pool.size.should eq 2
381
+ result.should eq [:result3, :result2, :result1, :result4]
382
+ checkpoint.check
383
+ end
384
+
385
+ it "should drop failed connection while connecting" do
386
+ pool = nil
387
+ ::EM.should_not_receive(:next_tick)
388
+ client.should_receive(:new) do
389
+ if pool
390
+ pool.available.length.should eq 0
391
+ pool.allocated.length.should eq 1
392
+ pool.size.should eq 1
393
+ end
394
+ raise PG::Error
395
+ end.twice
396
+ expect do
397
+ subject.new connection_class: client, size: 1
398
+ end.to raise_error PG::Error
399
+
400
+ pool = subject.new connection_class: client, size: 1, lazy: true
401
+ pool.should be_an_instance_of subject
402
+ pool.max_size.should eq 1
403
+ pool.available.length.should eq 0
404
+ pool.allocated.length.should eq 0
405
+ pool.size.should eq 0
406
+ expect do
407
+ pool.hold
408
+ end.to raise_error PG::Error
409
+ pool.max_size.should eq 1
410
+ pool.available.length.should eq 0
411
+ pool.allocated.length.should eq 0
412
+ pool.size.should eq 0
413
+ end
414
+
415
+ it "should drop failed connection while connecting asynchronously" do
416
+ pool = nil
417
+ ::EM.should_not_receive(:next_tick)
418
+ client.should_not_receive(:new)
419
+ client.should_receive(:connect_defer) do
420
+ if pool
421
+ pool.available.length.should eq 0
422
+ pool.allocated.length.should eq 1
423
+ pool.size.should eq 1
424
+ end
425
+ deferrable.new.tap {|d| d.fail pgerror }
426
+ end.twice
427
+ checkpoint.should_receive(:call).with(pgerror).exactly(3)
428
+ df = subject.connect_defer connection_class: client, size: 1
429
+ df.should be_an_instance_of deferrable
430
+ df.errback(&checkpoint)
431
+
432
+ pool = subject.new connection_class: client, size: 1, lazy: true
433
+ pool.should be_an_instance_of subject
434
+ pool.max_size.should eq 1
435
+ pool.available.length.should eq 0
436
+ pool.allocated.length.should eq 0
437
+ pool.size.should eq 0
438
+ df = pool.__send__(:hold_deferred, checkpoint)
439
+ df.should be_an_instance_of deferrable
440
+ df.errback(&checkpoint)
441
+ pool.max_size.should eq 1
442
+ pool.available.length.should eq 0
443
+ pool.allocated.length.should eq 0
444
+ pool.size.should eq 0
445
+ end
446
+
447
+ it "should drop only failed connection on error" do
448
+ pool = nil
449
+ ::EM.should_not_receive(:next_tick)
450
+ client.should_receive(:new) do
451
+ if pool
452
+ pool.available.length.should eq 0
453
+ pool.allocated.length.should eq 1
454
+ pool.size.should eq 1
455
+ end
456
+ client.allocate
457
+ end.twice
458
+ checkpoint.should_receive(:check).exactly(2)
459
+ pool = subject.new connection_class: client, size: 1
460
+ pool.should be_an_instance_of subject
461
+ pool.max_size.should eq 1
462
+ pool.available.length.should eq 1
463
+ pool.allocated.length.should eq 0
464
+ pool.size.should eq 1
465
+ expect do
466
+ pool.hold do |conn|
467
+ conn.should be_an_instance_of client
468
+ pool.available.length.should eq 0
469
+ pool.allocated.length.should eq 1
470
+ pool.size.should eq 1
471
+ conn.should_receive(:status).once.and_return(PG::CONNECTION_BAD)
472
+ conn.should_receive(:finished?).once.and_return(false)
473
+ conn.should_receive(:finish).once
474
+ checkpoint.check
475
+ raise PG::Error
476
+ end
477
+ end.to raise_error PG::Error
478
+ pool.max_size.should eq 1
479
+ pool.available.length.should eq 0
480
+ pool.allocated.length.should eq 0
481
+ pool.size.should eq 0
482
+ expect do
483
+ pool.hold do |conn|
484
+ pool.available.length.should eq 0
485
+ pool.allocated.length.should eq 1
486
+ pool.size.should eq 1
487
+ conn.should be_an_instance_of client
488
+ conn.should_not_receive(:status)
489
+ conn.should_not_receive(:finished?)
490
+ conn.should_not_receive(:finish)
491
+ checkpoint.check
492
+ raise 'foo'
493
+ end
494
+ end.to raise_error RuntimeError, 'foo'
495
+ pool.available.length.should eq 1
496
+ pool.allocated.length.should eq 0
497
+ pool.size.should eq 1
498
+ end
499
+
500
+ it "should drop only failed connection on deferred error" do
501
+ pool = nil
502
+ ::EM.should_not_receive(:next_tick)
503
+ client.should_not_receive(:new)
504
+ client.should_receive(:connect_defer) do
505
+ if pool
506
+ pool.available.length.should eq 0
507
+ pool.allocated.length.should eq 1
508
+ pool.size.should eq 1
509
+ end
510
+ deferrable.new.tap {|d| d.succeed client.allocate }
511
+ end.twice
512
+ checkpoint.should_receive(:check).exactly(2)
513
+ checkpoint.should_receive(:call).with(pgerror).exactly(2)
514
+ checkpoint.should_receive(:call).with(fooerror).exactly(2)
515
+ pool = subject.new connection_class: client, size: 1, lazy: true
516
+ pool.should be_an_instance_of subject
517
+ pool.max_size.should eq 1
518
+ pool.available.length.should eq 0
519
+ pool.allocated.length.should eq 0
520
+ pool.size.should eq 0
521
+ df = pool.__send__(:hold_deferred, checkpoint) do |conn|
522
+ pool.available.length.should eq 0
523
+ pool.allocated.length.should eq 1
524
+ pool.size.should eq 1
525
+ conn.should be_an_instance_of client
526
+ conn.should_receive(:status).once.and_return(PG::CONNECTION_BAD)
527
+ conn.should_receive(:finished?).once.and_return(false)
528
+ conn.should_receive(:finish).once
529
+ checkpoint.check
530
+ deferrable.new.tap {|d| d.fail pgerror }
531
+ end
532
+ df.should be_an_instance_of deferrable
533
+ df.errback(&checkpoint)
534
+ pool.max_size.should eq 1
535
+ pool.available.length.should eq 0
536
+ pool.allocated.length.should eq 0
537
+ pool.size.should eq 0
538
+ df = pool.__send__(:hold_deferred, checkpoint) do |conn|
539
+ pool.available.length.should eq 0
540
+ pool.allocated.length.should eq 1
541
+ pool.size.should eq 1
542
+ conn.should be_an_instance_of client
543
+ conn.should_not_receive(:status)
544
+ conn.should_not_receive(:finished?)
545
+ conn.should_not_receive(:finish)
546
+ checkpoint.check
547
+ deferrable.new.tap {|d| d.fail fooerror }
548
+ end
549
+ df.should be_an_instance_of deferrable
550
+ df.errback(&checkpoint)
551
+ pool.max_size.should eq 1
552
+ pool.available.length.should eq 1
553
+ pool.allocated.length.should eq 0
554
+ pool.size.should eq 1
555
+ end
556
+
557
+ let(:pool_size) { 7 }
558
+
559
+ it "pending requests should not starve on failed connections" do
560
+ pool = nil
561
+ client.should_receive(:new) do
562
+ pool.available.length.should eq 0
563
+ pool.size.should be > 0
564
+ pool.size.should be <= pool_size
565
+ pool.size.should eq pool.allocated.length
566
+ sleep_one_tick
567
+ raise PG::ConnectionBad
568
+ end.exactly(100)
569
+ client.should_receive(:connect_defer) do
570
+ pool.available.length.should eq 0
571
+ pool.size.should be > 0
572
+ pool.size.should be <= pool_size
573
+ pool.size.should eq pool.allocated.length
574
+ deferrable.new.tap do |df|
575
+ EM.next_tick { df.fail pgerror }
576
+ end
577
+ end.exactly(100)
578
+ pool = subject.new connection_class: client, lazy: true, size: pool_size
579
+ pool.should be_an_instance_of subject
580
+ pool.max_size.should eq pool_size
581
+ pool.size.should eq 0
582
+
583
+ queries = %w[fee bzz bzz fee bzz fee fee bzz fee bzz]*2
584
+ 10.times do
585
+ test_queries pool, queries.rotate! do |progress|
586
+ pending = pool.instance_variable_get(:@pending).length
587
+ expected_pending = progress.length - pool.max_size
588
+ pending.should eq [0, expected_pending].max
589
+ pool.available.length.should eq 0
590
+ pool.size.should be >= [pool_size, expected_pending].min
591
+ end
592
+ end
593
+
594
+ pool.max_size.should eq pool_size
595
+ pool.size.should eq 0
596
+ end
597
+
598
+ it "pending requests should not starve on commands with failing connections" do
599
+ pool = nil
600
+ expected_connections = if pool_size < 20
601
+ 200 + pool_size
602
+ elsif pool_size < 40
603
+ 220
604
+ elsif pool_size < 60
605
+ 180 + pool_size
606
+ else
607
+ 240
608
+ end
609
+ checkpoint.should_receive(:check_fiber).exactly(300)
610
+ checkpoint.should_receive(:check_defer).exactly(300)
611
+ checkpoint.should_receive(:connect).exactly(expected_connections)
612
+ client.stub(:new) do
613
+ pool.available.length.should eq 0
614
+ pool.size.should be > 0
615
+ pool.size.should be <= pool_size
616
+ pool.size.should eq pool.allocated.length
617
+ sleep_one_tick
618
+ checkpoint.connect
619
+ create_connection
620
+ end
621
+ pool = subject.new connection_class: client, lazy: true, size: pool_size
622
+ client.stub(:connect_defer) do
623
+ pool.available.length.should eq 0
624
+ pool.size.should be > 0
625
+ pool.size.should be <= pool_size
626
+ pool.size.should eq pool.allocated.length
627
+ checkpoint.connect
628
+ deferrable.new.tap do |df|
629
+ EM.next_tick { df.succeed create_connection }
630
+ end
631
+ end
632
+ pool.should be_an_instance_of subject
633
+ pool.max_size.should eq pool_size
634
+ pool.size.should eq 0
635
+
636
+ good_queries = %w[foo bar bar foo bar foo foo bar foo bar]*2
637
+ disconnecting_queries = %w[fee bzz bzz fee bzz fee fee bzz fee bzz]*2
638
+
639
+ 10.times do
640
+ test_queries pool, good_queries + disconnecting_queries + good_queries do |progress|
641
+ pending = pool.instance_variable_get(:@pending).length
642
+ expected_pending = progress.length - pool.max_size
643
+ pending.should eq [0, expected_pending].max
644
+ pool.size.should be > 0
645
+ end
646
+ good_queries.rotate!
647
+ disconnecting_queries.rotate!
648
+ end
649
+
650
+ pool.max_size.should eq pool_size
651
+ pool.available.length.should eq pool.size
652
+ pool.size.should eq expected_connections - 200
653
+ end
654
+
655
+ end