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,125 @@
|
|
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 PG::EM::FeaturedDeferrable do
|
9
|
+
subject { PG::EM::FeaturedDeferrable }
|
10
|
+
let(:df) { subject.new }
|
11
|
+
let(:cb) { Proc.new {} }
|
12
|
+
let(:error) { RuntimeError.new }
|
13
|
+
let(:pg_error) {
|
14
|
+
PG::Error.new.tap do |err|
|
15
|
+
err.instance_variable_set :@connection, :connection
|
16
|
+
err.instance_variable_set :@result, :result
|
17
|
+
end
|
18
|
+
}
|
19
|
+
|
20
|
+
it "should set callback with a block " do
|
21
|
+
cb.should_receive(:call).with(:result)
|
22
|
+
df = subject.new(&cb)
|
23
|
+
df.succeed(:result)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should set errback with a block" do
|
27
|
+
cb.should_receive(:call).with(:err)
|
28
|
+
df = subject.new(&cb)
|
29
|
+
df.fail(:err)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should execute callbacks and errbacks in setup order" do
|
33
|
+
results = []
|
34
|
+
setup_callbacks = proc do |df|
|
35
|
+
df.callback { results << 1 }
|
36
|
+
df.callback { results << 2 }
|
37
|
+
df.callback { results << 3 }
|
38
|
+
df.errback { results << 4 }
|
39
|
+
df.errback { results << 5 }
|
40
|
+
df.errback { results << 6 }
|
41
|
+
end
|
42
|
+
df = subject.new
|
43
|
+
setup_callbacks.call df
|
44
|
+
df.succeed
|
45
|
+
df.fail
|
46
|
+
df = subject.new
|
47
|
+
setup_callbacks.call df
|
48
|
+
df.fail
|
49
|
+
df.succeed
|
50
|
+
results.should eq [1, 2, 3, 4, 5, 6]
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should set completion with block" do
|
54
|
+
cb.should_receive(:call).with(:err)
|
55
|
+
df = subject.new
|
56
|
+
df.completion(&cb)
|
57
|
+
df.fail(:err)
|
58
|
+
df.succeed(:result)
|
59
|
+
|
60
|
+
cb.should_receive(:call).with(:result)
|
61
|
+
df = subject.new
|
62
|
+
df.completion(&cb)
|
63
|
+
df.succeed(:result)
|
64
|
+
df.fail(:err)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should bind status to another deferrable" do
|
68
|
+
cb.should_receive(:call).with(:result)
|
69
|
+
df = subject.new(&cb)
|
70
|
+
other_df = subject.new
|
71
|
+
df.bind_status(other_df)
|
72
|
+
other_df.succeed(:result)
|
73
|
+
|
74
|
+
cb.should_receive(:call).with(:err)
|
75
|
+
df = subject.new(&cb)
|
76
|
+
other_df = subject.new
|
77
|
+
df.bind_status(other_df)
|
78
|
+
other_df.fail(:err)
|
79
|
+
end
|
80
|
+
|
81
|
+
shared_context 'shared protect' do
|
82
|
+
it "should call df.fail and return nil" do
|
83
|
+
::EM.stub(:next_tick) {|&cb| cb.call }
|
84
|
+
df.errback(&cb)
|
85
|
+
cb.should_receive(:call).with(error)
|
86
|
+
df.send(protect_method) do
|
87
|
+
raise error
|
88
|
+
end.should be_nil
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should call df.fail and return custom fail value" do
|
92
|
+
::EM.stub(:next_tick) {|&cb| cb.call }
|
93
|
+
df.errback(&cb)
|
94
|
+
cb.should_receive(:call).with(error)
|
95
|
+
df.send(protect_method, :fail) do
|
96
|
+
raise error
|
97
|
+
end.should eq :fail
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "#protect" do
|
102
|
+
let(:protect_method) { :protect }
|
103
|
+
it "should return value" do
|
104
|
+
df.protect do
|
105
|
+
:result
|
106
|
+
end.should eq :result
|
107
|
+
end
|
108
|
+
|
109
|
+
include_context 'shared protect'
|
110
|
+
end
|
111
|
+
|
112
|
+
context "#protect_and_succeed" do
|
113
|
+
let(:protect_method) { :protect_and_succeed }
|
114
|
+
it "should call deferrable.succeed and return value" do
|
115
|
+
::EM.stub(:next_tick) {|&cb| cb.call }
|
116
|
+
df.callback(&cb)
|
117
|
+
cb.should_receive(:call).with(:result)
|
118
|
+
df.protect_and_succeed do
|
119
|
+
:result
|
120
|
+
end.should eq :result
|
121
|
+
end
|
122
|
+
|
123
|
+
include_context 'shared protect'
|
124
|
+
end
|
125
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: em-pg-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,33 +9,43 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-12-31 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: pg
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.
|
21
|
+
version: 0.17.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.17.0
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: eventmachine
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
|
-
- -
|
35
|
+
- - ~>
|
31
36
|
- !ruby/object:Gem::Version
|
32
|
-
version: 0.
|
37
|
+
version: 1.0.0
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.0.0
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: rspec
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ~>
|
@@ -43,21 +53,15 @@ dependencies:
|
|
43
53
|
version: 2.8.0
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: eventmachine
|
49
|
-
requirement: &244127700 !ruby/object:Gem::Requirement
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
57
|
none: false
|
51
58
|
requirements:
|
52
|
-
- -
|
59
|
+
- - ~>
|
53
60
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
55
|
-
type: :development
|
56
|
-
prerelease: false
|
57
|
-
version_requirements: *244127700
|
61
|
+
version: 2.8.0
|
58
62
|
- !ruby/object:Gem::Dependency
|
59
63
|
name: em-synchrony
|
60
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
61
65
|
none: false
|
62
66
|
requirements:
|
63
67
|
- - ~>
|
@@ -65,37 +69,58 @@ dependencies:
|
|
65
69
|
version: 1.0.0
|
66
70
|
type: :development
|
67
71
|
prerelease: false
|
68
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.0.0
|
69
78
|
description: PostgreSQL asynchronous EventMachine client, based on pg interface (PG::Connection)
|
70
79
|
email: rafal@yeondir.com
|
71
80
|
executables: []
|
72
81
|
extensions: []
|
73
82
|
extra_rdoc_files:
|
74
|
-
- README.
|
75
|
-
- BENCHMARKS.
|
83
|
+
- README.md
|
84
|
+
- BENCHMARKS.md
|
85
|
+
- LICENSE
|
86
|
+
- HISTORY.md
|
76
87
|
files:
|
77
|
-
-
|
78
|
-
-
|
79
|
-
-
|
88
|
+
- .yardopts
|
89
|
+
- BENCHMARKS.md
|
90
|
+
- HISTORY.md
|
91
|
+
- LICENSE
|
92
|
+
- README.md
|
80
93
|
- Rakefile
|
81
94
|
- benchmarks/em_pg.rb
|
82
95
|
- em-pg-client.gemspec
|
96
|
+
- lib/em-pg-client.rb
|
83
97
|
- lib/em-synchrony/pg.rb
|
98
|
+
- lib/pg/em-version.rb
|
84
99
|
- lib/pg/em.rb
|
100
|
+
- lib/pg/em/client/connect_watcher.rb
|
101
|
+
- lib/pg/em/client/watcher.rb
|
102
|
+
- lib/pg/em/connection_pool.rb
|
103
|
+
- lib/pg/em/featured_deferrable.rb
|
104
|
+
- spec/connection_pool_helpers.rb
|
105
|
+
- spec/em_client.rb
|
85
106
|
- spec/em_client_autoreconnect.rb
|
86
107
|
- spec/em_client_common.rb
|
87
|
-
- spec/em_devel_client.rb
|
88
|
-
- spec/em_release_client.rb
|
89
108
|
- spec/em_synchrony_client.rb
|
90
109
|
- spec/em_synchrony_client_autoreconnect.rb
|
110
|
+
- spec/pg_em_client_connect_finish.rb
|
111
|
+
- spec/pg_em_client_connect_timeout.rb
|
112
|
+
- spec/pg_em_client_options.rb
|
113
|
+
- spec/pg_em_connection_pool.rb
|
114
|
+
- spec/pg_em_featured_deferrable.rb
|
91
115
|
homepage: http://github.com/royaltm/ruby-em-pg-client
|
92
|
-
licenses:
|
116
|
+
licenses:
|
117
|
+
- MIT
|
93
118
|
post_install_message:
|
94
119
|
rdoc_options:
|
95
120
|
- --title
|
96
121
|
- em-pg-client
|
97
122
|
- --main
|
98
|
-
- README.
|
123
|
+
- README.md
|
99
124
|
require_paths:
|
100
125
|
- lib
|
101
126
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -103,7 +128,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
128
|
requirements:
|
104
129
|
- - ! '>='
|
105
130
|
- !ruby/object:Gem::Version
|
106
|
-
version: 1.9.
|
131
|
+
version: 1.9.2
|
107
132
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
133
|
none: false
|
109
134
|
requirements:
|
@@ -113,14 +138,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
138
|
requirements:
|
114
139
|
- PostgreSQL server
|
115
140
|
rubyforge_project:
|
116
|
-
rubygems_version: 1.8.
|
141
|
+
rubygems_version: 1.8.25
|
117
142
|
signing_key:
|
118
143
|
specification_version: 3
|
119
144
|
summary: EventMachine PostgreSQL client
|
120
145
|
test_files:
|
146
|
+
- spec/pg_em_client_connect_timeout.rb
|
147
|
+
- spec/em_client.rb
|
148
|
+
- spec/connection_pool_helpers.rb
|
149
|
+
- spec/pg_em_client_options.rb
|
121
150
|
- spec/em_synchrony_client.rb
|
122
|
-
- spec/em_devel_client.rb
|
123
151
|
- spec/em_client_common.rb
|
124
|
-
- spec/em_release_client.rb
|
125
152
|
- spec/em_synchrony_client_autoreconnect.rb
|
153
|
+
- spec/pg_em_client_connect_finish.rb
|
154
|
+
- spec/pg_em_connection_pool.rb
|
126
155
|
- spec/em_client_autoreconnect.rb
|
156
|
+
- spec/pg_em_featured_deferrable.rb
|
data/README.rdoc
DELETED
@@ -1,431 +0,0 @@
|
|
1
|
-
= em-pg-client
|
2
|
-
|
3
|
-
Author:: Rafał Michalski (mailto:rafal@yeondir.com)
|
4
|
-
|
5
|
-
* http://github.com/royaltm/ruby-em-pg-client
|
6
|
-
|
7
|
-
== DESCRIPTION
|
8
|
-
|
9
|
-
*em-pg-client* is the Ruby and EventMachine driver interface to the PostgreSQL
|
10
|
-
RDBMS. It is based on ruby-pg[https://bitbucket.org/ged/ruby-pg].
|
11
|
-
|
12
|
-
== FEATURES
|
13
|
-
|
14
|
-
* Non-blocking / asynchronous processing with EventMachine,
|
15
|
-
* fully async auto re-connects on connection losses (e.g.: RDBMS restarts),
|
16
|
-
* minimal changes to PG::Connection[http://deveiate.org/code/pg/PG/Connection.html] API,
|
17
|
-
* configurable timeouts (connect or execute) of asynchronous processing,
|
18
|
-
* additional Fiber-aware version supporting EM-Synchrony[https://github.com/igrigorik/em-synchrony].
|
19
|
-
|
20
|
-
== BUGS/LIMITATIONS
|
21
|
-
|
22
|
-
* no async support for: COPY commands (+get_copy_data+, +put_copy_data+),
|
23
|
-
+wait_for_notify+ and +transaction+
|
24
|
-
* actually no ActiveRecord nor Sequel support (you are welcome to contribute).
|
25
|
-
* doesn't work on Windows (issue #7)
|
26
|
-
|
27
|
-
== API Changes between versions
|
28
|
-
|
29
|
-
=== 0.1.x -> 0.2.x
|
30
|
-
* +on_reconnect+ renamed to more accurate +on_autoreconnect+
|
31
|
-
(well, it's not used by PG::EM::Client#reset call).
|
32
|
-
* +async_autoreconnect+ is +false+ by default if +on_autoreconnect+
|
33
|
-
is *not* specified as initialization option.
|
34
|
-
|
35
|
-
== TODO:
|
36
|
-
|
37
|
-
* implement EM adapted version of +get_copy_data+, +put_copy_data+,
|
38
|
-
+wait_for_notify+ and +transaction+
|
39
|
-
* add some fd/socket hackery to get it working on Windows (issue #7)
|
40
|
-
* em-synchrony ORM (ActiveRecord, Sequel and maybe Datamapper) support
|
41
|
-
as separate projects
|
42
|
-
* present more benchmarks
|
43
|
-
|
44
|
-
== REQUIREMENTS
|
45
|
-
|
46
|
-
* ruby >= 1.9 (tested: 1.9.3-p194, 1.9.2-p320, 1.9.1-p378)
|
47
|
-
* https://bitbucket.org/ged/ruby-pg >= 0.13.2 (>= 0.14 recommended)
|
48
|
-
* PostgreSQL[http://www.postgresql.org/ftp/source/] RDBMS >= 8.3
|
49
|
-
* http://rubyeventmachine.com >= 0.12.10
|
50
|
-
* (optional) EM-Synchrony[https://github.com/igrigorik/em-synchrony]
|
51
|
-
|
52
|
-
== INSTALL
|
53
|
-
|
54
|
-
$ [sudo] gem install em-pg-client
|
55
|
-
|
56
|
-
==== Gemfile
|
57
|
-
|
58
|
-
# eventmachine
|
59
|
-
gem "em-pg-client", "~> 0.2.1", :require => 'pg/em'
|
60
|
-
# em-synchrony
|
61
|
-
gem "em-pg-client", "~> 0.2.1", :require => ['pg/em', 'em-synchrony/pg']
|
62
|
-
|
63
|
-
==== Github
|
64
|
-
|
65
|
-
git clone git://github.com/royaltm/ruby-em-pg-client.git
|
66
|
-
|
67
|
-
== WHY?
|
68
|
-
|
69
|
-
Because I didn't find any ruby-pg's EM implementation to fit my needs.
|
70
|
-
I've found at least 3 other implementations of EM postgres client:
|
71
|
-
|
72
|
-
* https://github.com/jzimmek/em-postgresql-sequel
|
73
|
-
* https://github.com/leftbee/em-postgresql-adapter
|
74
|
-
* https://github.com/jtoy/em-postgres
|
75
|
-
|
76
|
-
and (except the EM-bundled one which uses no longer maintained postgres-pr library)
|
77
|
-
all of them have similiar flaws:
|
78
|
-
|
79
|
-
* 2 of them are designed to support some ORM (ActiveRecord or Sequel),
|
80
|
-
so they are EM-Synchrony only,
|
81
|
-
* non-standard API method names,
|
82
|
-
* no (nonexistent or non-working) autoreconnect implementation,
|
83
|
-
* poor error handling,
|
84
|
-
* not fully supporting asynchronous PG::Connection API.
|
85
|
-
|
86
|
-
The last one is worth some comment:
|
87
|
-
|
88
|
-
They all use blocking methods to retrieve whole result from server
|
89
|
-
(PGConn#block[http://deveiate.org/code/pg/PG/Connection.html#method-i-block] or
|
90
|
-
PGConn#get_result[http://deveiate.org/code/pg/PG/Connection.html#method-i-get_result] which also
|
91
|
-
blocks when there is not enough buffered data on socket).
|
92
|
-
|
93
|
-
This implementation makes use of non-blocking:
|
94
|
-
PGConn#is_busy[http://deveiate.org/code/pg/PG/Connection.html#method-i-is_busy] and
|
95
|
-
PGConn#consume_input[http://deveiate.org/code/pg/PG/Connection.html#method-i-consume_input] methods.
|
96
|
-
Depending on the size of queries results and the level of concurrency, the gain in overall speed and
|
97
|
-
responsiveness of your application might be actually quite huge. I've done some
|
98
|
-
tests[link:BENCHMARKS.rdoc] already.
|
99
|
-
|
100
|
-
== Thanks
|
101
|
-
|
102
|
-
The greetz go to:
|
103
|
-
- Authors[https://bitbucket.org/ged/ruby-pg/wiki/Home#!copying] of +pg+ driver (especially for its async-api)
|
104
|
-
- Francis Cianfrocca for great reactor framework (EventMachine[https://github.com/eventmachine/eventmachine])
|
105
|
-
- Ilya Grigorik (igrigorik[https://github.com/igrigorik]) for (untangling[http://www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers/]) EM with Fibers
|
106
|
-
|
107
|
-
== USAGE
|
108
|
-
+em-pg-client+ provides PG::EM::Client class which inherits
|
109
|
-
PG::Connection[http://deveiate.org/code/pg/PG/Connection.html].
|
110
|
-
You can work with PG::EM::Client almost the same way you would with
|
111
|
-
PG::Connection.
|
112
|
-
|
113
|
-
The real difference begins when you turn EventMachine reactor on.
|
114
|
-
|
115
|
-
=== BASIC
|
116
|
-
|
117
|
-
require 'pg/em'
|
118
|
-
|
119
|
-
# no async
|
120
|
-
pg = PG::EM::Client.new dbname: 'test'
|
121
|
-
pg.query('select * from foo') do |result|
|
122
|
-
puts Array(result).inspect
|
123
|
-
end
|
124
|
-
|
125
|
-
# asynchronous
|
126
|
-
EM.run do
|
127
|
-
df = pg.query('select * from foo')
|
128
|
-
df.callback { |result|
|
129
|
-
puts Array(result).inspect
|
130
|
-
EM.stop
|
131
|
-
}
|
132
|
-
df.errback {|ex|
|
133
|
-
raise ex
|
134
|
-
}
|
135
|
-
puts "sent"
|
136
|
-
end
|
137
|
-
|
138
|
-
# alternatively
|
139
|
-
EM.run do
|
140
|
-
pg.query('select * from foo') do |result|
|
141
|
-
raise result if result.is_a? ::Exception
|
142
|
-
puts Array(result).inspect
|
143
|
-
EM.stop
|
144
|
-
end
|
145
|
-
puts "sent"
|
146
|
-
end
|
147
|
-
|
148
|
-
=== PG::Connection methods adapted to EventMachine
|
149
|
-
The list of PG::EM::Client async methods for processing with EventMachine.
|
150
|
-
|
151
|
-
==== 1. Async methods (always returning +Deferrable+ object):
|
152
|
-
|
153
|
-
* +Client.async_connect+ (singleton)
|
154
|
-
* +async_reset+
|
155
|
-
* +async_exec+ (alias: +async_query+)
|
156
|
-
* +async_prepare+
|
157
|
-
* +async_exec_prepared+
|
158
|
-
* +async_describe_prepared+
|
159
|
-
* +async_describe_portal+
|
160
|
-
|
161
|
-
For arguments of theese methods consult their original blocking (without +async_+ prefix)
|
162
|
-
counterparts in PG::Connection[http://deveiate.org/code/pg/PG/Connection.html] manual.
|
163
|
-
|
164
|
-
Use +callback+ on the returned +Deferrable+ to receive result.
|
165
|
-
The result you receive is PG::EM::Client for PG::EM::Client.async_connect
|
166
|
-
and +async_reset+, and PG::Result[http://deveiate.org/code/pg/PG/Result.html] for the rest
|
167
|
-
of the methods. The received PG::EM::Client is in a connected state and ready for queries.
|
168
|
-
You need to +clear+ obtained PG::Result object yourself or leave it to +gc+.
|
169
|
-
|
170
|
-
To detect a failure in an executed method use +errback+ on returned +Deferrable+.
|
171
|
-
You should expect an instance of +Exception+ (usually PG::Error) as +errback+
|
172
|
-
argument. You may check its +backtrace+ to find origin of the error.
|
173
|
-
|
174
|
-
==== 2. Async / blocking methods (returning +Deferrable+ only when EM is running):
|
175
|
-
|
176
|
-
* +exec+ (alias: +query+)
|
177
|
-
* +prepare+
|
178
|
-
* +exec_prepared+
|
179
|
-
* +describe_prepared+
|
180
|
-
* +describe_portal+
|
181
|
-
|
182
|
-
Outside EventMachine's event loop these methods are regular, blocking PG::Connection
|
183
|
-
methods.
|
184
|
-
|
185
|
-
All the methods (1 & 2) accept block argument which they attach to +callback+ and +errback+
|
186
|
-
hooks of returned +Deferrable+.
|
187
|
-
|
188
|
-
You may also mix async and blocking methods without closing the connection.
|
189
|
-
You only need to start/stop EventMachine in between async calls.
|
190
|
-
|
191
|
-
==== Special options
|
192
|
-
There are 3 additional connection options and 1 standard +pg+ option used by
|
193
|
-
async methods. You may add them as one of the *hash* options to
|
194
|
-
PG::EM::Client.new or PG::EM::Client.async_connect or simply use accessor
|
195
|
-
methods to change them on the fly. The additional options are not passed to
|
196
|
-
+libpq+.
|
197
|
-
|
198
|
-
The options are:
|
199
|
-
|
200
|
-
- +async_autoreconnect+ (+true+ / +false+ with default +false+ unless
|
201
|
-
+on_autoreconnect+ is specified)
|
202
|
-
allows automatic re-connection when there was a problem with connection
|
203
|
-
to the server,
|
204
|
-
- +on_autoreconnect+ (+nil+ / +Proc+ with default +nil+)
|
205
|
-
a hook which is called after auto-reconnecting,
|
206
|
-
- +query_timeout+ (+Float+ / +Fixnum+ with default +0+)
|
207
|
-
allows to set timeout for query execution,
|
208
|
-
- +connect_timeout+ (+Float+ / +Fixnum+ with default +0+)
|
209
|
-
connection establishing and resetting timeout.
|
210
|
-
|
211
|
-
Only +connect_timeout+ is a standard +libpq+ option, although changing it by
|
212
|
-
accessor method only affects asynchronous functions.
|
213
|
-
|
214
|
-
=== AUTORECONNECTING IN ASYNC MODE
|
215
|
-
Autoreconnecting is done in non-blocking manner using +async_reset+ internally.
|
216
|
-
|
217
|
-
EM.run do
|
218
|
-
pg = PG::EM::Client.new dbname: 'test',
|
219
|
-
connect_timeout: 5, query_timeout: 50,
|
220
|
-
async_autoreconnect: true
|
221
|
-
try_query = lambda do |&blk|
|
222
|
-
pg.query('select * from foo') do |result|
|
223
|
-
raise result if result.is_a? ::Exception
|
224
|
-
puts Array(result).inspect
|
225
|
-
blk.call
|
226
|
-
end
|
227
|
-
end
|
228
|
-
try_query.call {
|
229
|
-
system 'pg_ctl stop -m fast'
|
230
|
-
system 'pg_ctl start -w'
|
231
|
-
try_query.call { EM.stop }
|
232
|
-
}
|
233
|
-
end
|
234
|
-
|
235
|
-
to enable this feature call:
|
236
|
-
|
237
|
-
pg.async_autoreconnect = true
|
238
|
-
|
239
|
-
or
|
240
|
-
|
241
|
-
pg = PG::EM::Client.new dbname: 'test',
|
242
|
-
async_autoreconnect: true
|
243
|
-
|
244
|
-
It's also possible to define +on_autoreconnect+ callback to be invoked
|
245
|
-
while the connection has been reset. It's called just before the send query
|
246
|
-
command is executed:
|
247
|
-
|
248
|
-
EM.run do
|
249
|
-
pg = PG::EM::Client.new dbname: 'test',
|
250
|
-
async_autoreconnect: true
|
251
|
-
pg.prepare('bar', 'select * from foo order by cdate desc') do
|
252
|
-
pg.on_autoreconnect = proc { |c, e|
|
253
|
-
c.prepare('bar', 'select * from foo order by cdate desc')
|
254
|
-
}
|
255
|
-
try_query = lambda do |&blk|
|
256
|
-
pg.exec_prepared('bar') do |result|
|
257
|
-
raise result if result.is_a? ::Exception
|
258
|
-
puts Array(result).inspect
|
259
|
-
blk.call
|
260
|
-
end
|
261
|
-
end
|
262
|
-
try_query.call {
|
263
|
-
system 'pg_ctl stop -m fast'
|
264
|
-
system 'pg_ctl start -w'
|
265
|
-
try_query.call { EM.stop }
|
266
|
-
}
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
As you can see it's possible to send async query from inside +on_autoreconnect+
|
271
|
-
proc. However you have to pass +Deferrable+ from the async callback to the
|
272
|
-
caller. See PG::EM::Client#on_autoreconnect docs for details.
|
273
|
-
|
274
|
-
=== TRUE ASYNC
|
275
|
-
For non-blocking connect use PG::EM::Client.async_connect and
|
276
|
-
PG::EM::Client#async_reset for asynchronous re-connect. Like other async
|
277
|
-
methods they return +Deferrable+ object.
|
278
|
-
Use Deferrable's #callback to obtain already connected PG::EM::Client.
|
279
|
-
|
280
|
-
EM.run do
|
281
|
-
pool = (1..10).map {
|
282
|
-
PG::EM::Client.async_connect dbname: 'test',
|
283
|
-
connect_timeout: 5, query_timeout: 50 }
|
284
|
-
|
285
|
-
togo = pool.length
|
286
|
-
|
287
|
-
pool.each_with_index do |df, i|
|
288
|
-
df.callback do |pg|
|
289
|
-
pg.query("select * from foo") do |result|
|
290
|
-
puts "recv: #{i}"
|
291
|
-
EM.stop if (togo-=1).zero?
|
292
|
-
end
|
293
|
-
puts "sent: #{i}"
|
294
|
-
end
|
295
|
-
df.errback { |ex| raise ex }
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
=== Fibers / EM-Synchrony
|
300
|
-
There is a special version of PG::EM::Client library with fiber aware methods
|
301
|
-
for EM-Synchrony or other implementations of Fiber untangled EventMachine.
|
302
|
-
|
303
|
-
The +require+ string is "em-synchrony/pg" instead of "pg/em".
|
304
|
-
|
305
|
-
+em-synchrony/pg+ version of PG::EM::Client.new is fully asynchronous and
|
306
|
-
blocks only current fiber. This also applies to PG::EM::Client#reset.
|
307
|
-
|
308
|
-
require 'em-synchrony'
|
309
|
-
require 'em-synchrony/pg'
|
310
|
-
|
311
|
-
EM.synchrony do
|
312
|
-
pg = PG::EM::Client.new dbname: 'test'
|
313
|
-
pg.query('select * from foo') do |result|
|
314
|
-
puts Array(result).inspect
|
315
|
-
end
|
316
|
-
EM.stop
|
317
|
-
end
|
318
|
-
|
319
|
-
Although em-synchrony[https://github.com/igrigorik/em-synchrony/] provides
|
320
|
-
very nice set of tools for untangled EventMachine, you don't really require
|
321
|
-
it to fully benefit from this version of PG::EM::Client.
|
322
|
-
|
323
|
-
==== PG::Connection methods adapted to EM-Synchrony
|
324
|
-
The list of PG::EM::Client fiber aware methods for processing with
|
325
|
-
EM-Synchrony / EventMachine.
|
326
|
-
|
327
|
-
All +async_*+ methods are exactly the same as in pure EventMachine version
|
328
|
-
of PG::EM::Client.
|
329
|
-
|
330
|
-
The fiber aware methods are:
|
331
|
-
|
332
|
-
* +Client.connect+ (singleton, alias: +new+, +open+, +setdb+, +setdblogin+)
|
333
|
-
* +reset+
|
334
|
-
* +exec+ (alias: +query+)
|
335
|
-
* +prepare+
|
336
|
-
* +exec_prepared+
|
337
|
-
* +describe_prepared+
|
338
|
-
* +describe_portal+
|
339
|
-
|
340
|
-
Under the hood, these methods call async counterparts of themselves and +yield+ from current
|
341
|
-
fiber awaiting for the result. The PG::Result (or PG::EM::Client for +connect+
|
342
|
-
and +reset+) is then returned to the caller. If code block was given, it is
|
343
|
-
executed with result as the argument. In that case the value of the block is
|
344
|
-
returned instead and PG::Result is cleared (or in case of +connect+ or +reset+
|
345
|
-
PG::EM::Client is being closed) after executing block. From single fiber point
|
346
|
-
of view, they behave like regular blocking PG::Connection methods.
|
347
|
-
|
348
|
-
Each of them is also automatic, detecting if EventMachine is running.
|
349
|
-
If called outside EM event loop they are exactly the original methods of
|
350
|
-
PG::Connection.
|
351
|
-
|
352
|
-
Like in pure EventMachine version you can mix async, fiber aware and
|
353
|
-
blocking methods without finishing the connection. You only need to
|
354
|
-
start/stop EventMachine in between async calls.
|
355
|
-
|
356
|
-
==== Handling errors
|
357
|
-
|
358
|
-
EM.synchrony do
|
359
|
-
begin
|
360
|
-
pg.query('select * from foo') do |result|
|
361
|
-
puts result
|
362
|
-
end
|
363
|
-
rescue PG::Error => e
|
364
|
-
puts "PSQL error: #{e.inspect}"
|
365
|
-
end
|
366
|
-
EM.stop
|
367
|
-
end
|
368
|
-
|
369
|
-
==== Parallel async queries
|
370
|
-
|
371
|
-
EM.synchrony do
|
372
|
-
pg = EM::Synchrony::ConnectionPool.new(size: 2) do
|
373
|
-
PG::EM::Client.new :dbname => 'test'
|
374
|
-
end
|
375
|
-
multi = EventMachine::Synchrony::Multi.new
|
376
|
-
multi.add :foo, pg.aquery('select * from foo') # or #async_query()
|
377
|
-
multi.add :bar, pg.aquery('select * from bar') # #aquery() is just an alias
|
378
|
-
res = multi.perform
|
379
|
-
p res
|
380
|
-
EM.stop
|
381
|
-
end
|
382
|
-
|
383
|
-
==== Fiber Concurrency
|
384
|
-
|
385
|
-
EM.synchrony do
|
386
|
-
# use ConnectionPool when more Fibers will be querying at the same time!
|
387
|
-
pg = EM::Synchrony::ConnectionPool.new(size: 5) do
|
388
|
-
PG::EM::Client.new :dbname => 'test'
|
389
|
-
end
|
390
|
-
counter = 0
|
391
|
-
EM::Synchrony::FiberIterator.new(['select * from foo']*10, 5) do |query|
|
392
|
-
i = counter
|
393
|
-
pg.query(query) do |result|
|
394
|
-
puts "recv: #{i}"
|
395
|
-
end
|
396
|
-
puts "sent: #{i}"
|
397
|
-
counter += 1
|
398
|
-
end
|
399
|
-
EM.stop
|
400
|
-
end
|
401
|
-
|
402
|
-
==== Async reconnect with on_autoreconnect callback
|
403
|
-
|
404
|
-
EM.synchrony do
|
405
|
-
on_autoreconnect = proc do |c, e|
|
406
|
-
c.prepare('bar', 'select * from foo order by cdate desc')
|
407
|
-
end
|
408
|
-
pg = EM::Synchrony::ConnectionPool.new(size: 5) do
|
409
|
-
p = PG::EM::Client.new dbname: 'test', on_autoreconnect: on_autoreconnect
|
410
|
-
on_autoreconnect.call p
|
411
|
-
p
|
412
|
-
end
|
413
|
-
try_query = lambda do
|
414
|
-
pg.exec_prepared('bar') do |result|
|
415
|
-
raise result if result.is_a? ::Exception
|
416
|
-
puts Array(result).inspect
|
417
|
-
end
|
418
|
-
end
|
419
|
-
try_query.call
|
420
|
-
system 'pg_ctl stop -m fast'
|
421
|
-
system 'pg_ctl start -w'
|
422
|
-
try_query.call
|
423
|
-
EM.stop
|
424
|
-
end
|
425
|
-
|
426
|
-
Specifying +on_autoreconnect+ as PG::EM::Client.new initialization option,
|
427
|
-
implicitly enables +async_autoreconnect+.
|
428
|
-
|
429
|
-
== LICENCE
|
430
|
-
|
431
|
-
The MIT License - Copyright (c) 2012 Rafał Michalski
|