pgtk 0.31.8 → 0.31.9
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/README.md +17 -9
- data/lib/pgtk/impatient.rb +27 -17
- 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: 52f06b5f116d87eb7229752520f8cfff19e5c97d53c94a5acac935ad5f5d74e7
|
|
4
|
+
data.tar.gz: 83f18d647dbcd7ab1e503ac14da13d3abf77353f70acc2937090b237bcf28af7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4f70d7c3755cee075cd586b4ed3830cc2c932bc4a0213e36fe18104e9b4c4c6ac74fcb00e985aeda37766c62b399ffd829a537f59582be7cb2be31a7eacfaac6
|
|
7
|
+
data.tar.gz: 9cf6517ef4f27aaf6403fd3dbc522b11c69853a7ccc704f009126f29d7b1aedbcf852522f3d90ff242b3bf482f51634f33c9c2373a88c880e8281b20a731403e
|
data/README.md
CHANGED
|
@@ -202,12 +202,20 @@ You can exclude specific queries from timeout enforcement using regex patterns:
|
|
|
202
202
|
impatient = Pgtk::Impatient.new(pool, 2, /^SELECT/, /^VACUUM/)
|
|
203
203
|
```
|
|
204
204
|
|
|
205
|
+
The timeout is enforced on the server side: each query is wrapped in a tiny
|
|
206
|
+
transaction that issues `SET LOCAL statement_timeout`, and PostgreSQL itself
|
|
207
|
+
terminates the query at the deadline. This guarantees the server-side
|
|
208
|
+
connection slot is freed even when the client cannot deliver a cancellation
|
|
209
|
+
request — for example, behind a transaction-pool PgBouncer that does not
|
|
210
|
+
forward client disconnects to in-flight server queries.
|
|
211
|
+
|
|
205
212
|
Key features:
|
|
206
213
|
|
|
207
214
|
1. Configurable timeout in seconds for each query
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
215
|
+
1. Raises `Pgtk::Impatient::TooSlow` exception when timeout is exceeded
|
|
216
|
+
1. Can exclude queries matching specific patterns from timeout checks
|
|
217
|
+
1. Sets PostgreSQL's `statement_timeout` per query and per transaction,
|
|
218
|
+
so timeouts are enforced server-side and orphan backends do not pile up
|
|
211
219
|
|
|
212
220
|
## Query Caching with `Pgtk::Stash`
|
|
213
221
|
|
|
@@ -253,9 +261,9 @@ Note that the caching implementation is basic and only suitable
|
|
|
253
261
|
for simple queries:
|
|
254
262
|
|
|
255
263
|
1. Queries must reference tables (using `FROM` or `JOIN`)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
the cache and invalidate all cached queries for affected tables
|
|
264
|
+
1. Cache is invalidated by table, not by specific rows
|
|
265
|
+
1. Write operations (`INSERT`, `UPDATE`, `DELETE`) bypass
|
|
266
|
+
the cache and invalidate all cached queries for affected tables
|
|
259
267
|
|
|
260
268
|
## Automatic Retries with `Pgtk::Retry`
|
|
261
269
|
|
|
@@ -284,11 +292,11 @@ retry_pool.exec('INSERT INTO logs (message) VALUES ($1)', ['User logged in'])
|
|
|
284
292
|
Key features:
|
|
285
293
|
|
|
286
294
|
1. Only `SELECT` queries are retried (to prevent duplicate data modifications)
|
|
287
|
-
|
|
295
|
+
1. Retries happen immediately, except on `PG::ConnectionBad`,
|
|
288
296
|
where an exponential backoff (50ms, 200ms, 1s) is applied
|
|
289
297
|
between attempts to avoid amplifying upstream login storms
|
|
290
|
-
|
|
291
|
-
|
|
298
|
+
1. The original error is raised after all retry attempts are exhausted
|
|
299
|
+
1. Works seamlessly with other decorators like `Pgtk::Spy` and `Pgtk::Impatient`
|
|
292
300
|
|
|
293
301
|
## Some Examples
|
|
294
302
|
|
data/lib/pgtk/impatient.rb
CHANGED
|
@@ -4,18 +4,21 @@
|
|
|
4
4
|
# SPDX-License-Identifier: MIT
|
|
5
5
|
|
|
6
6
|
require 'ellipsized'
|
|
7
|
-
require '
|
|
7
|
+
require 'pg'
|
|
8
8
|
require 'tago'
|
|
9
|
-
require 'timeout'
|
|
10
9
|
require_relative '../pgtk'
|
|
11
10
|
|
|
12
11
|
# Impatient is a decorator for Pool that enforces timeouts on all database operations.
|
|
13
12
|
# It ensures that SQL queries don't run indefinitely, which helps prevent application
|
|
14
13
|
# hangs and resource exhaustion when database operations are slow or stalled.
|
|
15
14
|
#
|
|
16
|
-
# This class implements the same interface as Pool but
|
|
17
|
-
#
|
|
18
|
-
#
|
|
15
|
+
# This class implements the same interface as Pool but enforces the timeout on the
|
|
16
|
+
# server side, by wrapping each query in a tiny transaction that issues
|
|
17
|
+
# +SET LOCAL statement_timeout+. PostgreSQL itself terminates the query at the
|
|
18
|
+
# deadline, which guarantees that the server-side connection slot is freed even
|
|
19
|
+
# when the client cannot deliver a cancellation request (for example, behind a
|
|
20
|
+
# transaction-pool PgBouncer that does not forward client disconnects to in-flight
|
|
21
|
+
# server queries). On timeout, +TooSlow+ is raised.
|
|
19
22
|
#
|
|
20
23
|
# Basic usage:
|
|
21
24
|
#
|
|
@@ -29,7 +32,7 @@ require_relative '../pgtk'
|
|
|
29
32
|
# # Execute queries with automatic timeout enforcement
|
|
30
33
|
# begin
|
|
31
34
|
# impatient.exec('SELECT * FROM large_table WHERE complex_condition')
|
|
32
|
-
# rescue
|
|
35
|
+
# rescue Pgtk::Impatient::TooSlow
|
|
33
36
|
# puts "Query timed out after 2 seconds"
|
|
34
37
|
# end
|
|
35
38
|
#
|
|
@@ -39,7 +42,7 @@ require_relative '../pgtk'
|
|
|
39
42
|
# t.exec('UPDATE large_table SET processed = true')
|
|
40
43
|
# t.exec('DELETE FROM queue WHERE processed = true')
|
|
41
44
|
# end
|
|
42
|
-
# rescue
|
|
45
|
+
# rescue PG::QueryCanceled
|
|
43
46
|
# puts "Transaction timed out"
|
|
44
47
|
# end
|
|
45
48
|
#
|
|
@@ -91,23 +94,30 @@ class Pgtk::Impatient
|
|
|
91
94
|
].join("\n")
|
|
92
95
|
end
|
|
93
96
|
|
|
94
|
-
# Execute a SQL query with a timeout.
|
|
97
|
+
# Execute a SQL query with a server-side timeout.
|
|
98
|
+
#
|
|
99
|
+
# The query is wrapped in a tiny transaction that issues
|
|
100
|
+
# +SET LOCAL statement_timeout+, so PostgreSQL itself terminates the query
|
|
101
|
+
# at the deadline. This guarantees the server-side connection slot is freed
|
|
102
|
+
# even when the client cannot deliver a cancellation request (for example,
|
|
103
|
+
# behind a transaction-pool PgBouncer). When the deadline fires, the
|
|
104
|
+
# underlying +PG::QueryCanceled+ is translated to +TooSlow+.
|
|
95
105
|
#
|
|
96
106
|
# @param [String, Array] query The SQL query with params inside (possibly)
|
|
97
107
|
# @param [Array] args List of arguments
|
|
98
108
|
# @return [Array] Result rows
|
|
99
|
-
# @raise [
|
|
109
|
+
# @raise [TooSlow] If the query takes too long
|
|
100
110
|
def exec(query, *args)
|
|
101
111
|
sql = query.is_a?(Array) ? query.join(' ') : query
|
|
102
112
|
return @pool.exec(sql, *args) if @off.any? { |re| re.match?(sql) }
|
|
103
113
|
start = Time.now
|
|
104
|
-
|
|
114
|
+
ms = [Integer(@timeout * 1000), 1].max
|
|
105
115
|
begin
|
|
106
|
-
|
|
107
|
-
|
|
116
|
+
@pool.transaction do |t|
|
|
117
|
+
t.exec("SET LOCAL statement_timeout = #{ms}")
|
|
118
|
+
t.exec(sql, *args)
|
|
108
119
|
end
|
|
109
|
-
rescue
|
|
110
|
-
raise(e) unless e.message == token
|
|
120
|
+
rescue PG::QueryCanceled
|
|
111
121
|
raise(TooSlow, [
|
|
112
122
|
'SQL query',
|
|
113
123
|
("with #{args.count} argument#{'s' if args.count > 1}" unless args.empty?),
|
|
@@ -125,14 +135,14 @@ class Pgtk::Impatient
|
|
|
125
135
|
# terminates the session, which frees locks and releases the connection
|
|
126
136
|
# slot back to the pool.
|
|
127
137
|
#
|
|
128
|
-
# @yield [
|
|
138
|
+
# @yield [Object] Yields a transaction object that responds to +exec+
|
|
129
139
|
# @return [Object] Result of the block
|
|
130
140
|
def transaction
|
|
131
141
|
@pool.transaction do |t|
|
|
132
|
-
ms = Integer(
|
|
142
|
+
ms = [Integer(@timeout * 1000), 1].max
|
|
133
143
|
t.exec("SET LOCAL statement_timeout = #{ms}")
|
|
134
144
|
t.exec("SET LOCAL idle_in_transaction_session_timeout = #{ms}")
|
|
135
|
-
yield(
|
|
145
|
+
yield(t)
|
|
136
146
|
end
|
|
137
147
|
end
|
|
138
148
|
end
|
data/lib/pgtk/version.rb
CHANGED