em-pg-client 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +4 -3
- data/HISTORY.md +11 -0
- data/README.md +2 -1
- data/Rakefile +30 -1
- data/benchmarks/single_row_mode.rb +8 -8
- data/lib/pg/em-version.rb +1 -1
- data/lib/pg/em.rb +137 -40
- data/lib/pg/em/client/connect_watcher.rb +32 -2
- data/lib/pg/em/connection_pool.rb +41 -9
- data/spec/em_client_autoreconnect.rb +45 -0
- data/spec/em_client_on_connect.rb +171 -0
- data/spec/em_connection_pool.rb +198 -0
- data/spec/em_synchrony_client_autoreconnect.rb +48 -0
- data/spec/pg_em_client_options.rb +39 -1
- data/spec/pg_em_connection_pool.rb +25 -1
- metadata +17 -27
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 94c88a93f1492f98d885d96bc35fafbbe7dbf88f
|
4
|
+
data.tar.gz: 2e58b30703259d32a712c71502c3a6de66280672
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7d50ddbe83812b62fe3364192c03e493f61fa70900f040fc89abc4d3b94c85def6933129e0d1aa3457e1fc74b3b5fba71c6d6f2322535fabdb9744f5b96f3c32
|
7
|
+
data.tar.gz: a3968e95cb4af73791a818af4ec1542aac9a5b1287b901915d627acc88ba31a5476925988684bef31318f654747237f033bb55b8d810cc75504a3ddbffef85c4
|
data/.travis.yml
CHANGED
@@ -3,10 +3,11 @@ rvm:
|
|
3
3
|
- 1.9.2
|
4
4
|
- 1.9.3
|
5
5
|
- 2.0.0
|
6
|
+
- 2.1.0
|
6
7
|
env:
|
7
|
-
- PGVERSION=9.3 PGBUILD=9.3.
|
8
|
-
- PGVERSION=9.0 PGBUILD=9.0.
|
9
|
-
- PGVERSION=8.4 PGBUILD=8.4.
|
8
|
+
- PGVERSION=9.3 PGBUILD=9.3.4-1-linux-x64
|
9
|
+
- PGVERSION=9.0 PGBUILD=9.0.17-1-linux-x64
|
10
|
+
- PGVERSION=8.4 PGBUILD=8.4.21-1-linux-x64
|
10
11
|
before_install:
|
11
12
|
- sudo /etc/init.d/postgresql stop
|
12
13
|
- export PGPREFIX=/opt/PostgreSQL/$PGVERSION
|
data/HISTORY.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
0.3.3
|
2
|
+
|
3
|
+
- rake: console for debugging
|
4
|
+
- added on_connect callback
|
5
|
+
- given block to ConnectionPool.new set as on_connect option fallback
|
6
|
+
- spec: connection pool database tests
|
7
|
+
- spec: on_connect tests
|
8
|
+
- spec: on_connect + on_autoreconnect auto re-connect tests
|
9
|
+
- spec: added travis rvm 2.1.0 and updated postgres server versions
|
10
|
+
- sugar: on_connect and on_autoreconnect block setters
|
11
|
+
|
1
12
|
0.3.2
|
2
13
|
|
3
14
|
- fix: asynchronous get_result performance
|
data/README.md
CHANGED
@@ -96,7 +96,7 @@ Install
|
|
96
96
|
#### Gemfile
|
97
97
|
|
98
98
|
```ruby
|
99
|
-
gem "em-pg-client", "~> 0.3.
|
99
|
+
gem "em-pg-client", "~> 0.3.3"
|
100
100
|
```
|
101
101
|
|
102
102
|
#### Github
|
@@ -181,6 +181,7 @@ The options are:
|
|
181
181
|
* `query_timeout`
|
182
182
|
* `async_autoreconnect`
|
183
183
|
* `on_autoreconnect`
|
184
|
+
* `on_connect`
|
184
185
|
|
185
186
|
Only `connect_timeout` is a standard `libpq` option, although changing it with
|
186
187
|
the accessor method affects asynchronous functions only.
|
data/Rakefile
CHANGED
@@ -40,9 +40,27 @@ namespace :test do
|
|
40
40
|
end
|
41
41
|
|
42
42
|
desc "Run safe tests only"
|
43
|
-
task :safe => [:warn, :spec, :async, :fiber]
|
43
|
+
task :safe => [:warn, :spec, :async, :fiber, :on_connect, :pool]
|
44
44
|
task :async => [:async_inet, :async_unix]
|
45
45
|
task :fiber => [:fiber_inet, :fiber_unix]
|
46
|
+
task :on_connect => [:on_connect_inet, :on_connect_unix]
|
47
|
+
task :pool => [:pool_inet, :pool_unix]
|
48
|
+
|
49
|
+
task :pool_inet do
|
50
|
+
sh env_inet.merge('COVNAME'=>'pool:inet'), "rspec spec/em_connection_pool.rb"
|
51
|
+
end
|
52
|
+
|
53
|
+
task :pool_unix do
|
54
|
+
sh env_unix.merge('COVNAME'=>'pool:unix'), "rspec spec/em_connection_pool.rb" unless windows_os?
|
55
|
+
end
|
56
|
+
|
57
|
+
task :on_connect_inet do
|
58
|
+
sh env_inet.merge('COVNAME'=>'on_connect:inet'), "rspec spec/em_client_on_connect.rb"
|
59
|
+
end
|
60
|
+
|
61
|
+
task :on_connect_unix do
|
62
|
+
sh env_unix.merge('COVNAME'=>'on_connect:unix'), "rspec spec/em_client_on_connect.rb" unless windows_os?
|
63
|
+
end
|
46
64
|
|
47
65
|
task :async_inet do
|
48
66
|
sh env_inet.merge('COVNAME'=>'async:inet'), "rspec spec/em_client.rb"
|
@@ -126,3 +144,14 @@ task :benchmark do
|
|
126
144
|
benchmark(i)
|
127
145
|
end
|
128
146
|
end
|
147
|
+
|
148
|
+
desc "Console"
|
149
|
+
task :console do
|
150
|
+
require 'irb'
|
151
|
+
require 'irb/completion'
|
152
|
+
require 'em-synchrony'
|
153
|
+
require 'em-pg-client'
|
154
|
+
require 'pg/em/connection_pool'
|
155
|
+
ARGV.clear
|
156
|
+
IRB.start
|
157
|
+
end
|
@@ -33,22 +33,22 @@ end
|
|
33
33
|
|
34
34
|
def threads(repeat, concurrency)
|
35
35
|
db = Hash.new { |pool, id| pool[id] = PG::Connection.new }
|
36
|
-
(
|
37
|
-
|
38
|
-
|
36
|
+
(0...concurrency).map do |i|
|
37
|
+
Thread.new do
|
38
|
+
(repeat/concurrency).times do
|
39
39
|
stream_results(db[i])
|
40
40
|
end
|
41
|
-
end
|
42
|
-
end
|
41
|
+
end
|
42
|
+
end.each(&:join)
|
43
43
|
db.each_value(&:finish).clear
|
44
44
|
end
|
45
45
|
|
46
46
|
def fibers(repeat, concurrency)
|
47
47
|
EM.synchrony do
|
48
48
|
db = PG::EM::ConnectionPool.new size: concurrency, lazy: true
|
49
|
-
(
|
50
|
-
|
51
|
-
|
49
|
+
FiberIterator.new((0...concurrency), concurrency).each do
|
50
|
+
db.hold do |pg|
|
51
|
+
(repeat/concurrency).times do
|
52
52
|
stream_results(pg)
|
53
53
|
end
|
54
54
|
end
|
data/lib/pg/em-version.rb
CHANGED
data/lib/pg/em.rb
CHANGED
@@ -18,6 +18,7 @@ require 'pg/em/client/connect_watcher'
|
|
18
18
|
|
19
19
|
module PG
|
20
20
|
module EM
|
21
|
+
ROOT_FIBER = Fiber.current
|
21
22
|
|
22
23
|
# == PostgreSQL EventMachine client
|
23
24
|
#
|
@@ -96,24 +97,23 @@ module PG
|
|
96
97
|
#
|
97
98
|
# {#describe_prepared} and {#exec_prepared} after
|
98
99
|
# {#prepare} should only be invoked on the *same* connection.
|
99
|
-
# If you are using a
|
100
|
-
# connection first
|
100
|
+
# If you are using a {ConnectionPool}, make sure to acquire a single
|
101
|
+
# connection first or execute +prepare+ command on every connection
|
102
|
+
# using +#on_connect+ hook.
|
101
103
|
#
|
102
104
|
class Client < PG::Connection
|
103
105
|
|
104
|
-
ROOT_FIBER = Fiber.current
|
105
|
-
|
106
106
|
# @!attribute connect_timeout
|
107
107
|
# @return [Float] connection timeout in seconds
|
108
108
|
# Connection timeout. Affects {#reset} and {#reset_defer}.
|
109
109
|
#
|
110
|
-
# Changing this property does not affect thread-blocking {#reset}
|
111
|
-
#
|
112
|
-
# However if passed as initialization option, it also affects blocking
|
113
|
-
# {#reset}.
|
110
|
+
# Changing this property does not affect thread-blocking {#reset} unless
|
111
|
+
# passed as a +connection_hash+.
|
114
112
|
#
|
115
113
|
# To enable it set to some positive value. To disable it: set to 0.
|
116
|
-
#
|
114
|
+
#
|
115
|
+
# You may set +:connect_timeout+ in a +connection_hash+ argument
|
116
|
+
# passed to {new} or {connect_defer}.
|
117
117
|
attr_accessor :connect_timeout
|
118
118
|
|
119
119
|
# @!attribute query_timeout
|
@@ -123,57 +123,141 @@ module PG
|
|
123
123
|
# {#reset} and {#reset_defer}.
|
124
124
|
#
|
125
125
|
# To enable it set to some positive value. To disable it: set to 0.
|
126
|
-
#
|
126
|
+
#
|
127
|
+
# You may set +:query_timeout+ in a +connection_hash+ argument
|
128
|
+
# passed to {new} or {connect_defer}.
|
127
129
|
attr_accessor :query_timeout
|
128
130
|
|
129
131
|
# @!attribute async_autoreconnect
|
130
132
|
# @return [Boolean] asynchronous auto re-connect status
|
131
133
|
# Enable/disable auto re-connect feature (+true+/+false+).
|
132
134
|
# Defaults to +false+ unless {#on_autoreconnect} is specified
|
133
|
-
#
|
135
|
+
# in a +connection_hash+.
|
134
136
|
#
|
135
137
|
# Changing {#on_autoreconnect} with accessor method doesn't change
|
136
138
|
# the state of {#async_autoreconnect}.
|
137
139
|
#
|
138
|
-
# You
|
140
|
+
# You may set +:async_autoreconnect+ in a +connection_hash+ argument
|
141
|
+
# passed to {new} or {connect_defer}.
|
139
142
|
attr_accessor :async_autoreconnect
|
140
143
|
|
141
144
|
# @!attribute on_autoreconnect
|
142
145
|
# @return [Proc<Client, Error>] auto re-connect hook
|
143
|
-
#
|
144
|
-
# automatically re-established. It's being invoked
|
145
|
-
# pending command is sent to the server.
|
146
|
+
# A proc like object that is being called after a connection with the
|
147
|
+
# server has been automatically re-established. It's being invoked
|
148
|
+
# just before the pending command is sent to the server.
|
146
149
|
#
|
147
|
-
#
|
148
|
-
#
|
150
|
+
# @yieldparam pg [Client] re-connected client instance
|
151
|
+
# @yieldparam error [Exception] an error after which the auto re-connect
|
152
|
+
# process began.
|
153
|
+
# @yieldreturn [false|true|Exception|EM::Deferrable|*]
|
154
|
+
#
|
155
|
+
# The first argument it receives is the connected {Client} instance.
|
156
|
+
# The second is the original +error+ that caused the reconnecting
|
149
157
|
# process.
|
150
158
|
#
|
151
|
-
#
|
159
|
+
# It's possible to execute queries from the +on_autoreconnect+ hook.
|
160
|
+
# Code is being executed in a fiber context, so both deferrable and
|
161
|
+
# fiber-synchronized query commands may be used.
|
162
|
+
#
|
163
|
+
# If exception is raised during execution of the +on_autoreconnect+
|
164
|
+
# hook the reset operation will fail with that exception.
|
165
|
+
#
|
166
|
+
# The hook can control later actions with its return value:
|
152
167
|
#
|
153
168
|
# - +false+ (explicitly, +nil+ is ignored) - the original +exception+
|
154
169
|
# is raised/passed back and the pending query command is not sent
|
155
170
|
# again to the server.
|
156
171
|
# - +true+ (explicitly, truish values are ignored), the pending command
|
157
172
|
# is called regardless of the connection's last transaction status.
|
158
|
-
# - Exception object - is raised/passed back and the pending command
|
173
|
+
# - +Exception+ object - is raised/passed back and the pending command
|
159
174
|
# is not sent.
|
160
|
-
# - Deferrable object - the chosen action will depend on the
|
161
|
-
# status.
|
175
|
+
# - +Deferrable+ object - the chosen action will depend on the returned
|
176
|
+
# deferrable status.
|
162
177
|
# - Other values are ignored and the pending query command is
|
163
|
-
# immediately sent to the server unless there was a
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
178
|
+
# immediately sent to the server unless there was a transaction in
|
179
|
+
# progress before the connection was reset.
|
180
|
+
#
|
181
|
+
# If both +on_connect+ and +on_autoreconnect+ hooks are set,
|
182
|
+
# the +on_connect+ is being called first and +on_autoreconnect+ is
|
183
|
+
# called only when +on_connect+ succeeds.
|
184
|
+
#
|
185
|
+
# You may set +:on_autoreconnect+ hook in a +connection_hash+ argument
|
186
|
+
# passed to {new} or {connect_defer}.
|
187
|
+
#
|
188
|
+
# @example How to use deferrable in on_autoreconnect hook
|
189
|
+
# pg.on_autoreconnect do |pg, e|
|
190
|
+
# logger.warn "PG connection was reset: #{e.inspect}, delaying 1 sec."
|
191
|
+
# EM::DefaultDeferrable.new.tap do |df|
|
192
|
+
# EM.add_timer(1) { df.succeed }
|
193
|
+
# end
|
194
|
+
# end
|
169
195
|
#
|
170
|
-
|
171
|
-
|
172
|
-
|
196
|
+
attr_writer :on_autoreconnect
|
197
|
+
|
198
|
+
def on_autoreconnect(&hook)
|
199
|
+
if block_given?
|
200
|
+
@on_autoreconnect = hook
|
201
|
+
else
|
202
|
+
@on_autoreconnect
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# @!attribute on_connect
|
207
|
+
# @return [Proc<Client,is_async,is_reset>] connect hook
|
208
|
+
# A proc like object that is being called after a connection with
|
209
|
+
# the server has been established.
|
210
|
+
#
|
211
|
+
# @yieldparam pg [Client] connected client instance
|
212
|
+
# @yieldparam is_async [Boolean] flag indicating if the connection
|
213
|
+
# was established asynchronously
|
214
|
+
# @yieldparam is_reset [Boolean] flag indicating if the connection
|
215
|
+
# client was reset
|
216
|
+
# @yieldreturn [EM::Deferrable|*]
|
217
|
+
#
|
218
|
+
# The first argument it receives is the connected {Client} instance.
|
219
|
+
# The second argument is +true+ if the connection was established in
|
220
|
+
# asynchronous manner, +false+ otherwise.
|
221
|
+
# The third argument is +true+ when the connection has been reset or
|
222
|
+
# +false+ on new connection.
|
223
|
+
#
|
224
|
+
# It's possible to execute queries from the +on_connect+ hook.
|
225
|
+
# Code is being executed in a fiber context, so both deferrable and
|
226
|
+
# fiber-synchronized query commands may be used.
|
227
|
+
# However deferrable commands will work only if eventmachine reactor
|
228
|
+
# is running, so check if +is_async+ is +true+.
|
229
|
+
#
|
230
|
+
# If exception is raised during execution of the +on_connect+ hook
|
231
|
+
# the connecting/reset operation will fail with that exception.
|
232
|
+
#
|
233
|
+
# The hook can control later actions with its return value:
|
234
|
+
#
|
235
|
+
# - +Deferrable+ object - the connection establishing status will depend
|
236
|
+
# on the returned deferrable status (only in asynchronous mode).
|
237
|
+
# - Other values are ignored.
|
238
|
+
#
|
239
|
+
# If both +on_connect+ and +on_autoreconnect+ hooks are set,
|
240
|
+
# the +on_connect+ is being called first and +on_autoreconnect+ is
|
241
|
+
# called only when +on_connect+ succeeds.
|
242
|
+
#
|
243
|
+
# You may set +:on_connect+ hook in a +connection_hash+ argument
|
244
|
+
# passed to {new} or {connect_defer}.
|
245
|
+
#
|
246
|
+
# @example How to use prepare in on_connect hook
|
247
|
+
# PG::EM::Client.new(on_connect: proc {|pg|
|
248
|
+
# pg.prepare("species_by_name",
|
173
249
|
# "select id, name from animals where species=$1 order by name")
|
174
|
-
#
|
250
|
+
# })
|
175
251
|
#
|
176
|
-
|
252
|
+
attr_writer :on_connect
|
253
|
+
|
254
|
+
def on_connect(&hook)
|
255
|
+
if block_given?
|
256
|
+
@on_connect = hook
|
257
|
+
else
|
258
|
+
@on_connect
|
259
|
+
end
|
260
|
+
end
|
177
261
|
|
178
262
|
# @!visibility private
|
179
263
|
# Used internally for marking connection as aborted on query timeout.
|
@@ -201,6 +285,7 @@ module PG
|
|
201
285
|
:@async_autoreconnect => nil,
|
202
286
|
:@connect_timeout => nil,
|
203
287
|
:@query_timeout => 0,
|
288
|
+
:@on_connect => nil,
|
204
289
|
:@on_autoreconnect => nil,
|
205
290
|
:@async_command_aborted => false,
|
206
291
|
}.freeze
|
@@ -214,6 +299,13 @@ module PG
|
|
214
299
|
when :async_autoreconnect
|
215
300
|
options[:@async_autoreconnect] = value
|
216
301
|
true
|
302
|
+
when :on_connect
|
303
|
+
if value.respond_to? :call
|
304
|
+
options[:@on_connect] = value
|
305
|
+
else
|
306
|
+
raise ArgumentError, "on_connect must respond to `call'"
|
307
|
+
end
|
308
|
+
true
|
217
309
|
when :on_reconnect
|
218
310
|
raise ArgumentError, "on_reconnect is no longer supported, use on_autoreconnect"
|
219
311
|
when :on_autoreconnect
|
@@ -253,8 +345,8 @@ module PG
|
|
253
345
|
# hooks of the returned deferrable.
|
254
346
|
#
|
255
347
|
# Special {Client} options (e.g.: {#async_autoreconnect}) must be
|
256
|
-
# provided
|
257
|
-
# if passed
|
348
|
+
# provided in a +connection_hash+ argument variant. They will be ignored
|
349
|
+
# if passed in a +connection_string+.
|
258
350
|
#
|
259
351
|
# +client_encoding+ *will* be set according to +Encoding.default_internal+.
|
260
352
|
#
|
@@ -292,7 +384,6 @@ module PG
|
|
292
384
|
df = FeaturedDeferrable.new(&blk)
|
293
385
|
# there can be only one watch handler over the socket
|
294
386
|
# apparently eventmachine has hard time dealing with more than one
|
295
|
-
# for blocking reset this is not needed
|
296
387
|
if @watcher
|
297
388
|
@watcher.detach if @watcher.watching?
|
298
389
|
@watcher = nil
|
@@ -338,6 +429,8 @@ module PG
|
|
338
429
|
@watcher = nil
|
339
430
|
end
|
340
431
|
super
|
432
|
+
@on_connect.call(self, false, true) if @on_connect
|
433
|
+
self
|
341
434
|
end
|
342
435
|
end
|
343
436
|
|
@@ -354,8 +447,8 @@ module PG
|
|
354
447
|
# @raise [PG::Error]
|
355
448
|
#
|
356
449
|
# Special {Client} options (e.g.: {#async_autoreconnect}) must be
|
357
|
-
# provided
|
358
|
-
# if passed
|
450
|
+
# provided in a +connection_hash+ argument variant. They will be ignored
|
451
|
+
# if passed in a +connection_string+.
|
359
452
|
#
|
360
453
|
# +client_encoding+ *will* be set according to +Encoding.default_internal+.
|
361
454
|
#
|
@@ -376,7 +469,11 @@ module PG
|
|
376
469
|
conn
|
377
470
|
end
|
378
471
|
else
|
379
|
-
super(*args)
|
472
|
+
conn = super(*args)
|
473
|
+
if on_connect = conn.on_connect
|
474
|
+
on_connect.call(conn, false, false)
|
475
|
+
end
|
476
|
+
conn
|
380
477
|
end
|
381
478
|
end
|
382
479
|
|
@@ -397,8 +494,8 @@ module PG
|
|
397
494
|
|
398
495
|
# Closes the backend connection.
|
399
496
|
#
|
400
|
-
# Detaches watch handler to prevent memory leak
|
401
|
-
#
|
497
|
+
# Detaches watch handler to prevent memory leak after
|
498
|
+
# calling parent PG::Connection#finish[http://deveiate.org/code/pg/PG/Connection.html#method-i-finish].
|
402
499
|
# @see http://deveiate.org/code/pg/PG/Connection.html#method-i-finish PG::Connection#finish
|
403
500
|
def finish
|
404
501
|
super
|
@@ -42,10 +42,40 @@ module PG
|
|
42
42
|
end
|
43
43
|
@timer.cancel if @timer
|
44
44
|
detach
|
45
|
-
@deferrable.
|
45
|
+
@deferrable.protect do
|
46
46
|
@client.raise_error ConnectionBad unless polling_ok
|
47
47
|
@client.set_default_encoding unless reconnecting?
|
48
|
-
@client
|
48
|
+
if on_connect = @client.on_connect
|
49
|
+
succeed_connection_with_hook(on_connect)
|
50
|
+
else
|
51
|
+
succeed_connection
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def succeed_connection
|
57
|
+
::EM.next_tick { @deferrable.succeed @client }
|
58
|
+
end
|
59
|
+
|
60
|
+
def succeed_connection_with_hook(on_connect)
|
61
|
+
::EM.next_tick do
|
62
|
+
Fiber.new do
|
63
|
+
# call on_connect handler and fail if it raises an error
|
64
|
+
begin
|
65
|
+
returned_df = on_connect.call(@client, true, reconnecting?)
|
66
|
+
rescue => ex
|
67
|
+
@deferrable.fail ex
|
68
|
+
else
|
69
|
+
if returned_df.respond_to?(:callback) && returned_df.respond_to?(:errback)
|
70
|
+
# the handler returned a deferrable
|
71
|
+
returned_df.callback { @deferrable.succeed @client }
|
72
|
+
# fail when handler's deferrable fails
|
73
|
+
returned_df.errback { |ex| @deferrable.fail ex }
|
74
|
+
else
|
75
|
+
@deferrable.succeed @client
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end.resume
|
49
79
|
end
|
50
80
|
end
|
51
81
|
|