pgtk 0.31.4 → 0.31.6
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 +4 -4
- data/lib/pgtk/pool.rb +77 -57
- data/lib/pgtk/retry.rb +12 -5
- data/lib/pgtk/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 117996e543551102eebf83be228996818dbfdbfc324f7f45b5d346a57d39fc1a
|
|
4
|
+
data.tar.gz: 22bd0738c88e60ed29ad3310a87755709dc4512d6b92932c88442253c29847f3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ae1185f452400e2f2f984e5266bdb9c6b8dc7d92b798eb714d18903949f230d8707911b562431984f4fa52178dc52198bfeac5b24d2aaeff918ee0b36804541
|
|
7
|
+
data.tar.gz: 4b8fc6853d556967173556f0067a6c611b4a93be0440c31659b0892f7d944606fbc19f15e27b1361b06cacea99e268bb81008893dfcc910e56103c10d0b9c42c
|
data/lib/pgtk/pool.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'loog'
|
|
|
4
4
|
# SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
|
|
5
5
|
# SPDX-License-Identifier: MIT
|
|
6
6
|
|
|
7
|
+
require 'ellipsized'
|
|
7
8
|
require 'pg'
|
|
8
9
|
require 'tago'
|
|
9
10
|
require_relative '../pgtk'
|
|
@@ -84,46 +85,7 @@ class Pgtk::Pool
|
|
|
84
85
|
" Pgtk version: #{Pgtk::VERSION}",
|
|
85
86
|
" PgSQL version: #{version}",
|
|
86
87
|
" #{@pool.size} connections:",
|
|
87
|
-
@pool.map
|
|
88
|
-
[
|
|
89
|
-
' ',
|
|
90
|
-
"##{c.backend_pid}",
|
|
91
|
-
case c.pipeline_status
|
|
92
|
-
when PG::Constants::PQ_PIPELINE_ON
|
|
93
|
-
'ON'
|
|
94
|
-
when PG::Constants::PQ_PIPELINE_OFF
|
|
95
|
-
'OFF'
|
|
96
|
-
when PG::Constants::PQ_PIPELINE_ABORTED
|
|
97
|
-
'ABORTED'
|
|
98
|
-
else
|
|
99
|
-
"pipeline_status=#{c.pipeline_status}"
|
|
100
|
-
end,
|
|
101
|
-
case c.status
|
|
102
|
-
when PG::Constants::CONNECTION_OK
|
|
103
|
-
'OK'
|
|
104
|
-
when PG::Constants::CONNECTION_BAD
|
|
105
|
-
'BAD'
|
|
106
|
-
else
|
|
107
|
-
"status=#{c.status}"
|
|
108
|
-
end,
|
|
109
|
-
case c.transaction_status
|
|
110
|
-
when PG::Constants::PQTRANS_IDLE
|
|
111
|
-
'IDLE'
|
|
112
|
-
when PG::Constants::PQTRANS_ACTIVE
|
|
113
|
-
'ACTIVE'
|
|
114
|
-
when PG::Constants::PQTRANS_INTRANS
|
|
115
|
-
'INTRANS'
|
|
116
|
-
when PG::Constants::PQTRANS_INERROR
|
|
117
|
-
'INERROR'
|
|
118
|
-
when PG::Constants::PQTRANS_UNKNOWN
|
|
119
|
-
'UNKNOWN'
|
|
120
|
-
else
|
|
121
|
-
"transaction_status=#{c.transaction_status}"
|
|
122
|
-
end
|
|
123
|
-
].join(' ')
|
|
124
|
-
rescue PG::ConnectionBad => e
|
|
125
|
-
e.message
|
|
126
|
-
end
|
|
88
|
+
@pool.map { |conn| info(conn) }
|
|
127
89
|
].flatten.join("\n")
|
|
128
90
|
end
|
|
129
91
|
|
|
@@ -247,6 +209,7 @@ class Pgtk::Pool
|
|
|
247
209
|
@timeout = timeout
|
|
248
210
|
@items = []
|
|
249
211
|
@taken = []
|
|
212
|
+
@free = []
|
|
250
213
|
@mutex = Mutex.new
|
|
251
214
|
@condition = ConditionVariable.new
|
|
252
215
|
end
|
|
@@ -256,6 +219,7 @@ class Pgtk::Pool
|
|
|
256
219
|
if @items.size < @size
|
|
257
220
|
@items << item
|
|
258
221
|
@taken << false
|
|
222
|
+
@free << (@items.size - 1)
|
|
259
223
|
else
|
|
260
224
|
index = @items.index(item)
|
|
261
225
|
if index.nil?
|
|
@@ -264,6 +228,7 @@ class Pgtk::Pool
|
|
|
264
228
|
@items[index] = item
|
|
265
229
|
end
|
|
266
230
|
@taken[index] = false
|
|
231
|
+
@free << index
|
|
267
232
|
end
|
|
268
233
|
@condition.signal
|
|
269
234
|
end
|
|
@@ -272,12 +237,12 @@ class Pgtk::Pool
|
|
|
272
237
|
def pop
|
|
273
238
|
@mutex.synchronize do
|
|
274
239
|
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout
|
|
275
|
-
while @
|
|
240
|
+
while @free.empty?
|
|
276
241
|
remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
277
242
|
raise(Busy, "No free connection appeared in the pool after #{@timeout}s of waiting") if remaining <= 0
|
|
278
243
|
@condition.wait(@mutex, remaining)
|
|
279
244
|
end
|
|
280
|
-
index = @
|
|
245
|
+
index = @free.shift
|
|
281
246
|
@taken[index] = true
|
|
282
247
|
@items[index]
|
|
283
248
|
end
|
|
@@ -311,6 +276,8 @@ class Pgtk::Pool
|
|
|
311
276
|
def exec(query, args = [], result = 0)
|
|
312
277
|
start = Time.now
|
|
313
278
|
sql = query.is_a?(Array) ? query.join(' ') : query
|
|
279
|
+
@conn.instance_variable_set(:@pgtk_last_query, sql)
|
|
280
|
+
@conn.instance_variable_set(:@pgtk_started_at, start)
|
|
314
281
|
begin
|
|
315
282
|
out =
|
|
316
283
|
if args.empty?
|
|
@@ -348,32 +315,85 @@ class Pgtk::Pool
|
|
|
348
315
|
|
|
349
316
|
def connect
|
|
350
317
|
conn = @pool.pop
|
|
351
|
-
conn = renew(conn) if dead?(conn)
|
|
352
318
|
begin
|
|
353
|
-
|
|
354
|
-
|
|
319
|
+
reason = cause(conn)
|
|
320
|
+
if reason
|
|
321
|
+
begin
|
|
322
|
+
conn = renew(conn, reason)
|
|
323
|
+
rescue StandardError => e
|
|
324
|
+
@log.warn("Failed to renew dead connection (#{reason}): #{e.message}")
|
|
325
|
+
end
|
|
326
|
+
end
|
|
355
327
|
begin
|
|
356
|
-
|
|
357
|
-
rescue StandardError =>
|
|
358
|
-
|
|
328
|
+
yield(conn)
|
|
329
|
+
rescue StandardError => e
|
|
330
|
+
begin
|
|
331
|
+
conn = renew(conn, "query failed: #{e.message.strip}")
|
|
332
|
+
rescue StandardError => re
|
|
333
|
+
@log.warn("Failed to renew connection after #{e.message}: #{re.message}")
|
|
334
|
+
end
|
|
335
|
+
raise(e)
|
|
359
336
|
end
|
|
360
|
-
raise(e)
|
|
361
337
|
ensure
|
|
362
338
|
@pool.push(conn)
|
|
363
339
|
end
|
|
364
340
|
end
|
|
365
341
|
|
|
366
|
-
def
|
|
367
|
-
conn.finished?
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
342
|
+
def cause(conn)
|
|
343
|
+
return 'finished' if conn.finished?
|
|
344
|
+
return 'status BAD' if conn.status == PG::Constants::CONNECTION_BAD
|
|
345
|
+
return "transaction status #{conn.transaction_status}" if conn.transaction_status != PG::Constants::PQTRANS_IDLE
|
|
346
|
+
nil
|
|
347
|
+
rescue StandardError => e
|
|
348
|
+
"inspection failed: #{e.message.strip}"
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def info(conn)
|
|
352
|
+
pipelines = { PG::Constants::PQ_PIPELINE_ON => 'ON', PG::Constants::PQ_PIPELINE_OFF => 'OFF',
|
|
353
|
+
PG::Constants::PQ_PIPELINE_ABORTED => 'ABORTED' }
|
|
354
|
+
statuses = { PG::Constants::CONNECTION_OK => 'OK', PG::Constants::CONNECTION_BAD => 'BAD' }
|
|
355
|
+
transactions = { PG::Constants::PQTRANS_IDLE => 'IDLE', PG::Constants::PQTRANS_ACTIVE => 'ACTIVE',
|
|
356
|
+
PG::Constants::PQTRANS_INTRANS => 'INTRANS', PG::Constants::PQTRANS_INERROR => 'INERROR',
|
|
357
|
+
PG::Constants::PQTRANS_UNKNOWN => 'UNKNOWN' }
|
|
358
|
+
conn.instance_variable_set(:@pgtk_pid, conn.backend_pid)
|
|
359
|
+
parts = [
|
|
360
|
+
' ',
|
|
361
|
+
"##{conn.backend_pid}",
|
|
362
|
+
pipelines.fetch(conn.pipeline_status, "pipeline_status=#{conn.pipeline_status}"),
|
|
363
|
+
statuses.fetch(conn.status, "status=#{conn.status}"),
|
|
364
|
+
transactions.fetch(conn.transaction_status, "transaction_status=#{conn.transaction_status}")
|
|
365
|
+
]
|
|
366
|
+
if conn.transaction_status != PG::Constants::PQTRANS_IDLE
|
|
367
|
+
started = conn.instance_variable_get(:@pgtk_started_at)
|
|
368
|
+
parts << started.ago if started
|
|
369
|
+
end
|
|
370
|
+
if conn.transaction_status == PG::Constants::PQTRANS_ACTIVE
|
|
371
|
+
running = conn.instance_variable_get(:@pgtk_last_query)
|
|
372
|
+
parts << "running: #{running.gsub(/\s+/, ' ').strip.ellipsized(60)}" if running
|
|
373
|
+
end
|
|
374
|
+
parts.join(' ')
|
|
375
|
+
rescue PG::ConnectionBad => e
|
|
376
|
+
pid = conn.instance_variable_get(:@pgtk_pid)
|
|
377
|
+
parts = [' ']
|
|
378
|
+
parts << (pid ? "##{pid}" : '#?')
|
|
379
|
+
parts << e.message.gsub(/\s+/, ' ').strip
|
|
380
|
+
closed = conn.instance_variable_get(:@pgtk_closed_at)
|
|
381
|
+
parts << "#{closed.ago} ago" if closed
|
|
382
|
+
reason = conn.instance_variable_get(:@pgtk_closed_reason)
|
|
383
|
+
parts << "because: #{reason.gsub(/\s+/, ' ').strip}" if reason
|
|
384
|
+
last = conn.instance_variable_get(:@pgtk_last_query)
|
|
385
|
+
parts << "last query: #{last.gsub(/\s+/, ' ').strip.ellipsized(60)}" if last
|
|
386
|
+
"#{parts.shift} #{parts.shift} #{parts.join(', ')}"
|
|
372
387
|
end
|
|
373
388
|
|
|
374
|
-
def renew(conn)
|
|
389
|
+
def renew(conn, reason)
|
|
375
390
|
begin
|
|
376
|
-
|
|
391
|
+
unless conn.finished?
|
|
392
|
+
conn.instance_variable_set(:@pgtk_pid, conn.backend_pid)
|
|
393
|
+
conn.instance_variable_set(:@pgtk_closed_at, Time.now)
|
|
394
|
+
conn.instance_variable_set(:@pgtk_closed_reason, reason.gsub(/\s+/, ' ').strip)
|
|
395
|
+
conn.close
|
|
396
|
+
end
|
|
377
397
|
rescue StandardError => e
|
|
378
398
|
@log.warn("Failed to close connection: #{e.message}")
|
|
379
399
|
end
|
data/lib/pgtk/retry.rb
CHANGED
|
@@ -85,11 +85,14 @@ class Pgtk::Retry
|
|
|
85
85
|
].join("\n")
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
-
# Execute a SQL query with automatic retry
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
88
|
+
# Execute a SQL query with automatic retry on transient failures.
|
|
89
|
+
# SELECT queries are retried on any error, since reads are idempotent.
|
|
90
|
+
# Non-SELECT queries are retried only on PG::ConnectionBad, since by
|
|
91
|
+
# definition the query never reached the server, so retrying cannot
|
|
92
|
+
# duplicate a write. Other errors on writes propagate immediately,
|
|
93
|
+
# because a failure may occur after the server received the query but
|
|
94
|
+
# before the acknowledgement reached the client, and retrying a
|
|
95
|
+
# non-idempotent write could duplicate it.
|
|
93
96
|
#
|
|
94
97
|
# @param [String] sql The SQL query with params inside (possibly)
|
|
95
98
|
# @return [Array] Result rows
|
|
@@ -98,6 +101,10 @@ class Pgtk::Retry
|
|
|
98
101
|
attempt = 0
|
|
99
102
|
begin
|
|
100
103
|
@pool.exec(sql, *)
|
|
104
|
+
rescue PG::ConnectionBad => e
|
|
105
|
+
attempt += 1
|
|
106
|
+
raise(Exhausted, "Retry gave up after #{@attempts} attempts: #{e.message}") if attempt >= @attempts
|
|
107
|
+
retry
|
|
101
108
|
rescue StandardError, Pgtk::Impatient::TooSlow => e
|
|
102
109
|
raise(e) unless query.strip.upcase.start_with?('SELECT')
|
|
103
110
|
attempt += 1
|
data/lib/pgtk/version.rb
CHANGED