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.
@@ -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
@@ -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.2-1-linux-x64
8
- - PGVERSION=9.0 PGBUILD=9.0.15-1-linux-x64
9
- - PGVERSION=8.4 PGBUILD=8.4.19-1-linux-x64
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.2"
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
- (repeat/concurrency).times do
37
- (0...concurrency).map do |i|
38
- Thread.new do
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.each(&:join)
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
- (repeat/concurrency).times do
50
- FiberIterator.new((0...concurrency), concurrency).each do
51
- db.hold do |pg|
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
@@ -1,5 +1,5 @@
1
1
  module PG
2
2
  module EM
3
- VERSION = '0.3.2'
3
+ VERSION = '0.3.3'
4
4
  end
5
5
  end
@@ -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 connection pool, make sure to acquire a single
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
- # You can also specify this as an option to {new} or {connect_defer}.
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
- # You can also specify this as an option to {new} or {connect_defer}.
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
- # as an initialization option.
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 can also specify this as an option to {new} or {connect_defer}.
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
- # Proc that is called after a connection with the server has been
144
- # automatically re-established. It's being invoked just before the
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
- # The first argument it receives is the +connection+ instance.
148
- # The second is the original +exception+ that caused the reconnecting
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
- # The proc can control the later action with its return value:
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 deferred
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 pending
164
- # transaction before the connection was reset.
165
- #
166
- # It's possible to execute queries from inside of the proc.
167
- #
168
- # You may pass this proc as an option to {new} or {connect_defer}.
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
- # @example How to use prepare in on_autoreconnect hook
171
- # pg.on_autoreconnect = proc do |conn, ex|
172
- # conn.prepare("species_by_name",
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
- # end
250
+ # })
175
251
  #
176
- attr_accessor :on_autoreconnect
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 as +connection_hash+ argument variant. They will be ignored
257
- # if passed as a +connection_string+.
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 as +connection_hash+ argument variant. They will be ignored
358
- # if passed as a +connection_string+.
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 then
401
- # calls parent PG::Connection#finish[http://deveiate.org/code/pg/PG/Connection.html#method-i-finish].
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.protect_and_succeed do
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