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,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