em-pg-client 0.3.2 → 0.3.3
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.
- 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
|
|