em-pg-client 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,43 @@
1
+ module PG
2
+ module EM
3
+
4
+ # Deferrable with error protectors
5
+ #
6
+ # Author:: Rafal Michalski
7
+ class FeaturedDeferrable < ::EM::DefaultDeferrable
8
+
9
+ def initialize(&blk)
10
+ completion(&blk) if block_given?
11
+ end
12
+
13
+ def completion(&blk)
14
+ callback(&blk)
15
+ errback(&blk)
16
+ end
17
+
18
+ def protect(fail_value = nil)
19
+ yield
20
+ rescue Exception => e
21
+ ::EM.next_tick { fail e }
22
+ fail_value
23
+ end
24
+
25
+ def protect_and_succeed(fail_value = nil)
26
+ ret = yield
27
+ rescue Exception => e
28
+ ::EM.next_tick { fail e }
29
+ fail_value
30
+ else
31
+ ::EM.next_tick { succeed ret }
32
+ ret
33
+ end
34
+
35
+ # bind deferred status of this deferrable to other +df+
36
+ def bind_status(df)
37
+ df.callback { |*a| succeed(*a) }
38
+ df.errback { |*a| fail(*a) }
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,89 @@
1
+ module ConnectionPoolHelpers
2
+ def sleep_one_tick
3
+ f = Fiber.current
4
+ EM.next_tick { f.resume }
5
+ Fiber.yield
6
+ end
7
+
8
+ def create_connection
9
+ client.allocate.tap do |conn|
10
+ conn.stub(:query) do |query|
11
+ query.should start_with 'f'
12
+ checkpoint.check_defer
13
+ f = Fiber.current
14
+ conn.instance_eval { @fiber = f }
15
+ EM.next_tick {
16
+ conn.instance_variable_get(:@fiber).should be f
17
+ f.resume
18
+ }
19
+ Fiber.yield
20
+ if query == 'fee'
21
+ conn.should_receive(:status).once.and_return(PG::CONNECTION_BAD)
22
+ conn.should_receive(:finished?).once.and_return(false)
23
+ conn.should_receive(:finish).once
24
+ raise PG::ConnectionBad
25
+ else
26
+ :result
27
+ end
28
+ end
29
+ conn.stub(:query_defer) do |query|
30
+ query.should start_with 'b'
31
+ checkpoint.check_fiber
32
+ deferrable.new.tap do |df|
33
+ conn.instance_eval { @defer = df }
34
+ EM.next_tick {
35
+ conn.instance_variable_get(:@defer).should be df
36
+ if query == 'bzz'
37
+ conn.should_receive(:status).once.and_return(PG::CONNECTION_BAD)
38
+ conn.should_receive(:finished?).once.and_return(false)
39
+ conn.should_receive(:finish).once
40
+ df.fail pgerror
41
+ else
42
+ df.succeed :result
43
+ end
44
+ }
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ def test_queries(pool, queries)
51
+ EM.synchrony do
52
+ EM.add_timer(2) { raise 'timeout' }
53
+ progress = Set.new
54
+ queries.each_with_index do |query, i|
55
+ case query
56
+ when 'foo'
57
+ Fiber.new do
58
+ pool.query(query).should be :result
59
+ progress.delete i
60
+ end.resume
61
+ when 'bar'
62
+ pool.query_defer(query).callback do |result|
63
+ result.should be :result
64
+ progress.delete i
65
+ end.should_not_receive(:fail)
66
+ when 'fee'
67
+ Fiber.new do
68
+ expect do
69
+ pool.query(query)
70
+ end.to raise_error(PG::ConnectionBad)
71
+ progress.delete i
72
+ end.resume
73
+ when 'bzz'
74
+ pool.query_defer(query).errback do |err|
75
+ err.should be pgerror
76
+ progress.delete i
77
+ end.should_not_receive(:succeed)
78
+ end
79
+ progress << i
80
+ end
81
+ progress.should eq Set.new(0...queries.length)
82
+ begin
83
+ yield progress
84
+ sleep_one_tick
85
+ end until progress.empty?
86
+ EM.stop
87
+ end
88
+ end
89
+ end
@@ -1,5 +1,5 @@
1
1
  $:.unshift "lib"
2
- gem 'eventmachine', '>= 1.0.0.beta.1'
2
+ gem 'eventmachine', '~> 1.0.0'
3
3
  gem 'pg', ENV['EM_PG_CLIENT_TEST_PG_VERSION']
4
4
  require 'date'
5
5
  require 'eventmachine'
@@ -15,7 +15,8 @@ describe PG::EM::Client do
15
15
 
16
16
  it "should populate foo with some data " do
17
17
  EM::Iterator.new(@values).map(proc{ |(data, id), iter|
18
- @client.query('INSERT INTO foo (id,cdate,data) VALUES($1,$2,$3) returning cdate', [id, DateTime.now, data]) do |result|
18
+ @client.query_defer('INSERT INTO foo (id,cdate,data) VALUES($1,$2,$3) returning cdate',
19
+ [id, DateTime.now, data]) do |result|
19
20
  result.should be_an_instance_of PG::Result
20
21
  iter.return(DateTime.parse(result[0]['cdate']))
21
22
  end.should be_a_kind_of ::EM::DefaultDeferrable
@@ -1,144 +1,268 @@
1
- $:.unshift "lib"
2
- require 'date'
3
- require 'eventmachine'
4
- require 'pg/em'
5
-
6
- $pgserver_cmd_stop = %Q[sudo su - postgres -c 'pg_ctl stop -m fast']
7
- $pgserver_cmd_start = %Q[sudo su - postgres -c 'pg_ctl -l $PGDATA/postgres.log start -w']
8
-
9
- shared_context 'em-pg common' do
10
- around(:each) do |testcase|
11
- EM.run(&testcase)
12
- end
13
-
14
- after(:all) do
15
- @client.close
16
- end
17
- end
18
-
19
- describe 'em-pg default autoreconnect' do
20
- include_context 'em-pg common'
21
-
22
- it "should not have modified argument Hash" do
23
- begin
24
- @options.should eq(async_autoreconnect: true)
25
- ensure
26
- EM.stop
27
- end
28
- end
29
-
30
- it "should get database size using query" do
31
- @tested_proc.call
32
- end
33
-
34
- it "should get database size using query after server restart" do
35
- system($pgserver_cmd_stop).should be_true
36
- system($pgserver_cmd_start).should be_true
37
- @tested_proc.call
38
- end
39
-
40
- it "should not get database size using query after server shutdown" do
41
- system($pgserver_cmd_stop).should be_true
42
- @client.query('SELECT pg_database_size(current_database());') do |ex|
43
- ex.should be_an_instance_of PG::Error
44
- EM.stop
45
- end.should be_a_kind_of ::EM::DefaultDeferrable
46
- end
47
-
48
- it "should get database size using query after server startup" do
49
- system($pgserver_cmd_start).should be_true
50
- @tested_proc.call
51
- end
52
-
53
- before(:all) do
54
- @tested_proc = proc do
55
- @client.query('SELECT pg_database_size(current_database());') do |result|
56
- result.should be_an_instance_of PG::Result
57
- result[0]['pg_database_size'].to_i.should be > 0
58
- EM.stop
59
- end.should be_a_kind_of ::EM::DefaultDeferrable
60
- end
61
- @options = {async_autoreconnect: true}
62
- @client = PG::EM::Client.new(@options)
63
- @client.set_notice_processor {|msg| puts "warning from pgsql: #{msg.to_s.chomp.inspect}"}
64
- end
65
- end
66
-
67
- describe 'em-pg autoreconnect with on_autoreconnect' do
68
- include_context 'em-pg common'
69
-
70
- it "should not have modified argument Hash" do
71
- begin
72
- @options.should eq(on_autoreconnect: @on_autoreconnect)
73
- ensure
74
- EM.stop
75
- end
76
- end
77
-
78
- it "should get database size using prepared statement"do
79
- @tested_proc.call
80
- end
81
-
82
- it "should get database size using prepared statement after server restart" do
83
- system($pgserver_cmd_stop).should be_true
84
- system($pgserver_cmd_start).should be_true
85
- @tested_proc.call
86
- end
87
-
88
- before(:all) do
89
- @tested_proc = proc do
90
- @client.exec_prepared('get_db_size') do |result|
91
- result.should be_an_instance_of PG::Result
92
- result[0]['pg_database_size'].to_i.should be > 0
93
- EM.stop
94
- end.should be_a_kind_of ::EM::DefaultDeferrable
95
- end
96
- @on_autoreconnect = proc do |client, ex|
97
- df = client.prepare('get_db_size', 'SELECT pg_database_size(current_database());')
98
- df.should be_a_kind_of ::EM::DefaultDeferrable
99
- df
100
- end
101
- @options = {on_autoreconnect: @on_autoreconnect}
102
- @client = PG::EM::Client.new(@options)
103
- @client.set_notice_processor {|msg| puts "warning from pgsql: #{msg.to_s.chomp.inspect}"}
104
- @client.prepare('get_db_size', 'SELECT pg_database_size(current_database());')
105
- end
106
- end
107
-
108
- describe 'em-pg with autoreconnect disabled' do
109
- include_context 'em-pg common'
110
-
111
- it "should get database size using query" do
112
- @tested_proc.call
113
- end
114
-
115
- it "should not get database size using query after server restart" do
116
- system($pgserver_cmd_stop).should be_true
117
- system($pgserver_cmd_start).should be_true
118
- @client.query('SELECT pg_database_size(current_database());') do |ex|
119
- ex.should be_an_instance_of PG::Error
120
- EM.stop
121
- end.should be_a_kind_of ::EM::DefaultDeferrable
122
- end
123
-
124
- it "should get database size using query after async manual connection reset" do
125
- @client.status.should be PG::CONNECTION_BAD
126
- @client.async_reset do |conn|
127
- conn.should be @client
128
- @client.status.should be PG::CONNECTION_OK
129
- @tested_proc.call
130
- end.should be_a_kind_of ::EM::DefaultDeferrable
131
- end
132
-
133
- before(:all) do
134
- @tested_proc = proc do
135
- @client.query('SELECT pg_database_size(current_database());') do |result|
136
- result.should be_an_instance_of PG::Result
137
- result[0]['pg_database_size'].to_i.should be > 0
138
- EM.stop
139
- end.should be_a_kind_of ::EM::DefaultDeferrable
140
- end
141
- @client = PG::EM::Client.new
142
- @client.set_notice_processor {|msg| puts "warning from pgsql: #{msg.to_s.chomp.inspect}"}
143
- end
144
- end
1
+ $:.unshift "lib"
2
+ gem 'eventmachine', '~> 1.0.0'
3
+ gem 'pg', ENV['EM_PG_CLIENT_TEST_PG_VERSION']
4
+ require 'date'
5
+ require 'eventmachine'
6
+ require 'pg/em'
7
+
8
+ $pgserver_cmd_stop = %Q[sudo -i -u postgres pg_ctl -D "#{ENV['PGDATA']}" stop -s -m fast]
9
+ $pgserver_cmd_start = %Q[sudo -i -u postgres pg_ctl -D "#{ENV['PGDATA']}" start -s -w]
10
+
11
+ shared_context 'pg-em common' do
12
+ around(:each) do |testcase|
13
+ EM.run(&testcase)
14
+ end
15
+
16
+ after(:all) do
17
+ @client.close
18
+ end
19
+ end
20
+
21
+ describe 'pg-em async connect fail' do
22
+ around(:each) do |testcase|
23
+ begin
24
+ system($pgserver_cmd_stop).should be_true
25
+ testcase.call
26
+ ensure
27
+ system($pgserver_cmd_start).should be_true
28
+ end
29
+ end
30
+
31
+ it "should not connect when server is down" do
32
+ error = nil
33
+ EM.run do
34
+ EM.add_timer(1) { EM.stop }
35
+ df = PG::EM::Client.async_connect
36
+ df.callback {|c| c.close }
37
+ df.errback do |err|
38
+ error = err
39
+ EM.stop
40
+ end
41
+ end
42
+ error.should be_an_instance_of PG::ConnectionBad
43
+ end
44
+ end
45
+
46
+ describe 'pg-em default autoreconnect' do
47
+ include_context 'pg-em common'
48
+
49
+ it "should not have modified argument Hash" do
50
+ begin
51
+ @options.should eq(async_autoreconnect: true)
52
+ ensure
53
+ EM.stop
54
+ end
55
+ end
56
+
57
+ it "should get database size using query" do
58
+ @tested_proc.call
59
+ end
60
+
61
+ it "should get database size using query after server restart" do
62
+ system($pgserver_cmd_stop).should be_true
63
+ system($pgserver_cmd_start).should be_true
64
+ @tested_proc.call
65
+ end
66
+
67
+ it "should not get database size using query after server shutdown" do
68
+ system($pgserver_cmd_stop).should be_true
69
+ @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
70
+ ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
71
+ EM.stop
72
+ end.should be_a_kind_of ::EM::DefaultDeferrable
73
+ end
74
+
75
+ it "should get database size using query after server startup" do
76
+ system($pgserver_cmd_start).should be_true
77
+ @tested_proc.call
78
+ end
79
+
80
+ it "should fail on invalid query after server restart" do
81
+ system($pgserver_cmd_stop).should be_true
82
+ system($pgserver_cmd_start).should be_true
83
+ @client.query_defer('SELLECT 1') do |ex|
84
+ ex.should be_an_instance_of PG::SyntaxError
85
+ EM.stop
86
+ end.should be_a_kind_of ::EM::DefaultDeferrable
87
+ end
88
+
89
+ it "should fail when in transaction after server restart" do
90
+ @client.query_defer('BEGIN') do |result|
91
+ result.should be_an_instance_of PG::Result
92
+ system($pgserver_cmd_stop).should be_true
93
+ system($pgserver_cmd_start).should be_true
94
+ @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
95
+ ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
96
+ @tested_proc.call
97
+ end.should be_a_kind_of ::EM::DefaultDeferrable
98
+ end
99
+ end
100
+
101
+ before(:all) do
102
+ @tested_proc = proc do
103
+ @client.query_defer('SELECT pg_database_size(current_database());') do |result|
104
+ result.should be_an_instance_of PG::Result
105
+ result[0]['pg_database_size'].to_i.should be > 0
106
+ EM.stop
107
+ end.should be_a_kind_of ::EM::DefaultDeferrable
108
+ end
109
+ @options = {async_autoreconnect: true}
110
+ @client = PG::EM::Client.new(@options)
111
+ @client.set_notice_processor {|msg| puts "warning from pgsql: #{msg.to_s.chomp.inspect}"}
112
+ end
113
+ end
114
+
115
+ describe 'pg-em autoreconnect with on_autoreconnect' do
116
+ include_context 'pg-em common'
117
+
118
+ it "should not have modified argument Hash" do
119
+ begin
120
+ @options.should eq(on_autoreconnect: @on_autoreconnect)
121
+ ensure
122
+ EM.stop
123
+ end
124
+ end
125
+
126
+ it "should get database size using prepared statement"do
127
+ @tested_proc.call
128
+ end
129
+
130
+ it "should get database size using prepared statement after server restart" do
131
+ system($pgserver_cmd_stop).should be_true
132
+ system($pgserver_cmd_start).should be_true
133
+ @tested_proc.call
134
+ end
135
+
136
+ it "should fail on invalid query after server restart" do
137
+ system($pgserver_cmd_stop).should be_true
138
+ system($pgserver_cmd_start).should be_true
139
+ @client.query_defer('SELLECT 1') do |ex|
140
+ ex.should be_an_instance_of PG::SyntaxError
141
+ EM.stop
142
+ end.should be_a_kind_of ::EM::DefaultDeferrable
143
+ end
144
+
145
+ it "should fail when in transaction after server restart" do
146
+ @client.query_defer('BEGIN') do |result|
147
+ result.should be_an_instance_of PG::Result
148
+ system($pgserver_cmd_stop).should be_true
149
+ system($pgserver_cmd_start).should be_true
150
+ @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
151
+ ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
152
+ @tested_proc.call
153
+ end.should be_a_kind_of ::EM::DefaultDeferrable
154
+ end
155
+ end
156
+
157
+ it "should fail on false from on_autoreconnect after server restart" do
158
+ @client.on_autoreconnect = proc { false }
159
+ system($pgserver_cmd_stop).should be_true
160
+ system($pgserver_cmd_start).should be_true
161
+ @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
162
+ ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
163
+ EM.stop
164
+ end.should be_a_kind_of ::EM::DefaultDeferrable
165
+ end
166
+
167
+ it "should complete on true from on_autoreconnect after server restart" do
168
+ @client.on_autoreconnect = proc { true }
169
+ system($pgserver_cmd_stop).should be_true
170
+ system($pgserver_cmd_start).should be_true
171
+ @client.query_defer('SELECT pg_database_size(current_database());') do |result|
172
+ result.should be_an_instance_of PG::Result
173
+ result[0]['pg_database_size'].to_i.should be > 0
174
+ EM.stop
175
+ end.should be_a_kind_of ::EM::DefaultDeferrable
176
+ end
177
+
178
+ it "should fail on query with true from on_autoreconnect after restart" do
179
+ @client.on_autoreconnect = proc { true }
180
+ system($pgserver_cmd_stop).should be_true
181
+ system($pgserver_cmd_start).should be_true
182
+ @client.query_defer('SELLECT 1') do |ex|
183
+ ex.should be_an_instance_of PG::SyntaxError
184
+ EM.stop
185
+ end.should be_a_kind_of ::EM::DefaultDeferrable
186
+ end
187
+
188
+ it "should fail on on_autoreconnect deferrable fail after server restart" do
189
+ @client.on_autoreconnect = proc do
190
+ ::EM::DefaultDeferrable.new.tap {|df| df.fail :boo }
191
+ end
192
+ system($pgserver_cmd_stop).should be_true
193
+ system($pgserver_cmd_start).should be_true
194
+ @client.query_defer('SELECT 1') do |ex|
195
+ ex.should be :boo
196
+ EM.stop
197
+ end.should be_a_kind_of ::EM::DefaultDeferrable
198
+ end
199
+
200
+ it "should fail on raised error in on_autoreconnect after server restart" do
201
+ @client.on_autoreconnect = proc do
202
+ raise TypeError
203
+ end
204
+ system($pgserver_cmd_stop).should be_true
205
+ system($pgserver_cmd_start).should be_true
206
+ @client.query_defer('SELECT 1') do |ex|
207
+ ex.should be_an_instance_of TypeError
208
+ EM.stop
209
+ end.should be_a_kind_of ::EM::DefaultDeferrable
210
+ end
211
+
212
+ before(:all) do
213
+ @tested_proc = proc do
214
+ @client.exec_prepared_defer('get_db_size') do |result|
215
+ result.should be_an_instance_of PG::Result
216
+ result[0]['pg_database_size'].to_i.should be > 0
217
+ EM.stop
218
+ end.should be_a_kind_of ::EM::DefaultDeferrable
219
+ end
220
+ @on_autoreconnect = proc do |client, ex|
221
+ df = client.prepare_defer('get_db_size', 'SELECT pg_database_size(current_database());')
222
+ df.should be_a_kind_of ::EM::DefaultDeferrable
223
+ df
224
+ end
225
+ @options = {on_autoreconnect: @on_autoreconnect}
226
+ @client = PG::EM::Client.new(@options)
227
+ @client.set_notice_processor {|msg| puts "warning from pgsql: #{msg.to_s.chomp.inspect}"}
228
+ @client.prepare('get_db_size', 'SELECT pg_database_size(current_database());')
229
+ end
230
+ end
231
+
232
+ describe 'pg-em with autoreconnect disabled' do
233
+ include_context 'pg-em common'
234
+
235
+ it "should get database size using query" do
236
+ @tested_proc.call
237
+ end
238
+
239
+ it "should not get database size using query after server restart" do
240
+ system($pgserver_cmd_stop).should be_true
241
+ system($pgserver_cmd_start).should be_true
242
+ @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
243
+ ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
244
+ EM.stop
245
+ end.should be_a_kind_of ::EM::DefaultDeferrable
246
+ end
247
+
248
+ it "should get database size using query after async manual connection reset" do
249
+ @client.status.should be PG::CONNECTION_BAD
250
+ @client.async_reset do |conn|
251
+ conn.should be @client
252
+ @client.status.should be PG::CONNECTION_OK
253
+ @tested_proc.call
254
+ end.should be_a_kind_of ::EM::DefaultDeferrable
255
+ end
256
+
257
+ before(:all) do
258
+ @tested_proc = proc do
259
+ @client.query_defer('SELECT pg_database_size(current_database());') do |result|
260
+ result.should be_an_instance_of PG::Result
261
+ result[0]['pg_database_size'].to_i.should be > 0
262
+ EM.stop
263
+ end.should be_a_kind_of ::EM::DefaultDeferrable
264
+ end
265
+ @client = PG::EM::Client.new
266
+ @client.set_notice_processor {|msg| puts "warning from pgsql: #{msg.to_s.chomp.inspect}"}
267
+ end
268
+ end