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
@@ -1,130 +1,154 @@
|
|
1
|
-
$:.unshift "lib"
|
2
|
-
|
3
|
-
|
4
|
-
require '
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
@client.
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
@
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
@
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
@tested_proc.call
|
103
|
-
end
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
@
|
116
|
-
@client
|
117
|
-
@
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
1
|
+
$:.unshift "lib"
|
2
|
+
gem 'eventmachine', '~> 1.0.0'
|
3
|
+
gem 'pg', ENV['EM_PG_CLIENT_TEST_PG_VERSION']
|
4
|
+
require 'date'
|
5
|
+
require 'em-synchrony'
|
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 'em-synchrony-pg common' do
|
12
|
+
around(:each) do |testcase|
|
13
|
+
EM.synchrony do
|
14
|
+
testcase.call
|
15
|
+
EM.stop
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
after(:all) do
|
20
|
+
@client.close
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'em-synchrony-pg default autoreconnect' do
|
25
|
+
include_context 'em-synchrony-pg common'
|
26
|
+
|
27
|
+
it "should not have modified argument Hash" do
|
28
|
+
@options.should eq(async_autoreconnect: true)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should get database size using query" do
|
32
|
+
@tested_proc.call
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should get database size using query after server restart" do
|
36
|
+
system($pgserver_cmd_stop).should be_true
|
37
|
+
system($pgserver_cmd_start).should be_true
|
38
|
+
@tested_proc.call
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should not get database size using query after server shutdown" do
|
42
|
+
system($pgserver_cmd_stop).should be_true
|
43
|
+
expect {
|
44
|
+
@tested_proc.call
|
45
|
+
}.to raise_error(@client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad)
|
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
|
+
it "should raise an error when in transaction after server restart" do
|
54
|
+
expect do
|
55
|
+
@client.transaction do
|
56
|
+
system($pgserver_cmd_stop).should be_true
|
57
|
+
system($pgserver_cmd_start).should be_true
|
58
|
+
@tested_proc.call
|
59
|
+
end
|
60
|
+
end.to raise_error(@client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad)
|
61
|
+
@tested_proc.call
|
62
|
+
end
|
63
|
+
|
64
|
+
before(:all) do
|
65
|
+
@tested_proc = proc do
|
66
|
+
@client.query('SELECT pg_database_size(current_database());') do |result|
|
67
|
+
result.should be_an_instance_of PG::Result
|
68
|
+
result[0]['pg_database_size'].to_i.should be > 0
|
69
|
+
end
|
70
|
+
end
|
71
|
+
@options = {async_autoreconnect: true}
|
72
|
+
@client = PG::EM::Client.new(@options)
|
73
|
+
@client.set_notice_processor {|msg| puts "warning from pgsql: #{msg.to_s.chomp.inspect}"}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'em-synchrony-pg autoreconnect with on_autoreconnect' do
|
78
|
+
include_context 'em-synchrony-pg common'
|
79
|
+
|
80
|
+
it "should not have modified argument Hash" do
|
81
|
+
@options.should eq(on_autoreconnect: @on_autoreconnect)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should get database size using prepared statement" do
|
85
|
+
@tested_proc.call
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should get database size using prepared statement after server restart" do
|
89
|
+
system($pgserver_cmd_stop).should be_true
|
90
|
+
system($pgserver_cmd_start).should be_true
|
91
|
+
@tested_proc.call
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should raise an error when in transaction after server restart" do
|
95
|
+
expect do
|
96
|
+
@client.transaction do
|
97
|
+
system($pgserver_cmd_stop).should be_true
|
98
|
+
system($pgserver_cmd_start).should be_true
|
99
|
+
@tested_proc.call
|
100
|
+
end
|
101
|
+
end.to raise_error(@client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad)
|
102
|
+
@tested_proc.call
|
103
|
+
end
|
104
|
+
|
105
|
+
before(:all) do
|
106
|
+
@tested_proc = proc do
|
107
|
+
@client.exec_prepared('get_db_size') do |result|
|
108
|
+
result.should be_an_instance_of PG::Result
|
109
|
+
result[0]['pg_database_size'].to_i.should be > 0
|
110
|
+
end
|
111
|
+
end
|
112
|
+
@on_autoreconnect = proc do |client, ex|
|
113
|
+
client.prepare('get_db_size', 'SELECT pg_database_size(current_database());')
|
114
|
+
end
|
115
|
+
@options = {on_autoreconnect: @on_autoreconnect}
|
116
|
+
@client = PG::EM::Client.new(@options)
|
117
|
+
@client.set_notice_processor {|msg| puts "warning from pgsql: #{msg.to_s.chomp.inspect}"}
|
118
|
+
@on_autoreconnect.call @client
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe 'em-synchrony-pg with autoreconnect disabled' do
|
123
|
+
include_context 'em-synchrony-pg common'
|
124
|
+
|
125
|
+
it "should get database size using query" do
|
126
|
+
@tested_proc.call
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should not get database size using query after server restart" do
|
130
|
+
system($pgserver_cmd_stop).should be_true
|
131
|
+
system($pgserver_cmd_start).should be_true
|
132
|
+
expect {
|
133
|
+
@tested_proc.call
|
134
|
+
}.to raise_error(@client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should get database size using query after manual connection reset" do
|
138
|
+
@client.status.should be PG::CONNECTION_BAD
|
139
|
+
@client.reset
|
140
|
+
@client.status.should be PG::CONNECTION_OK
|
141
|
+
@tested_proc.call
|
142
|
+
end
|
143
|
+
|
144
|
+
before(:all) do
|
145
|
+
@tested_proc = proc do
|
146
|
+
@client.query('SELECT pg_database_size(current_database());') do |result|
|
147
|
+
result.should be_an_instance_of PG::Result
|
148
|
+
result[0]['pg_database_size'].to_i.should be > 0
|
149
|
+
end
|
150
|
+
end
|
151
|
+
@client = PG::EM::Client.new
|
152
|
+
@client.set_notice_processor {|msg| puts "warning from pgsql: #{msg.to_s.chomp.inspect}"}
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,54 @@
|
|
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'
|
7
|
+
|
8
|
+
shared_context 'test deferred' do
|
9
|
+
it "should not finish connection on deferred connection failure" do
|
10
|
+
EM.run do
|
11
|
+
subject.connect_defer(options) do |ex|
|
12
|
+
ex.should be_an_instance_of PG::ConnectionBad
|
13
|
+
ex.connection.should be_an_instance_of subject
|
14
|
+
ex.connection.finished?.should be_false
|
15
|
+
EM.stop
|
16
|
+
end.should be_a_kind_of ::EM::DefaultDeferrable
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
shared_context 'test blocking' do
|
22
|
+
it "should not finish connection on blocking connection failure" do
|
23
|
+
EM.synchrony do
|
24
|
+
expect do
|
25
|
+
subject.new(options)
|
26
|
+
end.to raise_error(PG::ConnectionBad)
|
27
|
+
begin
|
28
|
+
subject.new(options)
|
29
|
+
rescue => ex
|
30
|
+
ex.should be_an_instance_of PG::ConnectionBad
|
31
|
+
ex.connection.should be_an_instance_of subject
|
32
|
+
ex.connection.finished?.should be_false
|
33
|
+
end
|
34
|
+
EM.stop
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'connect failure and finished? status' do
|
40
|
+
subject { PG::EM::Client }
|
41
|
+
let(:bogus_port) { 1 }
|
42
|
+
|
43
|
+
describe 'with localhost' do
|
44
|
+
let(:options) { {host: 'localhost', port: bogus_port} }
|
45
|
+
include_context 'test deferred'
|
46
|
+
include_context 'test blocking'
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'with unix socket' do
|
50
|
+
let(:options) { {host: '/tmp', port: bogus_port} }
|
51
|
+
include_context 'test deferred'
|
52
|
+
include_context 'test blocking'
|
53
|
+
end unless RSpec.windows_os?
|
54
|
+
end
|
@@ -0,0 +1,91 @@
|
|
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'
|
7
|
+
|
8
|
+
shared_context 'test async connect timeout' do
|
9
|
+
it "should timeout expire while connecting" do
|
10
|
+
this = :first
|
11
|
+
start = Time.now
|
12
|
+
subject.connect_defer(options) do |ex|
|
13
|
+
this = :second
|
14
|
+
ex.should be_an_instance_of PG::ConnectionBad
|
15
|
+
ex.message.should include 'timeout expired (async)'
|
16
|
+
(Time.now - start).should be > timeout
|
17
|
+
EM.stop
|
18
|
+
end.should be_a_kind_of ::EM::DefaultDeferrable
|
19
|
+
this.should be :first
|
20
|
+
end
|
21
|
+
|
22
|
+
around(:each) do |testcase|
|
23
|
+
EM.run(&testcase)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
shared_context 'test synchrony connect timeout' do
|
28
|
+
it "should timeout expire while connecting" do
|
29
|
+
start = Time.now
|
30
|
+
this = nil
|
31
|
+
EM.next_tick { this = :that }
|
32
|
+
expect do
|
33
|
+
subject.new(options)
|
34
|
+
end.to raise_error(PG::ConnectionBad, 'timeout expired (async)')
|
35
|
+
this.should be :that
|
36
|
+
(Time.now - start).should be > timeout
|
37
|
+
end
|
38
|
+
|
39
|
+
around(:each) do |testcase|
|
40
|
+
EM.synchrony do
|
41
|
+
begin
|
42
|
+
testcase.call
|
43
|
+
ensure
|
44
|
+
EM.stop
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'connect timeout expire' do
|
51
|
+
subject { PG::EM::Client }
|
52
|
+
let(:black_hole) { '192.168.255.254' }
|
53
|
+
let(:timeout) { 1 }
|
54
|
+
let(:envvar) { 'PGCONNECT_TIMEOUT' }
|
55
|
+
|
56
|
+
describe 'asynchronously using connect_timeout option' do
|
57
|
+
let(:options) { {host: black_hole, connect_timeout: timeout} }
|
58
|
+
|
59
|
+
before(:each) { ENV[envvar] = nil }
|
60
|
+
|
61
|
+
include_context 'test async connect timeout'
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'asynchronously using PGCONNECT_TIMEOUT env var' do
|
65
|
+
let(:options) { {host: black_hole} }
|
66
|
+
|
67
|
+
before(:each) { ENV[envvar] = timeout.to_s }
|
68
|
+
|
69
|
+
include_context 'test async connect timeout'
|
70
|
+
|
71
|
+
after(:each) { ENV[envvar] = nil }
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'sync-to-fiber using connect_timeout option' do
|
75
|
+
let(:options) { {host: black_hole, connect_timeout: timeout} }
|
76
|
+
|
77
|
+
before(:each) { ENV[envvar] = nil }
|
78
|
+
|
79
|
+
include_context 'test synchrony connect timeout'
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'sync-to-fiber using PGCONNECT_TIMEOUT env var' do
|
83
|
+
let(:options) { {host: black_hole} }
|
84
|
+
|
85
|
+
before(:each) { ENV[envvar] = timeout.to_s }
|
86
|
+
|
87
|
+
include_context 'test synchrony connect timeout'
|
88
|
+
|
89
|
+
after(:each) { ENV[envvar] = nil }
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,85 @@
|
|
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
|
+
describe 'em-pg-client options' do
|
9
|
+
subject { PG::EM::Client }
|
10
|
+
|
11
|
+
let(:callback) { proc {|c, e| false } }
|
12
|
+
let(:args) { [{async_autoreconnect: true, connect_timeout: 10, host: 'foo'}] }
|
13
|
+
let(:str_key_args) { [{'async_autoreconnect'=>true, 'connect_timeout'=>10, 'host'=>'foo'}] }
|
14
|
+
let(:pgconn_args) { [{connect_timeout: 10, host: 'foo'}] }
|
15
|
+
let(:str_key_pgconn_args) { [{'connect_timeout'=>10, 'host'=>'foo'}] }
|
16
|
+
let(:async_options) { {
|
17
|
+
:@async_autoreconnect => true,
|
18
|
+
:@connect_timeout => 10,
|
19
|
+
:@query_timeout=>0,
|
20
|
+
:@on_autoreconnect=>nil,
|
21
|
+
:@async_command_aborted=>false} }
|
22
|
+
|
23
|
+
it "should parse options and not modify original hash" do
|
24
|
+
orig_args = args.dup
|
25
|
+
orig_options = orig_args.first.dup
|
26
|
+
options = subject.parse_async_options orig_args
|
27
|
+
options.should eq async_options
|
28
|
+
orig_args.should eq pgconn_args
|
29
|
+
args.first.should eq orig_options
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should parse options with keys as strings" do
|
33
|
+
orig_args = str_key_args.dup
|
34
|
+
orig_options = orig_args.first.dup
|
35
|
+
options = subject.parse_async_options orig_args
|
36
|
+
options.should eq async_options
|
37
|
+
orig_args.should eq str_key_pgconn_args
|
38
|
+
str_key_args.first.should eq orig_options
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should set async_autoreconnect according to on_autoreconnect" do
|
42
|
+
options = subject.parse_async_options []
|
43
|
+
options.should be_an_instance_of Hash
|
44
|
+
options[:@on_autoreconnect].should be_nil
|
45
|
+
options[:@async_autoreconnect].should be_false
|
46
|
+
|
47
|
+
args = [on_autoreconnect: callback]
|
48
|
+
options = subject.parse_async_options args
|
49
|
+
args.should eq [{}]
|
50
|
+
options.should be_an_instance_of Hash
|
51
|
+
options[:@on_autoreconnect].should be callback
|
52
|
+
options[:@async_autoreconnect].should be_true
|
53
|
+
|
54
|
+
args = [async_autoreconnect: false,
|
55
|
+
on_autoreconnect: callback]
|
56
|
+
options = subject.parse_async_options args
|
57
|
+
args.should eq [{}]
|
58
|
+
options.should be_an_instance_of Hash
|
59
|
+
options[:@on_autoreconnect].should be callback
|
60
|
+
options[:@async_autoreconnect].should be_false
|
61
|
+
|
62
|
+
args = [on_autoreconnect: callback,
|
63
|
+
async_autoreconnect: false]
|
64
|
+
options = subject.parse_async_options args
|
65
|
+
args.should eq [{}]
|
66
|
+
options.should be_an_instance_of Hash
|
67
|
+
options[:@on_autoreconnect].should be callback
|
68
|
+
options[:@async_autoreconnect].should be_false
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should set only callable on_autoreconnect" do
|
72
|
+
expect do
|
73
|
+
subject.parse_async_options [on_autoreconnect: true]
|
74
|
+
end.to raise_error(ArgumentError, /must respond to/)
|
75
|
+
|
76
|
+
expect do
|
77
|
+
subject.parse_async_options [on_autoreconnect: Object.new]
|
78
|
+
end.to raise_error(ArgumentError, /must respond to/)
|
79
|
+
|
80
|
+
options = subject.parse_async_options [on_autoreconnect: callback]
|
81
|
+
options.should be_an_instance_of Hash
|
82
|
+
options[:@on_autoreconnect].should be callback
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|