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.
- data/.yardopts +1 -0
- data/{BENCHMARKS.rdoc → BENCHMARKS.md} +15 -10
- data/{HISTORY.rdoc → HISTORY.md} +32 -5
- data/LICENSE +21 -0
- data/README.md +392 -0
- data/Rakefile +30 -14
- data/em-pg-client.gemspec +8 -7
- data/lib/em-pg-client.rb +1 -0
- data/lib/em-synchrony/pg.rb +2 -107
- data/lib/pg/em-version.rb +5 -0
- data/lib/pg/em.rb +638 -344
- data/lib/pg/em/client/connect_watcher.rb +65 -0
- data/lib/pg/em/client/watcher.rb +102 -0
- data/lib/pg/em/connection_pool.rb +448 -0
- data/lib/pg/em/featured_deferrable.rb +43 -0
- data/spec/connection_pool_helpers.rb +89 -0
- data/spec/{em_devel_client.rb → em_client.rb} +3 -2
- data/spec/em_client_autoreconnect.rb +268 -144
- data/spec/em_client_common.rb +55 -54
- data/spec/em_synchrony_client.rb +254 -5
- data/spec/em_synchrony_client_autoreconnect.rb +154 -130
- data/spec/pg_em_client_connect_finish.rb +54 -0
- data/spec/pg_em_client_connect_timeout.rb +91 -0
- data/spec/pg_em_client_options.rb +85 -0
- data/spec/pg_em_connection_pool.rb +655 -0
- data/spec/pg_em_featured_deferrable.rb +125 -0
- metadata +64 -34
- data/README.rdoc +0 -431
- data/spec/em_release_client.rb +0 -39
@@ -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
|