pg_pipeline 0.2.5 → 0.3.1

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,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "pg"
4
- require "async"
5
- require "async/queue"
6
4
 
7
5
  require_relative "errors"
6
+ require_relative "runtime"
8
7
  require_relative "bounded_queue"
9
8
  require_relative "server_caps"
10
9
  require_relative "request"
@@ -21,7 +20,7 @@ module PgPipeline
21
20
  :owner_task, :reader_task, :writer_task
22
21
 
23
22
  attr_writer :readable_events, :results_read, :units_completed, :flush_calls,
24
- :flush_incomplete, :dispatches
23
+ :flush_incomplete, :dispatches, :leaked_watchers
25
24
 
26
25
  def readable_events = @readable_events || 0
27
26
  def results_read = @results_read || 0
@@ -29,6 +28,7 @@ module PgPipeline
29
28
  def flush_calls = @flush_calls || 0
30
29
  def flush_incomplete = @flush_incomplete || 0
31
30
  def dispatches = @dispatches || 0
31
+ def leaked_watchers = @leaked_watchers || 0
32
32
 
33
33
  def initialize(conn, max_pending: DEFAULT_MAX_PENDING, max_in_flight: DEFAULT_MAX_IN_FLIGHT)
34
34
  @max_pending = DriverOps.positive_integer!(max_pending, :max_pending)
@@ -38,37 +38,19 @@ module PgPipeline
38
38
  @caps = ServerCaps.from_connection(conn)
39
39
  @caps.assert_supported!
40
40
  DriverOps.warn_flush_coupling_once(@caps)
41
-
42
41
  @requests = BoundedQueue.new(@max_pending)
43
- @events = Async::Queue.new
44
- @reader_rearm = Async::Queue.new
45
- @writer_commands = Async::Queue.new
46
-
42
+ @events = Runtime::Queue.new
43
+ @reader_rearm = Runtime::Queue.new
44
+ @writer_commands = Runtime::Queue.new
45
+
46
+ @accepting = @running = @draining = @needs_flush = @writer_armed = @request_event_pending = false
47
+ @readable_events = @results_read = @units_completed = @flush_calls =
48
+ @flush_incomplete = @dispatches = @leaked_watchers = @submitting = 0
49
+ @socket = @owner_task = @reader_task = @writer_task = @dispatching = nil
47
50
  @inflight = []
48
- @dispatching = nil
49
- @submitting = 0
50
-
51
- @accepting = false
52
- @running = false
53
- @draining = false
54
- @needs_flush = false
55
- @writer_armed = false
56
- @request_event_pending = false
57
-
58
- @readable_events = 0
59
- @results_read = 0
60
- @units_completed = 0
61
- @flush_calls = 0
62
- @flush_incomplete = 0
63
- @dispatches = 0
64
-
65
- @socket = nil
66
- @owner_task = nil
67
- @reader_task = nil
68
- @writer_task = nil
69
- end
70
-
71
- def start(parent: Async::Task.current) = DriverOps.start(self, parent)
51
+ end
52
+
53
+ def start = DriverOps.start(self)
72
54
  def submit(request) = DriverOps.submit(self, request)
73
55
  def load = @requests.size + @inflight.size + @submitting + (@dispatching ? 1 : 0)
74
56
  def available? = @accepting && @running
@@ -91,7 +73,8 @@ module PgPipeline
91
73
  dispatches: dispatches,
92
74
  units_per_readable: DriverOps.ratio(units_completed, readable_events),
93
75
  results_per_readable: DriverOps.ratio(results_read, readable_events),
94
- flush_calls_per_unit: DriverOps.ratio(flush_calls, units_completed)
76
+ flush_calls_per_unit: DriverOps.ratio(flush_calls, units_completed),
77
+ leaked_watchers: leaked_watchers
95
78
  }
96
79
  end
97
80
 
@@ -99,21 +82,19 @@ module PgPipeline
99
82
  return true unless available?
100
83
 
101
84
  probe = Request.build("SELECT 1", nil)
102
- begin
103
- submit(probe)
104
- Async::Task.current.with_timeout(timeout) { probe.wait }
105
- true
106
- rescue Async::TimeoutError
107
- DriverOps.abort_timed_out_health_probe(self, probe)
108
- rescue QueryError, PipelineAbortedError
109
- true
110
- rescue ShutdownError, NotDispatchedError
111
- true
112
- rescue ConnectionLostError, PG::Error
113
- false
114
- ensure
115
- probe.cancel! unless probe.settled?
116
- end
85
+ submit(probe)
86
+ Runtime.with_timeout(timeout) { probe.wait }
87
+ true
88
+ rescue Runtime::TimeoutError
89
+ DriverOps.abort_timed_out_health_probe(self, probe)
90
+ rescue QueryError, PipelineAbortedError
91
+ true
92
+ rescue ShutdownError, NotDispatchedError
93
+ true
94
+ rescue ConnectionLostError, PG::Error
95
+ false
96
+ ensure
97
+ probe.cancel! if probe && !probe.settled?
117
98
  end
118
99
 
119
100
  def graceful_close = DriverOps.graceful_close(self)
@@ -124,6 +105,10 @@ module PgPipeline
124
105
  end
125
106
 
126
107
  module DriverOps
108
+ WATCHER_JOIN_TIMEOUT = 2.0
109
+ OWNER_JOIN_TIMEOUT = 5.0
110
+ WATCHER_POLL_INTERVAL = 0.25
111
+
127
112
  module_function
128
113
 
129
114
  def ratio(numerator, denominator)
@@ -155,25 +140,25 @@ module PgPipeline
155
140
  raise ArgumentError, "#{name} must be an integer >= 1"
156
141
  end
157
142
 
158
- def start(d, parent)
143
+ def start(d)
159
144
  raise Error, "driver already started" if d.running
145
+ raise Error, "driver start requires an active Fiber scheduler" unless Fiber.scheduler
160
146
 
161
147
  d.conn.setnonblocking(true)
162
148
  d.conn.enter_pipeline_mode
163
-
164
149
  d.socket = d.conn.socket_io
165
150
  d.accepting = true
166
151
  d.running = true
167
152
 
168
- d.reader_task = parent.async { reader_watcher(d) }
169
- d.writer_task = parent.async { writer_watcher(d) }
170
- d.owner_task = parent.async { owner_loop(d) }
153
+ d.reader_task = Runtime.spawn(name: :reader) { reader_watcher(d) }
154
+ d.writer_task = Runtime.spawn(name: :writer) { writer_watcher(d) }
155
+ d.owner_task = Runtime.spawn(name: :owner) { owner_loop(d) }
171
156
  d
172
157
  rescue Exception
173
158
  d.accepting = false
174
159
  d.running = false
175
- stop_watchers(d)
176
- safe_close_conn(d)
160
+ teardown_watchers(d)
161
+ join_owner(d)
177
162
  raise
178
163
  end
179
164
 
@@ -200,7 +185,7 @@ module PgPipeline
200
185
  d.accepting = false
201
186
  d.requests.close(ShutdownError.new("driver is closing"))
202
187
  d.events.enqueue(:close)
203
- d.owner_task.wait
188
+ join_owner(d)
204
189
  nil
205
190
  end
206
191
 
@@ -210,7 +195,7 @@ module PgPipeline
210
195
  d.accepting = false
211
196
  d.requests.close(not_dispatched_error(error))
212
197
  d.events.enqueue([:abort, error])
213
- d.owner_task.wait unless Async::Task.current.equal?(d.owner_task)
198
+ join_owner(d)
214
199
  nil
215
200
  end
216
201
 
@@ -232,7 +217,12 @@ module PgPipeline
232
217
  end
233
218
 
234
219
  def owner_loop(d)
235
- process_event(d, d.events.dequeue) while d.running
220
+ while d.running
221
+ event = d.events.dequeue
222
+ break if event.nil?
223
+
224
+ process_event(d, event)
225
+ end
236
226
  rescue StandardError => e
237
227
  fatal_close(d, ConnectionLostError.new("driver crashed: #{e.class}: #{e.message}"))
238
228
  ensure
@@ -241,7 +231,6 @@ module PgPipeline
241
231
 
242
232
  def process_event(d, event)
243
233
  input_changed = false
244
-
245
234
  case event
246
235
  when :requests
247
236
  d.request_event_pending = false
@@ -275,6 +264,7 @@ module PgPipeline
275
264
 
276
265
  def notify_requests(d)
277
266
  return if d.request_event_pending
267
+ return unless d.running
278
268
 
279
269
  d.request_event_pending = true
280
270
  d.events.enqueue(:requests)
@@ -282,7 +272,6 @@ module PgPipeline
282
272
 
283
273
  def pump_requests(d)
284
274
  dispatched = false
285
-
286
275
  while d.inflight.size < d.max_in_flight && !d.requests.empty?
287
276
  request = d.requests.dequeue
288
277
  break unless request
@@ -363,7 +352,6 @@ module PgPipeline
363
352
  return unless d.running
364
353
 
365
354
  d.flush_calls += 1
366
-
367
355
  if d.conn.sync_flush
368
356
  d.needs_flush = false
369
357
  else
@@ -377,6 +365,7 @@ module PgPipeline
377
365
 
378
366
  def arm_writer(d)
379
367
  return if d.writer_armed
368
+ return unless d.running
380
369
 
381
370
  d.writer_armed = true
382
371
  d.writer_commands.enqueue(:wait_writable)
@@ -404,7 +393,6 @@ module PgPipeline
404
393
  end
405
394
 
406
395
  status = result.result_status
407
-
408
396
  case status
409
397
  when PG::PGRES_TUPLES_OK
410
398
  ensure_before_query_boundary!(request, status)
@@ -465,15 +453,11 @@ module PgPipeline
465
453
  def finish_graceful_close(d)
466
454
  d.accepting = false
467
455
  d.running = false
468
-
469
- begin
470
- d.conn.exit_pipeline_mode
471
- rescue PG::Error => e
472
- fail_all(d, ConnectionLostError.new("failed to exit pipeline mode: #{e.message}"))
473
- ensure
474
- stop_watchers(d)
475
- safe_close_conn(d)
476
- end
456
+ d.conn.exit_pipeline_mode
457
+ rescue PG::Error => e
458
+ fail_all(d, ConnectionLostError.new("failed to exit pipeline mode: #{e.message}"))
459
+ ensure
460
+ teardown_watchers(d)
477
461
  end
478
462
 
479
463
  def fatal_close(d, error)
@@ -483,8 +467,8 @@ module PgPipeline
483
467
  d.running = false
484
468
  d.requests.close(not_dispatched_error(error))
485
469
  fail_all(d, error)
486
- stop_watchers(d)
487
- safe_close_conn(d)
470
+
471
+ teardown_watchers(d)
488
472
  end
489
473
 
490
474
  def fail_all(d, error)
@@ -520,10 +504,32 @@ module PgPipeline
520
504
  )
521
505
  end
522
506
 
507
+ def watcher_wait_timeout
508
+ scheduler = Fiber.scheduler
509
+ return nil if scheduler&.respond_to?(:fiber_interrupt)
510
+
511
+ WATCHER_POLL_INTERVAL
512
+ end
513
+
514
+ def wait_socket_readable(d, timeout)
515
+ loop do
516
+ return false unless d.running
517
+ return true if d.socket.wait_readable(timeout)
518
+ end
519
+ end
520
+
521
+ def wait_socket_writable(d, timeout)
522
+ loop do
523
+ return false unless d.running
524
+ return true if d.socket.wait_writable(timeout)
525
+ end
526
+ end
527
+
523
528
  def reader_watcher(d)
529
+ timeout = watcher_wait_timeout
530
+
524
531
  while d.running
525
- d.socket.wait_readable
526
- break unless d.running
532
+ break unless wait_socket_readable(d, timeout)
527
533
 
528
534
  d.events.enqueue(:readable)
529
535
  command = d.reader_rearm.dequeue
@@ -536,11 +542,13 @@ module PgPipeline
536
542
  end
537
543
 
538
544
  def writer_watcher(d)
545
+ timeout = watcher_wait_timeout
546
+
539
547
  while d.running
540
548
  command = d.writer_commands.dequeue
541
549
  break unless command == :wait_writable && d.running
550
+ break unless wait_socket_writable(d, timeout)
542
551
 
543
- d.socket.wait_writable
544
552
  d.events.enqueue(:writable) if d.running
545
553
  end
546
554
  rescue StandardError => e
@@ -549,21 +557,87 @@ module PgPipeline
549
557
  end
550
558
  end
551
559
 
552
- def stop_watchers(d)
553
- reader = d.reader_task
554
- writer = d.writer_task
560
+ def teardown_watchers(d)
561
+ tasks = release_watchers(d)
562
+ close_wait_points(d)
563
+ begin
564
+ d.socket&.close
565
+ rescue StandardError
566
+ nil
567
+ end
568
+ stop_watchers(tasks)
569
+ join_watchers(d, tasks)
570
+ d.socket = nil
571
+ safe_close_conn(d)
572
+ nil
573
+ end
574
+
575
+ def release_watchers(d)
576
+ tasks = [d.reader_task, d.writer_task].compact
555
577
  d.reader_task = nil
556
578
  d.writer_task = nil
579
+ tasks
580
+ end
581
+
582
+ def close_wait_points(d)
583
+ [d.reader_rearm, d.writer_commands, d.events].each do |queue|
584
+ queue&.close
585
+ rescue StandardError
586
+ nil
587
+ end
588
+ end
589
+
590
+ def stop_watchers(tasks)
591
+ Array(tasks).each do |task|
592
+ task.stop
593
+ rescue Runtime::Cancel, StandardError
594
+ nil
595
+ end
596
+
597
+ nil
598
+ end
557
599
 
558
- [reader, writer].each do |task|
559
- task&.stop
560
- rescue Async::Cancel, StandardError
600
+ def join_watchers(d, tasks)
601
+ Array(tasks).each do |task|
602
+ task.wait(WATCHER_JOIN_TIMEOUT)
603
+ rescue Runtime::TimeoutError
604
+ d.leaked_watchers += 1
605
+ warn_leaked_task(d, task, WATCHER_JOIN_TIMEOUT)
606
+ rescue Runtime::Cancel, StandardError
561
607
  nil
562
608
  end
563
609
 
564
610
  nil
565
611
  end
566
612
 
613
+ def join_owner(d)
614
+ owner = d.owner_task
615
+ return if owner.nil?
616
+ return if Fiber.current.equal?(owner.fiber)
617
+
618
+ begin
619
+ owner.wait(OWNER_JOIN_TIMEOUT)
620
+ rescue Runtime::TimeoutError
621
+ d.leaked_watchers += 1
622
+ warn_leaked_task(d, owner, OWNER_JOIN_TIMEOUT)
623
+ rescue Runtime::Cancel, StandardError
624
+ nil
625
+ end
626
+
627
+ nil
628
+ end
629
+
630
+ def warn_leaked_task(d, task, timeout)
631
+ return if ENV["PG_PIPELINE_SILENCE_WARNINGS"]
632
+
633
+ warn(
634
+ "pg_pipeline: task #{task.name.inspect} did not exit within " \
635
+ "#{timeout}s and has been leaked (total #{d.leaked_watchers}). " \
636
+ "Closing the socket and, on schedulers without #fiber_interrupt, the " \
637
+ "#{WATCHER_POLL_INTERVAL}s watcher poll did not release the fiber in time."
638
+ )
639
+ end
640
+
567
641
  def safe_close_conn(d)
568
642
  d.conn.close unless d.conn.finished?
569
643
  rescue StandardError
@@ -8,6 +8,7 @@ module PgPipeline
8
8
  class ConnectionLostError < Error; end
9
9
  class NotDispatchedError < ConnectionLostError; end
10
10
  class IndeterminateResultError < ConnectionLostError; end
11
+ class IndeterminateCommitError < IndeterminateResultError; end
11
12
  class ShutdownError < Error; end
12
13
  class ProtocolError < Error; end
13
14
  class RecursiveCheckoutError < Error; end
@@ -1,11 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "pg"
4
- require "async"
5
- require "async/semaphore"
6
- require "async/notification"
7
4
 
8
5
  require_relative "errors"
6
+ require_relative "runtime"
9
7
  require_relative "connection_driver"
10
8
  require_relative "prepared_statement"
11
9
  require_relative "server_caps"
@@ -19,7 +17,9 @@ module PgPipeline
19
17
  DEFAULT_HEALTH_INTERVAL = 10.0
20
18
  DEFAULT_HEALTH_TIMEOUT = 5.0
21
19
  DISCARD_SEQUENCES_SERVER_VERSION = 90_400
22
- CANCEL_SIGNAL = Async::Cancel
20
+ CANCEL_SIGNAL = Runtime::Cancel
21
+ SUPERVISOR_JOIN_TIMEOUT = 5.0
22
+ MAX_PAUSE_FAILURES = 3
23
23
 
24
24
  attr_reader :reconnects, :pipeline_size
25
25
 
@@ -58,7 +58,8 @@ module PgPipeline
58
58
  @health_failures = 0
59
59
  @supervisor_error = nil
60
60
  @supervisor = nil
61
- @task_parent = nil
61
+ @supervisor_wake = Runtime::Notification.new
62
+ @pause_failures = 0
62
63
 
63
64
  @prepared_statements = {}
64
65
  @prepared_generation = 0
@@ -70,30 +71,28 @@ module PgPipeline
70
71
  @pinned_error = nil
71
72
  @pinned_active = 0
72
73
  @pinned_owners = Hash.new(0)
73
- @pinned_idle = Async::Notification.new
74
+ @pinned_idle = Runtime::Notification.new
74
75
 
75
76
  @started = false
76
77
  @closing = false
77
78
  @closed = false
78
79
  end
79
80
 
80
- def start(parent: nil)
81
+ def start
81
82
  raise Error, "pool already started" if @started
82
83
  if @closing || @closed
83
84
  raise ShutdownError, "pool is closing or was closed and cannot be restarted; create a new Pool"
84
85
  end
85
-
86
- parent ||= Async::Task.current
87
- @task_parent = parent
86
+ raise Error, "pool start requires an active Fiber scheduler" unless Fiber.scheduler
88
87
 
89
88
  begin
90
- @pipeline_size.times { @drivers << start_pipeline_driver(parent) }
89
+ @pipeline_size.times { @drivers << start_pipeline_driver }
91
90
  @driver_backoff = Array.new(@drivers.size, 0.0)
92
91
  @driver_attempts = Array.new(@drivers.size, 0)
93
92
  @driver_last_health = Array.new(@drivers.size, monotonic)
94
- @pinned_gate = Async::Semaphore.new(@pinned_size) if @pinned_size.positive?
93
+ @pinned_gate = Runtime::Semaphore.new(@pinned_size) if @pinned_size.positive?
95
94
  @started = true
96
- @supervisor = parent.async { supervise } if @reconnect || @health_check
95
+ @supervisor = Runtime.spawn(name: :supervisor) { supervise } if @reconnect || @health_check
97
96
  rescue Exception
98
97
  cleanup_partial_start
99
98
  raise
@@ -174,9 +173,10 @@ module PgPipeline
174
173
  raise @pinned_error if @pinned_error
175
174
  raise Error, "pinned pool is disabled (pinned_size=0)" if @pinned_size.zero?
176
175
 
177
- if @pinned_owners[Async::Task.current].positive?
176
+ owner = Fiber.current
177
+ if @pinned_owners[owner].positive?
178
178
  raise RecursiveCheckoutError,
179
- "nested Client#session/transaction on the same task is not allowed; " \
179
+ "nested Client#session/transaction on the same fiber is not allowed; " \
180
180
  "use Transaction#savepoint for nested atomicity"
181
181
  end
182
182
 
@@ -184,7 +184,6 @@ module PgPipeline
184
184
  raise @pinned_error if @pinned_error
185
185
  raise ShutdownError, "pool is closing" if @closing
186
186
 
187
- owner = Async::Task.current
188
187
  conn = nil
189
188
  @pinned_active += 1
190
189
  @pinned_owners[owner] += 1
@@ -220,6 +219,7 @@ module PgPipeline
220
219
  reconnects: @reconnects,
221
220
  health_failures: @health_failures,
222
221
  supervisor_error: @supervisor_error&.message,
222
+ supervisor_alive: supervisor_alive?,
223
223
  closing: @closing,
224
224
  closed: @closed,
225
225
  pinned_error: @pinned_error&.message
@@ -229,7 +229,7 @@ module PgPipeline
229
229
  def graceful_close
230
230
  return unless @started
231
231
 
232
- if @pinned_owners[Async::Task.current].positive?
232
+ if @pinned_owners[Fiber.current].positive?
233
233
  raise Error, "cannot close the pool from inside Client#session/transaction"
234
234
  end
235
235
 
@@ -284,7 +284,7 @@ module PgPipeline
284
284
  def supervise
285
285
  until @closing
286
286
  begin
287
- reap_and_replace(@task_parent) if @reconnect
287
+ reap_and_replace if @reconnect
288
288
  health_probe if @health_check
289
289
  @supervisor_error = nil
290
290
  rescue StandardError => e
@@ -292,9 +292,29 @@ module PgPipeline
292
292
  end
293
293
 
294
294
  break if @closing
295
+ break unless supervisor_pause
296
+ end
297
+ end
295
298
 
296
- sleep(supervisor_sleep_interval)
299
+ def supervisor_pause
300
+ @supervisor_wake.wait(supervisor_sleep_interval)
301
+ @pause_failures = 0
302
+ true
303
+ rescue StandardError => e
304
+ @supervisor_error = e
305
+ @pause_failures = (@pause_failures || 0) + 1
306
+
307
+ unless ENV["PG_PIPELINE_SILENCE_WARNINGS"]
308
+ warn("pg_pipeline: supervisor pause failed (#{e.class}: #{e.message}) " \
309
+ "[#{@pause_failures}/#{MAX_PAUSE_FAILURES}]")
297
310
  end
311
+
312
+ @pause_failures < MAX_PAUSE_FAILURES
313
+ end
314
+
315
+ def supervisor_alive?
316
+ supervisor = @supervisor
317
+ !supervisor.nil? && !supervisor.finished?
298
318
  end
299
319
 
300
320
  def supervisor_sleep_interval
@@ -305,7 +325,7 @@ module PgPipeline
305
325
  interval.positive? ? interval : @reconnect_interval
306
326
  end
307
327
 
308
- def reap_and_replace(parent)
328
+ def reap_and_replace
309
329
  now = monotonic
310
330
  ensure_driver_slots!
311
331
 
@@ -316,7 +336,7 @@ module PgPipeline
316
336
  next if now < (@driver_backoff[index] || 0.0)
317
337
 
318
338
  begin
319
- @drivers[index] = start_pipeline_driver(parent)
339
+ @drivers[index] = start_pipeline_driver
320
340
  @driver_backoff[index] = 0.0
321
341
  @driver_attempts[index] = 0
322
342
  @driver_last_health[index] = monotonic
@@ -339,10 +359,16 @@ module PgPipeline
339
359
  next if now - (@driver_last_health[index] || 0.0) < @health_interval
340
360
 
341
361
  @driver_last_health[index] = now
342
- next if driver.health_check(@health_timeout)
343
362
 
344
- @health_failures += 1
345
- driver.abort!
363
+ begin
364
+ next if driver.health_check(@health_timeout)
365
+
366
+ @health_failures += 1
367
+ driver.abort!
368
+ rescue StandardError => e
369
+ @health_failures += 1
370
+ @supervisor_error = e
371
+ end
346
372
  end
347
373
  end
348
374
 
@@ -376,10 +402,21 @@ module PgPipeline
376
402
  def stop_supervisor
377
403
  supervisor = @supervisor
378
404
  @supervisor = nil
379
- supervisor&.stop
405
+ return nil unless supervisor
406
+
407
+ @supervisor_wake.signal
408
+
409
+ begin
410
+ supervisor.wait(SUPERVISOR_JOIN_TIMEOUT)
411
+ rescue Runtime::TimeoutError
412
+ unless ENV["PG_PIPELINE_SILENCE_WARNINGS"]
413
+ warn("pg_pipeline: supervisor did not exit within #{SUPERVISOR_JOIN_TIMEOUT}s and has been leaked")
414
+ end
415
+ rescue CANCEL_SIGNAL, StandardError
416
+ nil
417
+ end
418
+
380
419
  nil
381
- rescue CANCEL_SIGNAL => e
382
- e
383
420
  rescue StandardError
384
421
  nil
385
422
  end
@@ -413,11 +450,11 @@ module PgPipeline
413
450
  raise Error, "pool not started" unless @started
414
451
  end
415
452
 
416
- def start_pipeline_driver(parent)
453
+ def start_pipeline_driver
417
454
  conn = PoolOps.new_connection(@connection_args)
418
455
  prepare_registered_statements(conn)
419
456
  driver = ConnectionDriver.new(conn, max_pending: @max_pending, max_in_flight: @max_in_flight)
420
- driver.start(parent: parent)
457
+ driver.start
421
458
  rescue Exception
422
459
  PoolOps.safe_close(conn) if conn
423
460
  raise
@@ -447,28 +484,26 @@ module PgPipeline
447
484
  end
448
485
 
449
486
  def release_pinned(owner, conn)
450
- begin
451
- if conn
452
- if @closing
453
- PoolOps.safe_close(conn)
454
- else
455
- recycled = recycle_pinned_connection(conn)
456
-
457
- if recycled
458
- if @closing
459
- PoolOps.safe_close(recycled)
460
- else
461
- @pinned_free << recycled
462
- end
487
+ if conn
488
+ if @closing
489
+ PoolOps.safe_close(conn)
490
+ else
491
+ recycled = recycle_pinned_connection(conn)
492
+
493
+ if recycled
494
+ if @closing
495
+ PoolOps.safe_close(recycled)
496
+ else
497
+ @pinned_free << recycled
463
498
  end
464
499
  end
465
500
  end
466
- ensure
467
- @pinned_active -= 1
468
- @pinned_owners[owner] -= 1
469
- @pinned_owners.delete(owner) if @pinned_owners[owner].zero?
470
- @pinned_idle.signal if @pinned_active.zero?
471
501
  end
502
+ ensure
503
+ @pinned_active -= 1
504
+ @pinned_owners[owner] -= 1
505
+ @pinned_owners.delete(owner) if @pinned_owners[owner].zero?
506
+ @pinned_idle.signal if @pinned_active.zero?
472
507
  end
473
508
 
474
509
  def recycle_pinned_connection(conn)
@@ -513,7 +548,6 @@ module PgPipeline
513
548
  close_free_pinned
514
549
  @pinned_gate = nil
515
550
  @supervisor = nil
516
- @task_parent = nil
517
551
  @started = false
518
552
  @closing = false
519
553
  @closed = false