pg_pipeline 0.3.3 → 0.3.4

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.
@@ -1,15 +1,63 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "pg"
3
+ require 'pg'
4
4
 
5
- require_relative "errors"
6
- require_relative "runtime"
7
- require_relative "connection_driver"
8
- require_relative "prepared_statement"
9
- require_relative "type_maps"
10
- require_relative "server_caps"
5
+ require_relative 'errors'
6
+ require_relative 'runtime'
7
+ require_relative 'connection_driver'
8
+ require_relative 'prepared_statement'
9
+ require_relative 'type_maps'
10
+ require_relative 'server_caps'
11
11
 
12
12
  module PgPipeline
13
+ PoolSettings = Data.define(
14
+ :pipeline_size, :pinned_size, :max_pending, :max_in_flight, :reconnect,
15
+ :reconnect_interval, :reconnect_backoff_max, :health_check, :health_interval,
16
+ :health_timeout, :cancel_pinned_on_abort
17
+ ) do
18
+ def self.build(options)
19
+ unknown = options.keys - members
20
+ raise ArgumentError, "unknown keyword: #{unknown.first}" unless unknown.empty?
21
+
22
+ new(**normalize_settings(defaults.merge(options)))
23
+ end
24
+
25
+ def self.defaults
26
+ {
27
+ pipeline_size: Pool::DEFAULT_PIPELINE_SIZE,
28
+ pinned_size: Pool::DEFAULT_PINNED_SIZE,
29
+ max_pending: ConnectionDriver::DEFAULT_MAX_PENDING,
30
+ max_in_flight: ConnectionDriver::DEFAULT_MAX_IN_FLIGHT,
31
+ reconnect: true,
32
+ reconnect_interval: Pool::DEFAULT_RECONNECT_INTERVAL,
33
+ reconnect_backoff_max: Pool::DEFAULT_RECONNECT_BACKOFF_MAX,
34
+ health_check: true,
35
+ health_interval: Pool::DEFAULT_HEALTH_INTERVAL,
36
+ health_timeout: Pool::DEFAULT_HEALTH_TIMEOUT,
37
+ cancel_pinned_on_abort: true
38
+ }
39
+ end
40
+
41
+ def self.normalize_settings(values)
42
+ {
43
+ pipeline_size: PoolOps.positive_integer!(values.fetch(:pipeline_size), :pipeline_size),
44
+ pinned_size: PoolOps.nonnegative_integer!(values.fetch(:pinned_size), :pinned_size),
45
+ max_pending: values.fetch(:max_pending),
46
+ max_in_flight: values.fetch(:max_in_flight),
47
+ reconnect: values.fetch(:reconnect),
48
+ reconnect_interval: PoolOps.finite_float!(values.fetch(:reconnect_interval), :reconnect_interval),
49
+ reconnect_backoff_max: PoolOps.finite_float!(values.fetch(:reconnect_backoff_max), :reconnect_backoff_max),
50
+ health_check: values.fetch(:health_check),
51
+ health_interval: PoolOps.finite_float!(values.fetch(:health_interval), :health_interval, allow_zero: true),
52
+ health_timeout: PoolOps.finite_float!(values.fetch(:health_timeout), :health_timeout),
53
+ cancel_pinned_on_abort: values.fetch(:cancel_pinned_on_abort)
54
+ }
55
+ end
56
+
57
+ private_class_method :defaults, :normalize_settings
58
+ end
59
+ private_constant :PoolSettings
60
+
13
61
  class Pool
14
62
  DEFAULT_PIPELINE_SIZE = 4
15
63
  DEFAULT_PINNED_SIZE = 2
@@ -24,30 +72,20 @@ module PgPipeline
24
72
 
25
73
  attr_reader :reconnects, :pipeline_size
26
74
 
27
- def initialize(connection_args,
28
- pipeline_size: DEFAULT_PIPELINE_SIZE,
29
- pinned_size: DEFAULT_PINNED_SIZE,
30
- max_pending: ConnectionDriver::DEFAULT_MAX_PENDING,
31
- max_in_flight: ConnectionDriver::DEFAULT_MAX_IN_FLIGHT,
32
- reconnect: true,
33
- reconnect_interval: DEFAULT_RECONNECT_INTERVAL,
34
- reconnect_backoff_max: DEFAULT_RECONNECT_BACKOFF_MAX,
35
- health_check: true,
36
- health_interval: DEFAULT_HEALTH_INTERVAL,
37
- health_timeout: DEFAULT_HEALTH_TIMEOUT,
38
- cancel_pinned_on_abort: true)
75
+ def initialize(connection_args, **options)
39
76
  @connection_args = connection_args
40
- @pipeline_size = PoolOps.positive_integer!(pipeline_size, :pipeline_size)
41
- @pinned_size = PoolOps.nonnegative_integer!(pinned_size, :pinned_size)
42
- @max_pending = max_pending
43
- @max_in_flight = max_in_flight
44
- @reconnect = reconnect
45
- @reconnect_interval = PoolOps.finite_float!(reconnect_interval, :reconnect_interval)
46
- @reconnect_backoff_max = PoolOps.finite_float!(reconnect_backoff_max, :reconnect_backoff_max)
47
- @health_check = health_check
48
- @health_interval = PoolOps.finite_float!(health_interval, :health_interval, allow_zero: true)
49
- @health_timeout = PoolOps.finite_float!(health_timeout, :health_timeout)
50
- @cancel_pinned_on_abort = cancel_pinned_on_abort
77
+ settings = PoolSettings.build(options)
78
+ @pipeline_size = settings.pipeline_size
79
+ @pinned_size = settings.pinned_size
80
+ @max_pending = settings.max_pending
81
+ @max_in_flight = settings.max_in_flight
82
+ @reconnect = settings.reconnect
83
+ @reconnect_interval = settings.reconnect_interval
84
+ @reconnect_backoff_max = settings.reconnect_backoff_max
85
+ @health_check = settings.health_check
86
+ @health_interval = settings.health_interval
87
+ @health_timeout = settings.health_timeout
88
+ @cancel_pinned_on_abort = settings.cancel_pinned_on_abort
51
89
 
52
90
  @drivers = []
53
91
  @driver_backoff = []
@@ -81,20 +119,10 @@ module PgPipeline
81
119
  end
82
120
 
83
121
  def start
84
- raise Error, "pool already started" if @started
85
- if @closing || @closed
86
- raise ShutdownError, "pool is closing or was closed and cannot be restarted; create a new Pool"
87
- end
88
- raise Error, "pool start requires an active Fiber scheduler" unless Fiber.scheduler
122
+ validate_start!
89
123
 
90
124
  begin
91
- @pipeline_size.times { @drivers << start_pipeline_driver }
92
- @driver_backoff = Array.new(@drivers.size, 0.0)
93
- @driver_attempts = Array.new(@drivers.size, 0)
94
- @driver_last_health = Array.new(@drivers.size, monotonic)
95
- @pinned_gate = Runtime::Semaphore.new(@pinned_size) if @pinned_size.positive?
96
- @started = true
97
- @supervisor = Runtime.spawn(name: :supervisor) { supervise } if @reconnect || @health_check
125
+ start_runtime
98
126
  rescue Exception
99
127
  cleanup_partial_start
100
128
  raise
@@ -103,6 +131,35 @@ module PgPipeline
103
131
  self
104
132
  end
105
133
 
134
+ private
135
+
136
+ def validate_start!
137
+ raise Error, 'pool already started' if @started
138
+ if @closing || @closed
139
+ raise ShutdownError,
140
+ 'pool is closing or was closed and cannot be restarted; create a new Pool'
141
+ end
142
+ raise Error, 'pool start requires an active Fiber scheduler' unless Fiber.scheduler
143
+ end
144
+
145
+ def start_runtime
146
+ @pipeline_size.times { @drivers << start_pipeline_driver }
147
+ driver_count = @drivers.size
148
+ @driver_backoff = Array.new(driver_count, 0.0)
149
+ @driver_attempts = Array.new(driver_count, 0)
150
+ @driver_last_health = Array.new(driver_count, monotonic)
151
+ pinned_size = @pinned_size
152
+ @pinned_gate = Runtime::Semaphore.new(pinned_size) if pinned_size.positive?
153
+ @started = true
154
+ start_supervisor if @reconnect || @health_check
155
+ end
156
+
157
+ def start_supervisor
158
+ @supervisor = Runtime.spawn(name: :supervisor) { supervise }
159
+ end
160
+
161
+ public
162
+
106
163
  def closing? = @closing
107
164
 
108
165
  def pipeline_driver
@@ -110,7 +167,7 @@ module PgPipeline
110
167
 
111
168
  driver = PoolOps.select_driver_into(@drivers, @rr, @rr_slot)
112
169
  @rr = @rr_slot[0]
113
- raise NotDispatchedError, "no live pipeline connections; request was not dispatched" unless driver
170
+ raise NotDispatchedError, 'no live pipeline connections; request was not dispatched' unless driver
114
171
 
115
172
  driver
116
173
  end
@@ -171,9 +228,7 @@ module PgPipeline
171
228
 
172
229
  def dispatch_prepare(statement)
173
230
  drivers = @drivers.select(&:available?)
174
- if drivers.empty?
175
- raise NotDispatchedError, "no live pipeline connections; statement was not prepared"
176
- end
231
+ raise NotDispatchedError, 'no live pipeline connections; statement was not prepared' if drivers.empty?
177
232
 
178
233
  drivers.map { |driver| Request.prepare(statement).tap { |r| driver.submit(r) } }
179
234
  end
@@ -181,14 +236,12 @@ module PgPipeline
181
236
  def await_prepares!(requests)
182
237
  first_error = nil
183
238
  requests.each do |request|
184
- begin
185
- RequestOps.clear_result(request.wait)
186
- rescue StandardError => e
187
- if first_error
188
- e.clear_result! if e.respond_to?(:clear_result!)
189
- else
190
- first_error = e
191
- end
239
+ RequestOps.clear_result(request.wait)
240
+ rescue StandardError => e
241
+ if first_error
242
+ e.clear_result! if e.respond_to?(:clear_result!)
243
+ else
244
+ first_error = e
192
245
  end
193
246
  end
194
247
  raise first_error if first_error
@@ -201,17 +254,17 @@ module PgPipeline
201
254
  @prepared_generation += 1
202
255
  end
203
256
 
204
- def with_pinned
257
+ def with_pinned(&block)
205
258
  ensure_available!
206
259
  raise @pinned_error if @pinned_error
207
- raise Error, "pinned pool is disabled (pinned_size=0)" if @pinned_size.zero?
260
+ raise Error, 'pinned pool is disabled (pinned_size=0)' if @pinned_size.zero?
208
261
 
209
262
  owner = Fiber.current
210
263
  assert_not_nested_pinned!(owner)
211
264
 
212
265
  @pinned_gate.acquire do
213
266
  assert_pinned_open!
214
- run_pinned(owner) { |conn| yield conn }
267
+ run_pinned(owner, &block)
215
268
  end
216
269
  end
217
270
 
@@ -243,7 +296,7 @@ module PgPipeline
243
296
  return unless @started
244
297
 
245
298
  if @pinned_owners[Fiber.current].positive?
246
- raise Error, "cannot close the pool from inside Client#session/transaction"
299
+ raise Error, 'cannot close the pool from inside Client#session/transaction'
247
300
  end
248
301
 
249
302
  @closing = true
@@ -278,9 +331,7 @@ module PgPipeline
278
331
 
279
332
  begin
280
333
  first_error = preferred_shutdown_error(first_error, stop_supervisor)
281
- if @cancel_pinned_on_abort
282
- first_error = preferred_shutdown_error(first_error, cancel_in_use_pinned)
283
- end
334
+ first_error = preferred_shutdown_error(first_error, cancel_in_use_pinned) if @cancel_pinned_on_abort
284
335
  first_error = preferred_shutdown_error(first_error, close_all_drivers(:abort!))
285
336
  ensure
286
337
  close_free_pinned
@@ -317,7 +368,7 @@ module PgPipeline
317
368
  @supervisor_error = e
318
369
  @pause_failures = (@pause_failures || 0) + 1
319
370
 
320
- unless ENV["PG_PIPELINE_SILENCE_WARNINGS"]
371
+ unless ENV['PG_PIPELINE_SILENCE_WARNINGS']
321
372
  warn("pg_pipeline: supervisor pause failed (#{e.class}: #{e.message}) " \
322
373
  "[#{@pause_failures}/#{MAX_PAUSE_FAILURES}]")
323
374
  end
@@ -331,11 +382,13 @@ module PgPipeline
331
382
  end
332
383
 
333
384
  def supervisor_sleep_interval
385
+ reconnect_interval = @reconnect_interval
386
+ health_interval = @health_interval
334
387
  candidates = []
335
- candidates << @reconnect_interval if @reconnect
336
- candidates << @health_interval if @health_check && @health_interval.positive?
337
- interval = candidates.min || @reconnect_interval
338
- interval.positive? ? interval : @reconnect_interval
388
+ candidates << reconnect_interval if @reconnect
389
+ candidates << health_interval if @health_check && health_interval.positive?
390
+ interval = candidates.min || reconnect_interval
391
+ interval.positive? ? interval : reconnect_interval
339
392
  end
340
393
 
341
394
  def reap_and_replace
@@ -343,46 +396,61 @@ module PgPipeline
343
396
  ensure_driver_slots!
344
397
 
345
398
  @drivers.each_index do |index|
346
- driver = @drivers[index]
347
- next if driver.available?
348
- next unless driver.dead?
349
- next if now < (@driver_backoff[index] || 0.0)
399
+ replace_unavailable_driver(index, now)
400
+ end
401
+ end
350
402
 
351
- begin
352
- @drivers[index] = start_pipeline_driver
353
- @driver_backoff[index] = 0.0
354
- @driver_attempts[index] = 0
355
- @driver_last_health[index] = monotonic
356
- @reconnects += 1
357
- rescue StandardError
358
- @driver_attempts[index] = (@driver_attempts[index] || 0) + 1
359
- @driver_backoff[index] = now + next_backoff(@driver_attempts[index])
360
- end
403
+ def replace_unavailable_driver(index, now)
404
+ driver = @drivers[index]
405
+ return if driver.available? || !driver.dead? || now < (@driver_backoff[index] || 0.0)
406
+
407
+ begin
408
+ @drivers[index] = start_pipeline_driver
409
+ reset_driver_retry_state(index)
410
+ rescue StandardError
411
+ backoff_driver_retry(index, now)
361
412
  end
362
413
  end
363
414
 
415
+ def reset_driver_retry_state(index)
416
+ @driver_backoff[index] = 0.0
417
+ @driver_attempts[index] = 0
418
+ @driver_last_health[index] = monotonic
419
+ @reconnects += 1
420
+ end
421
+
422
+ def backoff_driver_retry(index, now)
423
+ attempts = (@driver_attempts[index] || 0) + 1
424
+ @driver_attempts[index] = attempts
425
+ @driver_backoff[index] = now + next_backoff(attempts)
426
+ end
427
+
364
428
  def health_probe
365
429
  now = monotonic
366
430
  ensure_driver_slots!
367
431
 
368
432
  @drivers.each_index do |index|
369
- driver = @drivers[index]
370
- next unless driver.available?
371
- next unless driver.load.zero?
372
- next if now - (@driver_last_health[index] || 0.0) < @health_interval
433
+ probe_driver_health(index, now)
434
+ end
435
+ end
373
436
 
374
- @driver_last_health[index] = now
437
+ def probe_driver_health(index, now)
438
+ driver = @drivers[index]
439
+ return unless driver.available? && driver.load.zero?
440
+ return if now - (@driver_last_health[index] || 0.0) < @health_interval
375
441
 
376
- begin
377
- next if driver.health_check(@health_timeout)
442
+ @driver_last_health[index] = now
443
+ run_driver_health_check(driver)
444
+ end
378
445
 
379
- @health_failures += 1
380
- driver.abort!
381
- rescue StandardError => e
382
- @health_failures += 1
383
- @supervisor_error = e
384
- end
385
- end
446
+ def run_driver_health_check(driver)
447
+ return if driver.health_check(@health_timeout)
448
+
449
+ @health_failures += 1
450
+ driver.abort!
451
+ rescue StandardError => e
452
+ @health_failures += 1
453
+ @supervisor_error = e
386
454
  end
387
455
 
388
456
  def ensure_driver_slots!
@@ -395,13 +463,14 @@ module PgPipeline
395
463
  def next_backoff(attempts)
396
464
  exponent = [Integer(attempts) - 1, 0].max
397
465
  delay = @reconnect_interval * (2.0**exponent)
398
- delay.finite? && delay < @reconnect_backoff_max ? delay : @reconnect_backoff_max
466
+ maximum = @reconnect_backoff_max
467
+ delay.finite? && delay < maximum ? delay : maximum
399
468
  end
400
469
 
401
470
  def cancel_in_use_pinned
402
471
  cancellation = nil
403
472
 
404
- @pinned_in_use.keys.each do |conn|
473
+ @pinned_in_use.each_key do |conn|
405
474
  conn.cancel if conn.respond_to?(:cancel)
406
475
  rescue CANCEL_SIGNAL => e
407
476
  cancellation ||= e
@@ -422,7 +491,7 @@ module PgPipeline
422
491
  begin
423
492
  supervisor.wait(SUPERVISOR_JOIN_TIMEOUT)
424
493
  rescue Runtime::TimeoutError
425
- unless ENV["PG_PIPELINE_SILENCE_WARNINGS"]
494
+ unless ENV['PG_PIPELINE_SILENCE_WARNINGS']
426
495
  warn("pg_pipeline: supervisor did not exit within #{SUPERVISOR_JOIN_TIMEOUT}s and has been leaked")
427
496
  end
428
497
  rescue CANCEL_SIGNAL, StandardError
@@ -439,13 +508,11 @@ module PgPipeline
439
508
  cancellation = nil
440
509
 
441
510
  @drivers.each do |driver|
442
- begin
443
- driver.public_send(method_name)
444
- rescue CANCEL_SIGNAL => e
445
- cancellation ||= e
446
- rescue StandardError => e
447
- first_error ||= e
448
- end
511
+ driver.public_send(method_name)
512
+ rescue CANCEL_SIGNAL => e
513
+ cancellation ||= e
514
+ rescue StandardError => e
515
+ first_error ||= e
449
516
  end
450
517
 
451
518
  cancellation || first_error
@@ -459,14 +526,15 @@ module PgPipeline
459
526
  end
460
527
 
461
528
  def ensure_available!
462
- raise ShutdownError, "pool is closing or closed" if @closing || @closed
463
- raise Error, "pool not started" unless @started
529
+ raise ShutdownError, 'pool is closing or closed' if @closing || @closed
530
+ raise Error, 'pool not started' unless @started
464
531
  end
465
532
 
466
533
  def start_pipeline_driver
467
534
  conn = PoolOps.new_connection(@connection_args)
468
535
  prepare_registered_statements(conn)
469
- driver = ConnectionDriver.new(conn, max_pending: @max_pending, max_in_flight: @max_in_flight, type_maps: @type_maps)
536
+ driver = ConnectionDriver.new(conn, max_pending: @max_pending, max_in_flight: @max_in_flight,
537
+ type_maps: @type_maps)
470
538
  driver.start
471
539
  rescue Exception
472
540
  PoolOps.safe_close(conn) if conn
@@ -479,35 +547,42 @@ module PgPipeline
479
547
  loop do
480
548
  generation = @prepared_generation || 0
481
549
  statements = (@prepared_statements || {}).values.dup
482
-
483
- statements.each do |statement|
484
- next if prepared.key?(statement.physical_name)
485
-
486
- result = if statement.param_types.nil?
487
- conn.prepare(statement.physical_name, statement.sql)
488
- else
489
- conn.prepare(statement.physical_name, statement.sql, statement.param_types)
490
- end
491
-
492
- RequestOps.clear_result(result)
493
- prepared[statement.physical_name] = true
494
- end
550
+ prepare_statements(conn, statements, prepared)
495
551
 
496
552
  break if generation == (@prepared_generation || 0)
497
553
  end
498
554
  end
499
555
 
556
+ def prepare_statements(conn, statements, prepared)
557
+ statements.each do |statement|
558
+ prepare_statement_on_connection(conn, statement, prepared) unless prepared.key?(statement.physical_name)
559
+ end
560
+ end
561
+
562
+ def prepare_statement_on_connection(conn, statement, prepared)
563
+ param_types = statement.param_types
564
+ name = statement.physical_name
565
+ sql = statement.sql
566
+ result = if param_types.nil?
567
+ conn.prepare(name, sql)
568
+ else
569
+ conn.prepare(name, sql, param_types)
570
+ end
571
+ RequestOps.clear_result(result)
572
+ prepared[name] = true
573
+ end
574
+
500
575
  def assert_not_nested_pinned!(owner)
501
576
  return unless @pinned_owners[owner].positive?
502
577
 
503
578
  raise RecursiveCheckoutError,
504
- "nested Client#session/transaction on the same fiber is not allowed; " \
505
- "use Transaction#savepoint for nested atomicity"
579
+ 'nested Client#session/transaction on the same fiber is not allowed; ' \
580
+ 'use Transaction#savepoint for nested atomicity'
506
581
  end
507
582
 
508
583
  def assert_pinned_open!
509
584
  raise @pinned_error if @pinned_error
510
- raise ShutdownError, "pool is closing" if @closing
585
+ raise ShutdownError, 'pool is closing' if @closing
511
586
  end
512
587
 
513
588
  def run_pinned(owner)
@@ -534,8 +609,9 @@ module PgPipeline
534
609
  return_pinned_connection(conn) if conn
535
610
  ensure
536
611
  @pinned_active -= 1
537
- @pinned_owners[owner] -= 1
538
- @pinned_owners.delete(owner) if @pinned_owners[owner].zero?
612
+ owner_count = @pinned_owners[owner] - 1
613
+ @pinned_owners[owner] = owner_count
614
+ @pinned_owners.delete(owner) if owner_count.zero?
539
615
  @pinned_idle.signal if @pinned_active.zero?
540
616
  end
541
617
 
@@ -556,30 +632,30 @@ module PgPipeline
556
632
  end
557
633
 
558
634
  def recycle_pinned_connection(conn)
559
- if conn.server_version < DISCARD_SEQUENCES_SERVER_VERSION
560
- PoolOps.safe_close(conn)
561
- return PoolOps.new_connection(@connection_args)
562
- end
635
+ return replace_pinned_connection(conn) if conn.server_version < DISCARD_SEQUENCES_SERVER_VERSION
563
636
 
564
637
  PoolOps.sanitize_pinned_connection(conn)
565
638
  conn
566
639
  rescue CANCEL_SIGNAL
567
640
  PoolOps.safe_close(conn)
568
641
  raise
569
- rescue StandardError => cleanup_error
570
- PoolOps.safe_close(conn)
571
-
642
+ rescue StandardError => e
572
643
  begin
573
- PoolOps.new_connection(@connection_args)
644
+ replace_pinned_connection(conn)
574
645
  rescue StandardError => replacement_error
575
646
  @pinned_error = ConnectionLostError.new(
576
- "pinned connection reset failed (#{cleanup_error.class}: #{cleanup_error.message}) " \
647
+ "pinned connection reset failed (#{e.class}: #{e.message}) " \
577
648
  "and replacement failed (#{replacement_error.class}: #{replacement_error.message})"
578
649
  )
579
650
  nil
580
651
  end
581
652
  end
582
653
 
654
+ def replace_pinned_connection(conn)
655
+ PoolOps.safe_close(conn)
656
+ PoolOps.new_connection(@connection_args)
657
+ end
658
+
583
659
  def wait_for_pinned_idle
584
660
  @pinned_idle.wait while @pinned_active.positive?
585
661
  end
@@ -613,14 +689,7 @@ module PgPipeline
613
689
  module PoolOps
614
690
  module_function
615
691
 
616
- def positive_integer!(value, name)
617
- integer = Integer(value)
618
- raise ArgumentError, "#{name} must be >= 1" if integer < 1
619
-
620
- integer
621
- rescue ArgumentError, TypeError
622
- raise ArgumentError, "#{name} must be an integer >= 1"
623
- end
692
+ def positive_integer!(value, name) = DriverOps.positive_integer!(value, name)
624
693
 
625
694
  def finite_float!(value, name, allow_zero: false)
626
695
  number = Float(value)
@@ -629,7 +698,7 @@ module PgPipeline
629
698
 
630
699
  number
631
700
  rescue ArgumentError, TypeError
632
- requirement = allow_zero ? "non-negative finite" : "positive finite"
701
+ requirement = allow_zero ? 'non-negative finite' : 'positive finite'
633
702
  raise ArgumentError, "#{name} must be a #{requirement} number (got #{value.inspect})"
634
703
  end
635
704
 
@@ -642,16 +711,18 @@ module PgPipeline
642
711
  raise ArgumentError, "#{name} must be an integer >= 0"
643
712
  end
644
713
 
645
- def select_driver_into(drivers, rr, slot)
714
+ def select_driver_into(drivers, round_robin, slot)
646
715
  size = drivers.length
647
716
  if size.zero?
648
- slot[0] = rr
717
+ slot[0] = round_robin
649
718
  return nil
650
719
  end
651
720
 
652
- start = rr % size
721
+ start = round_robin % size
653
722
  best = nil
654
- best_index, best_load, offset = 0, 0, 0
723
+ best_index = 0
724
+ best_load = 0
725
+ offset = 0
655
726
 
656
727
  while offset < size
657
728
  index = start + offset
@@ -668,13 +739,13 @@ module PgPipeline
668
739
  best_load = load
669
740
  end
670
741
 
671
- slot[0] = best ? (best_index + 1) % size : rr
742
+ slot[0] = best ? (best_index + 1) % size : round_robin
672
743
  best
673
744
  end
674
745
 
675
- def select_driver(drivers, rr)
676
- slot = [rr]
677
- driver = select_driver_into(drivers, rr, slot)
746
+ def select_driver(drivers, round_robin)
747
+ slot = [round_robin]
748
+ driver = select_driver_into(drivers, round_robin, slot)
678
749
  [driver, slot[0]]
679
750
  end
680
751
 
@@ -683,20 +754,21 @@ module PgPipeline
683
754
  end
684
755
 
685
756
  def sanitize_pinned_connection(conn)
686
- raise ConnectionLostError, "pinned connection is closed" if conn.finished?
687
- raise ConnectionLostError, "pinned connection is bad" unless conn.status == PG::CONNECTION_OK
757
+ raise ConnectionLostError, 'pinned connection is closed' if conn.finished?
758
+ raise ConnectionLostError, 'pinned connection is bad' unless conn.status == PG::CONNECTION_OK
688
759
 
689
- case conn.transaction_status
760
+ transaction_status = conn.transaction_status
761
+ case transaction_status
690
762
  when PG::PQTRANS_IDLE
691
763
  nil
692
764
  when PG::PQTRANS_INTRANS, PG::PQTRANS_INERROR
693
- conn.exec("ROLLBACK")
765
+ conn.exec('ROLLBACK')
694
766
  else
695
767
  raise ConnectionLostError,
696
- "pinned connection returned in unsafe transaction state #{conn.transaction_status}"
768
+ "pinned connection returned in unsafe transaction state #{transaction_status}"
697
769
  end
698
770
 
699
- conn.exec("DISCARD ALL")
771
+ conn.exec('DISCARD ALL')
700
772
  true
701
773
  end
702
774
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "errors"
4
- require_relative "request"
3
+ require_relative 'errors'
4
+ require_relative 'request'
5
5
 
6
6
  module PgPipeline
7
7
  class PreparedStatement
@@ -23,7 +23,7 @@ module PgPipeline
23
23
  alias call query
24
24
 
25
25
  def inspect
26
- flag = @typed ? " typed" : ""
26
+ flag = @typed ? ' typed' : ''
27
27
  "#<#{self.class} name=#{@name.inspect} sql=#{@sql.inspect}#{flag}>"
28
28
  end
29
29
 
@@ -41,8 +41,8 @@ module PgPipeline
41
41
 
42
42
  def snapshot_name(name)
43
43
  value = name.to_s
44
- raise ArgumentError, "prepared statement name must not be empty" if value.empty?
45
- raise ArgumentError, "prepared statement name must not contain NUL" if value.include?("\0")
44
+ raise ArgumentError, 'prepared statement name must not be empty' if value.empty?
45
+ raise ArgumentError, 'prepared statement name must not contain NUL' if value.include?("\0")
46
46
 
47
47
  value.frozen? ? value : value.dup.freeze
48
48
  end