em-pg-client-12 0.3.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.
- checksums.yaml +7 -0
- data/.rspec +1 -0
- data/.travis.yml +32 -0
- data/.yardopts +1 -0
- data/BENCHMARKS.md +91 -0
- data/Gemfile +5 -0
- data/HISTORY.md +107 -0
- data/LICENSE +21 -0
- data/README.md +456 -0
- data/Rakefile +157 -0
- data/benchmarks/em_pg.rb +96 -0
- data/benchmarks/single_row_mode.rb +88 -0
- data/em-pg-client.gemspec +34 -0
- data/examples/single_row_mode.rb +57 -0
- data/lib/em-pg-client.rb +1 -0
- data/lib/em-synchrony/pg.rb +3 -0
- data/lib/pg/em-version.rb +5 -0
- data/lib/pg/em.rb +1129 -0
- data/lib/pg/em/client/connect_watcher.rb +89 -0
- data/lib/pg/em/client/watcher.rb +204 -0
- data/lib/pg/em/connection_pool.rb +480 -0
- data/lib/pg/em/featured_deferrable.rb +43 -0
- data/spec/connection_pool_helpers.rb +89 -0
- data/spec/em_client.rb +33 -0
- data/spec/em_client_autoreconnect.rb +672 -0
- data/spec/em_client_common.rb +619 -0
- data/spec/em_client_on_connect.rb +171 -0
- data/spec/em_connection_pool.rb +200 -0
- data/spec/em_synchrony_client.rb +787 -0
- data/spec/em_synchrony_client_autoreconnect.rb +560 -0
- 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 +133 -0
- data/spec/pg_em_connection_pool.rb +679 -0
- data/spec/pg_em_featured_deferrable.rb +125 -0
- data/spec/spec_helper.rb +9 -0
- metadata +187 -0
@@ -0,0 +1,619 @@
|
|
1
|
+
NOTIFY_PAYLOAD = defined?(PG.library_version) && PG.library_version >= 90000
|
2
|
+
NOTIFY_PAYLOAD_QUERY = NOTIFY_PAYLOAD ? %q[NOTIFY "ruby-em-pg-client", 'foo'] : %q[NOTIFY "ruby-em-pg-client"]
|
3
|
+
|
4
|
+
module PGSpecMacros
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
def pg_exec_and_check(client, method, *args, &additional_checks)
|
10
|
+
client.__send__(method, *args) do |result|
|
11
|
+
result.should be_an_instance_of PG::Result
|
12
|
+
additional_checks.call(result) if additional_checks
|
13
|
+
EM.stop
|
14
|
+
end.should be_a_kind_of ::EM::Deferrable
|
15
|
+
end
|
16
|
+
|
17
|
+
def pg_exec_and_check_with_error(client, stop, err_class, err_message, method, *args, &additional_checks)
|
18
|
+
client.__send__(method, *args) do |exception|
|
19
|
+
exception.should be_an_instance_of err_class
|
20
|
+
exception.to_s.should include err_message if err_message
|
21
|
+
additional_checks.call(exception) if additional_checks
|
22
|
+
EM.next_tick { EM.stop } if stop
|
23
|
+
end.should be_a_kind_of ::EM::Deferrable
|
24
|
+
end
|
25
|
+
|
26
|
+
def ensure_em_stop
|
27
|
+
yield
|
28
|
+
ensure
|
29
|
+
EM.stop
|
30
|
+
end
|
31
|
+
|
32
|
+
module ClassMethods
|
33
|
+
def it_should_execute(text, method, *args)
|
34
|
+
it "should #{text}" do
|
35
|
+
pg_exec_and_check(@client, method, *args)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def it_should_execute_with_error(text, err_class, err_message, method, *args)
|
40
|
+
it "should #{text}" do
|
41
|
+
pg_exec_and_check_with_error(@client, true, err_class, err_message, method, *args)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def it_should_rollback
|
46
|
+
it_should_execute("rollback transaction", :query_defer, 'ROLLBACK')
|
47
|
+
end
|
48
|
+
|
49
|
+
def it_should_begin
|
50
|
+
it_should_execute("begin transaction", :query_defer, 'BEGIN TRANSACTION')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
shared_context 'em-pg common before' do
|
56
|
+
|
57
|
+
around(:each) do |testcase|
|
58
|
+
EM.run do
|
59
|
+
EM.stop if testcase.call.is_a? Exception
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
before(:all) do
|
64
|
+
@cdates = []
|
65
|
+
@values = Array(('AA'..'ZZ').each_with_index)
|
66
|
+
ENV['PGCLIENTENCODING'] = nil
|
67
|
+
Encoding.default_internal = nil
|
68
|
+
@client = described_class.new
|
69
|
+
end
|
70
|
+
|
71
|
+
after(:all) do
|
72
|
+
@client.close
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should be a client #{PG::VERSION}" do
|
76
|
+
ensure_em_stop do
|
77
|
+
@client.should be_an_instance_of described_class
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should have disabled async_autoreconnect" do
|
82
|
+
ensure_em_stop do
|
83
|
+
@client.async_autoreconnect.should be_false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should enable async_autoreconnect" do
|
88
|
+
ensure_em_stop do
|
89
|
+
@client.async_autoreconnect = true
|
90
|
+
@client.async_autoreconnect.should be_true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should have same internal and external encoding" do
|
95
|
+
ensure_em_stop do
|
96
|
+
@client.external_encoding.should be @client.internal_encoding
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it_should_begin
|
101
|
+
|
102
|
+
it_should_execute("drop table `foo` if exists",
|
103
|
+
:query_defer, 'DROP TABLE IF EXISTS foo')
|
104
|
+
|
105
|
+
it_should_execute("create simple table `foo`",
|
106
|
+
:query_defer, 'CREATE TABLE foo (id integer,cdate timestamp with time zone,data varchar)')
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
shared_context 'em-pg common after' do
|
111
|
+
|
112
|
+
if described_class.single_row_mode?
|
113
|
+
|
114
|
+
it "should get each result in single row mode" do
|
115
|
+
@client.single_row_mode?.should be_true
|
116
|
+
@client.get_result_defer do |result|
|
117
|
+
result.should be_nil
|
118
|
+
@client.send_query('SELECT data, id FROM foo order by id')
|
119
|
+
@client.set_single_row_mode
|
120
|
+
EM::Iterator.new(@values, 1).map(proc{ |(data, id), iter|
|
121
|
+
@client.get_result_defer do |result|
|
122
|
+
result.should be_an_instance_of PG::Result
|
123
|
+
result.check
|
124
|
+
result.result_status.should eq PG::PGRES_SINGLE_TUPLE
|
125
|
+
value = result.to_a
|
126
|
+
value.should eq [{'data' => data, 'id' => id.to_s}]
|
127
|
+
result.clear
|
128
|
+
iter.return value
|
129
|
+
end.should be_a_kind_of ::EM::Deferrable
|
130
|
+
}, proc{ |results|
|
131
|
+
results.length.should eq @values.length
|
132
|
+
@client.get_result_defer do |result|
|
133
|
+
result.should be_an_instance_of PG::Result
|
134
|
+
result.check
|
135
|
+
result.result_status.should eq PG::PGRES_TUPLES_OK
|
136
|
+
result.to_a.should eq []
|
137
|
+
result.clear
|
138
|
+
@client.get_result_defer do |result|
|
139
|
+
result.should be_nil
|
140
|
+
EM.stop
|
141
|
+
end.should be_a_kind_of ::EM::Deferrable
|
142
|
+
end.should be_a_kind_of ::EM::Deferrable
|
143
|
+
})
|
144
|
+
end.should be_a_kind_of ::EM::Deferrable
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
it_should_execute("create prepared statement",
|
150
|
+
:prepare_defer, 'get_foo', 'SELECT * FROM foo order by id')
|
151
|
+
|
152
|
+
it "should describe prepared statement" do
|
153
|
+
pg_exec_and_check(@client, :describe_prepared_defer, 'get_foo') do |result|
|
154
|
+
result.nfields.should eq 3
|
155
|
+
result.fname(0).should eq 'id'
|
156
|
+
result.values.should be_empty
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should read foo table with prepared statement" do
|
161
|
+
pg_exec_and_check(@client, :exec_prepared_defer, 'get_foo') do |result|
|
162
|
+
result.each_with_index do |row, i|
|
163
|
+
row['id'].to_i.should == i
|
164
|
+
DateTime.parse(row['cdate']).should == @cdates[i]
|
165
|
+
row['data'].should == @values[i][0]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
it_should_execute("declare cursor",
|
171
|
+
:query_defer, 'DECLARE foobar SCROLL CURSOR FOR SELECT * FROM foo')
|
172
|
+
|
173
|
+
it "should fetch two rows from table" do
|
174
|
+
pg_exec_and_check(@client, :query_defer, 'FETCH FORWARD 2 FROM foobar') do |result|
|
175
|
+
result.nfields.should eq 3
|
176
|
+
result.fname(0).should eq 'id'
|
177
|
+
result.values.length.should eq 2
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should describe cursor with describe_portal" do
|
182
|
+
pg_exec_and_check(@client, :describe_portal_defer, 'foobar') do |result|
|
183
|
+
result.nfields.should eq 3
|
184
|
+
result.fname(0).should eq 'id'
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
it_should_execute("close cursor", :query_defer, 'CLOSE foobar')
|
189
|
+
|
190
|
+
it "should connect to database asynchronously" do
|
191
|
+
this = :first
|
192
|
+
Encoding.default_internal = Encoding::ISO_8859_1
|
193
|
+
described_class.connect_defer do |conn|
|
194
|
+
this = :second
|
195
|
+
Encoding.default_internal = nil
|
196
|
+
conn.should be_an_instance_of described_class
|
197
|
+
conn.external_encoding.should_not eq(conn.internal_encoding)
|
198
|
+
conn.internal_encoding.should be Encoding::ISO_8859_1
|
199
|
+
conn.get_client_encoding.should eq "LATIN1"
|
200
|
+
pg_exec_and_check(conn, :query_defer, 'SELECT pg_database_size(current_database());') do |result|
|
201
|
+
result[0]['pg_database_size'].to_i.should be > 0
|
202
|
+
end
|
203
|
+
end.should be_a_kind_of ::EM::Deferrable
|
204
|
+
this.should be :first
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should connect without setting incompatible encoding" do
|
208
|
+
this = :first
|
209
|
+
Encoding.default_internal = Encoding::Emacs_Mule
|
210
|
+
described_class.connect_defer do |conn|
|
211
|
+
this = :second
|
212
|
+
Encoding.default_internal = nil
|
213
|
+
conn.should be_an_instance_of described_class
|
214
|
+
conn.external_encoding.should be conn.internal_encoding
|
215
|
+
EM.stop
|
216
|
+
end.should be_a_kind_of ::EM::Deferrable
|
217
|
+
this.should be :first
|
218
|
+
end
|
219
|
+
|
220
|
+
it_should_execute_with_error("raise syntax error in misspelled multiple statement",
|
221
|
+
PG::SyntaxError,
|
222
|
+
"syntax error",
|
223
|
+
:query_defer, 'SELECT * from pg_class; SRELECT CURRENT_TIMESTAMP; SELECT 42 number')
|
224
|
+
|
225
|
+
it_should_rollback
|
226
|
+
|
227
|
+
it "should return only last statement" do
|
228
|
+
pg_exec_and_check(@client, :query_defer,
|
229
|
+
'SELECT * from pg_class; SELECT CURRENT_TIMESTAMP; SELECT 42 number') do |result|
|
230
|
+
result[0]['number'].should eq "42"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should timeout expire while executing query" do
|
235
|
+
@client.query_timeout.should eq 0
|
236
|
+
@client.query_timeout = 1.5
|
237
|
+
@client.query_timeout.should eq 1.5
|
238
|
+
start_time = Time.now
|
239
|
+
pg_exec_and_check_with_error(@client, false,
|
240
|
+
PG::ConnectionBad, "query timeout expired",
|
241
|
+
:query_defer, 'SELECT pg_sleep(2)') do
|
242
|
+
(Time.now - start_time).should be < 2
|
243
|
+
@client.async_command_aborted.should be_true
|
244
|
+
@client.status.should be PG::CONNECTION_BAD
|
245
|
+
@client.query_timeout = 0
|
246
|
+
@client.query_timeout.should eq 0
|
247
|
+
@client.async_autoreconnect = false
|
248
|
+
pg_exec_and_check_with_error(@client, true,
|
249
|
+
PG::ConnectionBad, "previous query expired",
|
250
|
+
:query_defer, 'SELECT 1') do
|
251
|
+
@client.async_autoreconnect = true
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should timeout not expire while executing query with partial results" do
|
257
|
+
@client.query_timeout.should eq 0
|
258
|
+
@client.query_timeout = 1.1
|
259
|
+
@client.query_timeout.should eq 1.1
|
260
|
+
start_time = Time.now
|
261
|
+
pg_exec_and_check(@client, :query_defer,
|
262
|
+
'SELECT * from pg_class;' +
|
263
|
+
'SELECT pg_sleep(1);' +
|
264
|
+
'SELECT * from pg_class;' +
|
265
|
+
'SELECT pg_sleep(1);' +
|
266
|
+
'SELECT 42 number') do |result|
|
267
|
+
(Time.now - start_time).should be > 2
|
268
|
+
result[0]['number'].should eq "42"
|
269
|
+
@client.query_timeout = 0
|
270
|
+
@client.query_timeout.should eq 0
|
271
|
+
@client.async_command_aborted.should be_false
|
272
|
+
@client.status.should be PG::CONNECTION_OK
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should timeout expire while executing query with partial results" do
|
277
|
+
@client.query_timeout.should eq 0
|
278
|
+
@client.query_timeout = 1.1
|
279
|
+
@client.query_timeout.should eq 1.1
|
280
|
+
start_time = Time.now
|
281
|
+
pg_exec_and_check_with_error(@client, true,
|
282
|
+
PG::ConnectionBad, "query timeout expired",
|
283
|
+
:query_defer,
|
284
|
+
'SELECT * from pg_class;' +
|
285
|
+
'SELECT pg_sleep(1);' +
|
286
|
+
'SELECT * from pg_class;' +
|
287
|
+
'SELECT pg_sleep(2);' +
|
288
|
+
'SELECT 42 number') do
|
289
|
+
(Time.now - start_time).should be > 2
|
290
|
+
@client.async_command_aborted.should be_true
|
291
|
+
@client.status.should be PG::CONNECTION_BAD
|
292
|
+
@client.query_timeout = 0
|
293
|
+
@client.query_timeout.should eq 0
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
it "should clear connection with blocking reset" do
|
298
|
+
ensure_em_stop do
|
299
|
+
@client.async_command_aborted.should be_true
|
300
|
+
@client.status.should be PG::CONNECTION_BAD
|
301
|
+
@client.reset
|
302
|
+
@client.async_command_aborted.should be_false
|
303
|
+
@client.status.should be PG::CONNECTION_OK
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
it "should not expire after executing erraneous query" do
|
308
|
+
@client.query_timeout.should eq 0
|
309
|
+
@client.query_timeout = 0.1
|
310
|
+
@client.query_timeout.should eq 0.1
|
311
|
+
start_time = Time.now
|
312
|
+
pg_exec_and_check_with_error(@client, false,
|
313
|
+
PG::SyntaxError, "syntax error",
|
314
|
+
:query_defer, 'SELLECT 1') do
|
315
|
+
@client.async_command_aborted.should be_false
|
316
|
+
::EM.add_timer(0.11) do
|
317
|
+
@client.async_command_aborted.should be_false
|
318
|
+
@client.status.should be PG::CONNECTION_OK
|
319
|
+
@client.query_timeout = 0
|
320
|
+
@client.query_timeout.should eq 0
|
321
|
+
EM.stop
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
it "should get last result asynchronously" do
|
327
|
+
@client.get_last_result_defer do |result|
|
328
|
+
result.should be_nil
|
329
|
+
@client.send_query('SELECT 1; SELECT 2; SELECT 3')
|
330
|
+
@client.get_last_result_defer do |result|
|
331
|
+
result.should be_an_instance_of PG::Result
|
332
|
+
result.getvalue(0,0).should eq '3'
|
333
|
+
result.clear
|
334
|
+
@client.get_last_result_defer do |result|
|
335
|
+
result.should be_nil
|
336
|
+
EM.stop
|
337
|
+
end
|
338
|
+
end.should be_a_kind_of ::EM::Deferrable
|
339
|
+
end.should be_a_kind_of ::EM::Deferrable
|
340
|
+
end
|
341
|
+
|
342
|
+
it "should get each result asynchronously" do
|
343
|
+
@client.get_result_defer do |result|
|
344
|
+
result.should be_nil
|
345
|
+
@client.send_query('SELECT 4; SELECT 5; SELECT 6')
|
346
|
+
EM::Iterator.new(%w[4 5 6], 1).map(proc{ |value, iter|
|
347
|
+
@client.get_result_defer do |result|
|
348
|
+
result.should be_an_instance_of PG::Result
|
349
|
+
result.check
|
350
|
+
result.result_status.should eq PG::PGRES_TUPLES_OK
|
351
|
+
result.getvalue(0,0).should eq value
|
352
|
+
result.clear
|
353
|
+
iter.return value
|
354
|
+
end.should be_a_kind_of ::EM::Deferrable
|
355
|
+
}, proc{ |results|
|
356
|
+
results.should eq %w[4 5 6]
|
357
|
+
@client.get_result_defer do |result|
|
358
|
+
result.should be_nil
|
359
|
+
EM.stop
|
360
|
+
end.should be_a_kind_of ::EM::Deferrable
|
361
|
+
})
|
362
|
+
end.should be_a_kind_of ::EM::Deferrable
|
363
|
+
end
|
364
|
+
|
365
|
+
it "should raise connection reset on connection breakdown" do
|
366
|
+
client = described_class.new query_timeout: 0.1
|
367
|
+
client.send_query('SELECT pg_sleep(10)')
|
368
|
+
client.get_last_result_defer do |ex|
|
369
|
+
ex.should be_an_instance_of PG::ConnectionBad
|
370
|
+
ex.message.should match /connection reset/
|
371
|
+
EM.add_timer(0.2) do
|
372
|
+
client.async_command_aborted.should be_false
|
373
|
+
EM.stop
|
374
|
+
end
|
375
|
+
end
|
376
|
+
# simulate connection breakdown
|
377
|
+
client.finish
|
378
|
+
end
|
379
|
+
|
380
|
+
it "should return nil result after async reset began" do
|
381
|
+
checkpoint = 0
|
382
|
+
@client.send_query('SELECT 1')
|
383
|
+
@client.reset_defer do |conn|
|
384
|
+
conn.should be @client
|
385
|
+
EM.next_tick do
|
386
|
+
checkpoint.should eq 2
|
387
|
+
@client.send_query('SELECT 1')
|
388
|
+
@client.get_last_result_defer do |result|
|
389
|
+
result.should be_an_instance_of PG::Result
|
390
|
+
result.getvalue(0,0).should eq '1'
|
391
|
+
EM.stop
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
@client.get_result_defer do |result|
|
396
|
+
result.should be_nil
|
397
|
+
checkpoint += 1
|
398
|
+
end
|
399
|
+
@client.get_last_result_defer do |result|
|
400
|
+
result.should be_nil
|
401
|
+
checkpoint += 1
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
it "should receive notification while waiting for it" do
|
406
|
+
sender = described_class.new
|
407
|
+
sender_pid = nil
|
408
|
+
wait_over = false
|
409
|
+
@client.query_defer('LISTEN "ruby-em-pg-client"') do |result|
|
410
|
+
result.should be_an_instance_of PG::Result
|
411
|
+
@client.wait_for_notify_defer do |notification|
|
412
|
+
notification.should be_an_instance_of Hash
|
413
|
+
notification[:relname].should eq 'ruby-em-pg-client'
|
414
|
+
notification[:be_pid].should eq sender_pid
|
415
|
+
notification[:extra].should eq (NOTIFY_PAYLOAD ? 'foo' : '')
|
416
|
+
@client.query_defer('UNLISTEN *') do |result|
|
417
|
+
result.should be_an_instance_of PG::Result
|
418
|
+
EM.stop unless sender
|
419
|
+
wait_over = true
|
420
|
+
end
|
421
|
+
end.should be_a_kind_of ::EM::Deferrable
|
422
|
+
EM.next_tick do
|
423
|
+
sender.query_defer('SELECT pg_backend_pid()') do |result|
|
424
|
+
result.should be_an_instance_of PG::Result
|
425
|
+
sender_pid = result.getvalue(0,0).to_i
|
426
|
+
sender.query_defer(NOTIFY_PAYLOAD_QUERY) do |result|
|
427
|
+
result.should be_an_instance_of PG::Result
|
428
|
+
sender.finish
|
429
|
+
sender = nil
|
430
|
+
EM.stop if wait_over
|
431
|
+
end.should be_a_kind_of ::EM::Deferrable
|
432
|
+
end.should be_a_kind_of ::EM::Deferrable
|
433
|
+
end
|
434
|
+
end.should be_a_kind_of ::EM::Deferrable
|
435
|
+
end
|
436
|
+
|
437
|
+
it "should receive previously sent notification" do
|
438
|
+
@client.query_defer('LISTEN "ruby-em-pg-client"') do |result|
|
439
|
+
result.should be_an_instance_of PG::Result
|
440
|
+
@client.query_defer('SELECT pg_backend_pid()') do |result|
|
441
|
+
result.should be_an_instance_of PG::Result
|
442
|
+
sender_pid = result.getvalue(0,0).to_i
|
443
|
+
@client.query_defer(%q[NOTIFY "ruby-em-pg-client"]) do |result|
|
444
|
+
result.should be_an_instance_of PG::Result
|
445
|
+
@client.wait_for_notify_defer(1) do |notification|
|
446
|
+
notification.should be_an_instance_of Hash
|
447
|
+
notification[:relname].should eq 'ruby-em-pg-client'
|
448
|
+
notification[:be_pid].should eq sender_pid
|
449
|
+
notification[:extra].should eq ''
|
450
|
+
@client.query_defer('UNLISTEN *') do |result|
|
451
|
+
result.should be_an_instance_of PG::Result
|
452
|
+
EM.stop
|
453
|
+
end.should be_a_kind_of ::EM::Deferrable
|
454
|
+
end.should be_a_kind_of ::EM::Deferrable
|
455
|
+
end.should be_a_kind_of ::EM::Deferrable
|
456
|
+
end.should be_a_kind_of ::EM::Deferrable
|
457
|
+
end.should be_a_kind_of ::EM::Deferrable
|
458
|
+
end
|
459
|
+
|
460
|
+
it "should perform queries and receive own notification while waiting for it" do
|
461
|
+
sender_pid = nil
|
462
|
+
@client.query_defer('SELECT pg_backend_pid()') do |result|
|
463
|
+
sender_pid = result.getvalue(0,0).to_i
|
464
|
+
@client.query_defer('LISTEN "ruby-em-pg-client"') do |result|
|
465
|
+
result.should be_an_instance_of PG::Result
|
466
|
+
@client.query_defer(NOTIFY_PAYLOAD_QUERY) do |result|
|
467
|
+
result.should be_an_instance_of PG::Result
|
468
|
+
end.should be_a_kind_of ::EM::Deferrable
|
469
|
+
end.should be_a_kind_of ::EM::Deferrable
|
470
|
+
end.should be_a_kind_of ::EM::Deferrable
|
471
|
+
@client.wait_for_notify_defer do |notification|
|
472
|
+
notification.should be_an_instance_of Hash
|
473
|
+
notification[:relname].should eq 'ruby-em-pg-client'
|
474
|
+
notification[:be_pid].should eq sender_pid
|
475
|
+
notification[:extra].should eq (NOTIFY_PAYLOAD ? 'foo' : '')
|
476
|
+
@client.query_defer('UNLISTEN *') do |result|
|
477
|
+
result.should be_an_instance_of PG::Result
|
478
|
+
EM.stop
|
479
|
+
end.should be_a_kind_of ::EM::Deferrable
|
480
|
+
end.should be_a_kind_of ::EM::Deferrable
|
481
|
+
end
|
482
|
+
|
483
|
+
it "should reach timeout while waiting for notification" do
|
484
|
+
start_time = Time.now
|
485
|
+
async_flag = false
|
486
|
+
@client.wait_for_notify_defer(0.2) do |notification|
|
487
|
+
notification.should be_nil
|
488
|
+
(Time.now - start_time).should be >= 0.2
|
489
|
+
async_flag.should be_true
|
490
|
+
EM.stop
|
491
|
+
end.should be_a_kind_of ::EM::Deferrable
|
492
|
+
async_flag = true
|
493
|
+
end
|
494
|
+
|
495
|
+
it "should reach timeout while waiting for notification and executing query" do
|
496
|
+
start_time = Time.now
|
497
|
+
async_flag = false
|
498
|
+
@client.query_defer('SELECT pg_sleep(0.5)') do |result|
|
499
|
+
result.should be_an_instance_of PG::Result
|
500
|
+
(Time.now - start_time).should be >= 0.5
|
501
|
+
async_flag.should be_true
|
502
|
+
EM.stop
|
503
|
+
end
|
504
|
+
@client.wait_for_notify_defer(0.1) do |notification|
|
505
|
+
notification.should be_nil
|
506
|
+
(Time.now - start_time).should be_between(0.1, 0.5)
|
507
|
+
async_flag.should be_true
|
508
|
+
end.should be_a_kind_of ::EM::Deferrable
|
509
|
+
async_flag = true
|
510
|
+
end
|
511
|
+
|
512
|
+
it "should timeout expire while executing query and waiting for notification" do
|
513
|
+
@client.query_timeout.should eq 0
|
514
|
+
@client.query_timeout = 0.2
|
515
|
+
@client.query_timeout.should eq 0.2
|
516
|
+
visit_counter = 0
|
517
|
+
start_time = Time.now
|
518
|
+
pg_exec_and_check_with_error(@client, false,
|
519
|
+
PG::ConnectionBad, "query timeout expired (async)",
|
520
|
+
:wait_for_notify_defer) do
|
521
|
+
(Time.now - start_time).should be >= 0.2
|
522
|
+
(visit_counter+=1).should eq 2
|
523
|
+
@client.async_command_aborted.should be_true
|
524
|
+
@client.status.should be PG::CONNECTION_BAD
|
525
|
+
@client.query_timeout = 0
|
526
|
+
@client.query_timeout.should eq 0
|
527
|
+
@client.async_autoreconnect = false
|
528
|
+
pg_exec_and_check_with_error(@client, false,
|
529
|
+
PG::ConnectionBad, "previous query expired",
|
530
|
+
:wait_for_notify_defer) do
|
531
|
+
@client.reset_defer do |conn|
|
532
|
+
conn.should be @client
|
533
|
+
@client.async_autoreconnect = true
|
534
|
+
EM.stop
|
535
|
+
end
|
536
|
+
end
|
537
|
+
end
|
538
|
+
pg_exec_and_check_with_error(@client, false,
|
539
|
+
PG::ConnectionBad, "query timeout expired (async)",
|
540
|
+
:query_defer, 'SELECT pg_sleep(1)') do
|
541
|
+
(Time.now - start_time).should be_between(0.2, 1)
|
542
|
+
(visit_counter+=1).should eq 1
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
it "should fail wait_for_notify on connection reset" do
|
547
|
+
@client.status.should be PG::CONNECTION_OK
|
548
|
+
visit_counter = 0
|
549
|
+
pg_exec_and_check_with_error(@client, false,
|
550
|
+
PG::ConnectionBad, "connection reset",
|
551
|
+
:wait_for_notify_defer) do
|
552
|
+
pg_exec_and_check_with_error(@client, false,
|
553
|
+
PG::ConnectionBad, "connection reset",
|
554
|
+
:wait_for_notify_defer) do
|
555
|
+
(visit_counter+=1).should eq 2
|
556
|
+
end
|
557
|
+
@client.reset_defer do |conn|
|
558
|
+
conn.should be @client
|
559
|
+
(visit_counter+=1).should eq 3
|
560
|
+
EM.stop
|
561
|
+
end
|
562
|
+
end
|
563
|
+
@client.reset
|
564
|
+
(visit_counter+=1).should eq 1
|
565
|
+
end
|
566
|
+
|
567
|
+
it "should fail wait_for_notify and slow query on connection reset" do
|
568
|
+
@client.status.should be PG::CONNECTION_OK
|
569
|
+
visit_counter = 0
|
570
|
+
pg_exec_and_check_with_error(@client, false,
|
571
|
+
PG::ConnectionBad, "connection reset",
|
572
|
+
:query_defer, 'SELECT pg_sleep(10)') do
|
573
|
+
(visit_counter+=1).should eq 2
|
574
|
+
end
|
575
|
+
pg_exec_and_check_with_error(@client, false,
|
576
|
+
PG::ConnectionBad, "connection reset",
|
577
|
+
:wait_for_notify_defer) do
|
578
|
+
(visit_counter+=1).should eq 3
|
579
|
+
pg_exec_and_check_with_error(@client, false,
|
580
|
+
PG::ConnectionBad, "connection reset",
|
581
|
+
:wait_for_notify_defer) do
|
582
|
+
(visit_counter+=1).should eq 5
|
583
|
+
end
|
584
|
+
@client.reset_defer do |conn|
|
585
|
+
conn.should be @client
|
586
|
+
(visit_counter+=1).should eq 6
|
587
|
+
EM.stop
|
588
|
+
end
|
589
|
+
end
|
590
|
+
@client.reset
|
591
|
+
(visit_counter+=1).should eq 1
|
592
|
+
pg_exec_and_check_with_error(@client, false,
|
593
|
+
PG::ConnectionBad, "connection reset",
|
594
|
+
:query_defer, 'SELECT pg_sleep(10)') do
|
595
|
+
(visit_counter+=1).should eq 4
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
it "should fail wait_for_notify on another wait_for_notify" do
|
600
|
+
@client.status.should be PG::CONNECTION_OK
|
601
|
+
visit_counter = 0
|
602
|
+
@client.wait_for_notify_defer(0.5).errback do |ex|
|
603
|
+
ex.should be_nil
|
604
|
+
(visit_counter+=1).should eq 2
|
605
|
+
end.callback do
|
606
|
+
raise "This should never be called"
|
607
|
+
end.should be_a_kind_of ::EM::Deferrable
|
608
|
+
(visit_counter+=1).should eq 1
|
609
|
+
@client.wait_for_notify_defer(0.1).callback do |notification|
|
610
|
+
notification.should be_nil
|
611
|
+
(visit_counter+=1).should eq 4
|
612
|
+
EM.stop
|
613
|
+
end.errback do
|
614
|
+
raise "This should never be called"
|
615
|
+
end.should be_a_kind_of ::EM::Deferrable
|
616
|
+
(visit_counter+=1).should eq 3
|
617
|
+
end
|
618
|
+
|
619
|
+
end
|