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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3bb4583fbf47130e8b86eec8f44c70577f053c9cacd1e9774a09a5b7a5d3999
4
- data.tar.gz: '099f44877fde9db5faa63a776e31ecb6d1b56cc155dd08fd0b6dff52252a719e'
3
+ metadata.gz: c47c7cb9266d0541b6d57aeba3c25f311f1e8ba6b661c2b79ddc0aea6fe5fe24
4
+ data.tar.gz: 8c34eae2688944b938ee4adfe2c07ae2730571536ff328dc41368add95667410
5
5
  SHA512:
6
- metadata.gz: ddc6fa628e441b5cf744a3521e0ca90a923cf732be59662c6cee9df5f522ad69319628d6ee98e18711baa28d8d42eef7585c0af6239cb9a8f4f921212338d014
7
- data.tar.gz: 75e56315d776d60cadf07b675e048fdb674f06bc8b67fe960e95be4d7ed8bd89008eb25a8828eb96655994ed83fa23f392a541a73b77dbc4f113dc28261c815a
6
+ metadata.gz: a869a0dcfb3a2c8f0eaa9f7f4979ebf71f8af893b3792de15a4db6c8a2f9852f9f7a42474580468cdb3b9cac50f08ec0a2525627551fc731d715b70ac1c3d03c
7
+ data.tar.gz: ca9179c01ab0a824b07be2923578b51489d91ccd2e9076ba2da44d789f2aef666cffd77d481b85577c0265f8f7bc0f0b33fe028ba535007928bb7489b21c504c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.4] - 2026-10-28
4
+
5
+ Internal refactoring of the control plane. No intended public API or wire
6
+ behaviour changes.
7
+
8
+ ### Changed
9
+
10
+ - Centralized pool option defaults and validation.
11
+ - Split driver, pool, request, SQL-guard, and type-map operations into smaller
12
+ focused methods and removed redundant repeated lookups.
13
+
14
+ ### Fixed
15
+
16
+ - Readable driver events now trigger result draining.
17
+
3
18
  ## [0.3.3] - 2026-08-19
4
19
 
5
20
  Opt-in typed result decoding for multiplexed prepared statements. Default
@@ -1,17 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "errors"
4
- require_relative "runtime"
3
+ require_relative 'errors'
4
+ require_relative 'runtime'
5
5
 
6
6
  module PgPipeline
7
7
  class BoundedQueue
8
8
  def initialize(limit)
9
9
  @limit = Integer(limit)
10
- raise ArgumentError, "limit must be >= 1" if @limit < 1
10
+ raise ArgumentError, 'limit must be >= 1' if @limit < 1
11
11
  rescue ArgumentError, TypeError
12
- raise ArgumentError, "limit must be an integer >= 1"
12
+ raise ArgumentError, 'limit must be an integer >= 1'
13
13
  else
14
- @items, @consumers, @producers = [], [], []
14
+ @items = []
15
+ @consumers = []
16
+ @producers = []
15
17
 
16
18
  @closed = false
17
19
  @close_error = nil
@@ -93,7 +95,7 @@ module PgPipeline
93
95
  end
94
96
 
95
97
  def raise_close_error
96
- error = @close_error || ShutdownError.new("queue closed")
98
+ error = @close_error || ShutdownError.new('queue closed')
97
99
  raise error.class, error.message
98
100
  end
99
101
  end
@@ -1,19 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "errors"
4
- require_relative "pool"
5
- require_relative "request"
6
- require_relative "prepared_statement"
7
- require_relative "session_guard"
8
- require_relative "session"
9
- require_relative "transaction"
3
+ require_relative 'errors'
4
+ require_relative 'pool'
5
+ require_relative 'request'
6
+ require_relative 'prepared_statement'
7
+ require_relative 'session_guard'
8
+ require_relative 'session'
9
+ require_relative 'transaction'
10
10
 
11
11
  module PgPipeline
12
12
  class Client
13
13
  attr_reader :guard
14
14
 
15
15
  def initialize(connection_args = nil, guard: :default, **pool_opts)
16
- @owner_thread, @scheduler = nil, nil
16
+ @owner_thread = nil
17
+ @scheduler = nil
17
18
 
18
19
  @guard = SessionGuard.normalize_mode!(guard)
19
20
  @pool = Pool.new(connection_args, **pool_opts)
@@ -26,9 +27,11 @@ module PgPipeline
26
27
 
27
28
  def start = ClientOps.start(self)
28
29
  def query(sql, params = RequestOps::EMPTY_PARAMS) = ClientOps.query(self, sql, params)
30
+
29
31
  def prepare(name, sql, param_types = nil, typed: false)
30
32
  ClientOps.prepare(self, name, sql, param_types, typed: typed)
31
33
  end
34
+
32
35
  def stats = ClientOps.stats(self)
33
36
 
34
37
  def session(&block)
@@ -44,9 +47,8 @@ module PgPipeline
44
47
 
45
48
  private
46
49
 
47
- attr_reader :pool, :owner_thread, :scheduler
48
- attr_accessor :started
49
- attr_writer :owner_thread, :scheduler
50
+ attr_accessor :owner_thread, :scheduler, :started
51
+ attr_reader :pool
50
52
  end
51
53
 
52
54
  module ClientOps
@@ -56,16 +58,18 @@ module PgPipeline
56
58
  client = Client.new(connection_args, **opts).start
57
59
  yield client
58
60
  ensure
59
- client.close if client
61
+ client&.close
60
62
  end
61
63
 
62
64
  def start(client)
63
- raise Error, "client already started" if started?(client)
64
- raise Error, "client start requires an active Fiber scheduler" unless Fiber.scheduler
65
+ raise Error, 'client already started' if started?(client)
66
+
67
+ scheduler = Fiber.scheduler
68
+ raise Error, 'client start requires an active Fiber scheduler' unless scheduler
65
69
 
66
70
  pool(client).start
67
71
  client.__send__(:owner_thread=, Thread.current)
68
- client.__send__(:scheduler=, Fiber.scheduler)
72
+ client.__send__(:scheduler=, scheduler)
69
73
  client.__send__(:started=, true)
70
74
  client
71
75
  end
@@ -108,26 +112,38 @@ module PgPipeline
108
112
 
109
113
  def submit_with_failover(client)
110
114
  attempts = 0
111
- limit = [pool(client).pipeline_size, 1].max
115
+ client_pool = pool(client)
116
+ limit = [client_pool.pipeline_size, 1].max
112
117
  last_error = nil
113
118
 
114
119
  while attempts < limit
115
120
  request = yield
116
- begin
117
- pool(client).__send__(:pipeline_driver).submit(request)
118
- return request
119
- rescue NotDispatchedError => e
120
- last_error = e
121
- attempts += 1
122
- rescue ShutdownError => e
123
- last_error = e
124
- attempts += 1
125
- break unless request.state == :new && !request.settled?
126
- end
121
+ last_error = submit_attempt(client_pool, request)
122
+ return request unless last_error
123
+
124
+ attempts += 1
125
+ break if stop_retrying?(request, last_error)
127
126
  end
128
127
 
129
- error = last_error || NotDispatchedError.new("no live pipeline connections; request was not dispatched")
130
- raise(error.is_a?(NotDispatchedError) ? error : NotDispatchedError.new(error.message))
128
+ raise_submission_error(last_error)
129
+ end
130
+
131
+ def submit_attempt(client_pool, request)
132
+ client_pool.__send__(:pipeline_driver).submit(request)
133
+ nil
134
+ rescue NotDispatchedError, ShutdownError => e
135
+ e
136
+ end
137
+
138
+ def stop_retrying?(request, error)
139
+ error.is_a?(ShutdownError) && (request.state != :new || request.settled?)
140
+ end
141
+
142
+ def raise_submission_error(error)
143
+ error ||= NotDispatchedError.new('no live pipeline connections; request was not dispatched')
144
+ raise error if error.is_a?(NotDispatchedError)
145
+
146
+ raise NotDispatchedError, error.message
131
147
  end
132
148
 
133
149
  def session(client)
@@ -143,13 +159,13 @@ module PgPipeline
143
159
  end
144
160
  end
145
161
 
146
- def transaction(client)
162
+ def transaction(client, &block)
147
163
  ensure_started!(client)
148
164
 
149
165
  pool(client).__send__(:with_pinned) do |conn|
150
166
  tx = Transaction.new(conn)
151
167
  begin
152
- TransactionOps.run(tx) { |transaction| yield transaction }
168
+ TransactionOps.run(tx, &block)
153
169
  ensure
154
170
  SessionOps.close!(tx)
155
171
  end
@@ -165,10 +181,11 @@ module PgPipeline
165
181
  return unless started?(client)
166
182
 
167
183
  ensure_context!(client)
184
+ client_pool = pool(client)
168
185
  begin
169
- pool(client).graceful_close
186
+ client_pool.graceful_close
170
187
  ensure
171
- client.__send__(:started=, false) if pool(client).closing?
188
+ client.__send__(:started=, false) if client_pool.closing?
172
189
  end
173
190
  end
174
191
 
@@ -176,10 +193,11 @@ module PgPipeline
176
193
  return unless started?(client)
177
194
 
178
195
  ensure_context!(client)
196
+ client_pool = pool(client)
179
197
  begin
180
- pool(client).abort!
198
+ client_pool.abort!
181
199
  ensure
182
- client.__send__(:started=, false) if pool(client).closing?
200
+ client.__send__(:started=, false) if client_pool.closing?
183
201
  end
184
202
  end
185
203
 
@@ -188,7 +206,7 @@ module PgPipeline
188
206
  end
189
207
 
190
208
  def ensure_started!(client)
191
- raise Error, "client not started; call #start or use Client.open" unless started?(client)
209
+ raise Error, 'client not started; call #start or use Client.open' unless started?(client)
192
210
 
193
211
  ensure_context!(client)
194
212
  end
@@ -197,7 +215,7 @@ module PgPipeline
197
215
  return if Thread.current.equal?(client.__send__(:owner_thread)) &&
198
216
  Fiber.scheduler.equal?(client.__send__(:scheduler))
199
217
 
200
- raise Error, "client is reactor-local and cannot be used from another thread/scheduler"
218
+ raise Error, 'client is reactor-local and cannot be used from another thread/scheduler'
201
219
  end
202
220
 
203
221
  def started?(client)