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.
@@ -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