pgtk 0.32.7 → 0.33.0
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/Gemfile.lock +3 -3
- data/lib/pgtk/stash.rb +42 -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: 7acb299c9e4e6cd1e272651041704fd89da2858bd1e4b2aa935eed9724815419
|
|
4
|
+
data.tar.gz: 1260a6fb42a11e34d1a733eb9d1bff8ba6a28f81724457e12986adccd134ebc5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 29aad3a51e77675418fc5eaffa72d7e0015d4e6d1464c87cc118f3ab94e223b0ce4461cffba91e7f4e2d22b594bb0a61f2d4b299db1055d2637d9afca5a0533d
|
|
7
|
+
data.tar.gz: 8483ec44d6da87cd0e57f57e2ca14373965269a5463bc74ba7e38ac4693b4acf35299a64b6ec91a17fc04812160ca31e6826313fce660cdd3e210c8cc3007c81
|
data/Gemfile.lock
CHANGED
|
@@ -36,7 +36,7 @@ GEM
|
|
|
36
36
|
tago (~> 0.1)
|
|
37
37
|
ellipsized (0.3.0)
|
|
38
38
|
joined (0.4.0)
|
|
39
|
-
json (2.
|
|
39
|
+
json (2.20.0)
|
|
40
40
|
language_server-protocol (3.17.0.5)
|
|
41
41
|
lint_roller (1.1.0)
|
|
42
42
|
logger (1.7.0)
|
|
@@ -95,7 +95,7 @@ GEM
|
|
|
95
95
|
tago (~> 0.0)
|
|
96
96
|
regexp_parser (2.12.0)
|
|
97
97
|
rexml (3.4.4)
|
|
98
|
-
rubocop (1.
|
|
98
|
+
rubocop (1.88.1)
|
|
99
99
|
json (~> 2.3)
|
|
100
100
|
language_server-protocol (~> 3.17.0.2)
|
|
101
101
|
lint_roller (~> 1.1.0)
|
|
@@ -109,7 +109,7 @@ GEM
|
|
|
109
109
|
rubocop-ast (1.49.1)
|
|
110
110
|
parser (>= 3.3.7.2)
|
|
111
111
|
prism (~> 1.7)
|
|
112
|
-
rubocop-elegant (0.
|
|
112
|
+
rubocop-elegant (0.7.0)
|
|
113
113
|
lint_roller (~> 1.1)
|
|
114
114
|
rubocop (~> 1.75)
|
|
115
115
|
rubocop-minitest (0.39.1)
|
data/lib/pgtk/stash.rb
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
require 'concurrent-ruby'
|
|
7
7
|
require 'joined'
|
|
8
8
|
require 'loog'
|
|
9
|
+
require 'objspace'
|
|
9
10
|
require 'tago'
|
|
10
11
|
require_relative '../pgtk'
|
|
11
12
|
|
|
@@ -15,6 +16,10 @@ require_relative '../pgtk'
|
|
|
15
16
|
# the cache when tables are modified. Read queries are cached while write
|
|
16
17
|
# queries bypass the cache and invalidate related cached entries.
|
|
17
18
|
#
|
|
19
|
+
# Tables that are too write-heavy to benefit from caching can be listed
|
|
20
|
+
# via the +volatile:+ constructor parameter; any read whose FROM/JOIN
|
|
21
|
+
# set touches one of them bypasses the cache entirely.
|
|
22
|
+
#
|
|
18
23
|
# Thread-safe with read-write locking.
|
|
19
24
|
#
|
|
20
25
|
# The implementation is very naive! Use it at your own risk.
|
|
@@ -34,6 +39,7 @@ class Pgtk::Stash
|
|
|
34
39
|
REINDEX TRUNCATE CREATE ALTER DROP SET START
|
|
35
40
|
].freeze
|
|
36
41
|
MODS_RE = Regexp.new("\\A(#{MODS.join('|')})(\\s|$)")
|
|
42
|
+
WITH_RE = /\AWITH(\s|$)/
|
|
37
43
|
|
|
38
44
|
IDENT = '[a-z_][a-z0-9_]*'
|
|
39
45
|
|
|
@@ -48,7 +54,7 @@ class Pgtk::Stash
|
|
|
48
54
|
CLOCK_TIMESTAMP)(?:\s*\(|(?=[^a-z0-9_]|$))
|
|
49
55
|
/ix
|
|
50
56
|
|
|
51
|
-
private_constant :MODS, :ALTS, :IDENT, :MODS_RE, :ALTS_RE, :READS_RE, :NONDETERMINISTIC
|
|
57
|
+
private_constant :MODS, :ALTS, :IDENT, :MODS_RE, :WITH_RE, :ALTS_RE, :READS_RE, :NONDETERMINISTIC
|
|
52
58
|
|
|
53
59
|
# Initialize a new Stash with query caching.
|
|
54
60
|
#
|
|
@@ -66,11 +72,13 @@ class Pgtk::Stash
|
|
|
66
72
|
# @param [Float] retirement Interval in seconds between retirement tasks
|
|
67
73
|
# @param [Loog] loog Logger instance
|
|
68
74
|
# @param [Concurrent::ReentrantReadWriteLock] entrance Read-write lock for thread-safe access
|
|
75
|
+
# @param [Array<String>] volatile Table names that must never be cached
|
|
69
76
|
def initialize(
|
|
70
77
|
pool,
|
|
71
78
|
stash: { queries: {}, tables: {}, table_mod: {}, table_inflight: {} },
|
|
72
79
|
loog: Loog::NULL,
|
|
73
80
|
entrance: Concurrent::ReentrantReadWriteLock.new,
|
|
81
|
+
volatile: [],
|
|
74
82
|
refill: 16,
|
|
75
83
|
delay: 0,
|
|
76
84
|
maxqueue: 128,
|
|
@@ -88,6 +96,7 @@ class Pgtk::Stash
|
|
|
88
96
|
end
|
|
89
97
|
@loog = loog
|
|
90
98
|
@entrance = entrance
|
|
99
|
+
@volatile = Array(volatile).map(&:to_s).freeze
|
|
91
100
|
@refill = refill
|
|
92
101
|
@delay = delay
|
|
93
102
|
@maxqueue = maxqueue
|
|
@@ -130,9 +139,9 @@ class Pgtk::Stash
|
|
|
130
139
|
# @return [PG::Result] Query result object
|
|
131
140
|
def exec(query, params = [], result = 0)
|
|
132
141
|
pure = (query.is_a?(Array) ? query.join(' ') : query).gsub(/\s+/, ' ').strip
|
|
133
|
-
if MODS_RE.match?(pure)
|
|
142
|
+
if MODS_RE.match?(pure) || (WITH_RE.match?(pure) && ALTS_RE.match?(pure))
|
|
134
143
|
modify(pure, params, result)
|
|
135
|
-
elsif /(^|\s)pg_[a-z_]+\(/.match?(pure)
|
|
144
|
+
elsif /(^|\s)pg_[a-z_]+\(/.match?(pure) || (!@volatile.empty? && @volatile.intersect?(pure.scan(READS_RE).flatten))
|
|
136
145
|
@pool.exec(pure, params, result)
|
|
137
146
|
else
|
|
138
147
|
select(pure, params, result)
|
|
@@ -145,7 +154,7 @@ class Pgtk::Stash
|
|
|
145
154
|
# @return [Object] The result of the block
|
|
146
155
|
def transaction
|
|
147
156
|
@pool.transaction do |t|
|
|
148
|
-
yield(Pgtk::Stash.new(t, stash: @stash, loog: @loog, entrance: @entrance))
|
|
157
|
+
yield(Pgtk::Stash.new(t, stash: @stash, loog: @loog, entrance: @entrance, volatile: @volatile))
|
|
149
158
|
end
|
|
150
159
|
end
|
|
151
160
|
|
|
@@ -155,7 +164,7 @@ class Pgtk::Stash
|
|
|
155
164
|
# @return [Object] The result of the block
|
|
156
165
|
def session
|
|
157
166
|
@pool.session do |t|
|
|
158
|
-
yield(Pgtk::Stash.new(t, stash: @stash, loog: @loog, entrance: @entrance))
|
|
167
|
+
yield(Pgtk::Stash.new(t, stash: @stash, loog: @loog, entrance: @entrance, volatile: @volatile))
|
|
159
168
|
end
|
|
160
169
|
end
|
|
161
170
|
|
|
@@ -184,6 +193,7 @@ class Pgtk::Stash
|
|
|
184
193
|
' Not launched yet'
|
|
185
194
|
end,
|
|
186
195
|
" #{cached} queries cached (#{cached > @cap ? 'above' : 'below'} the cap)",
|
|
196
|
+
" ~#{footprint} bytes of RAM occupied by cache",
|
|
187
197
|
" #{@stash[:tables].count} tables in cache",
|
|
188
198
|
" #{list.sum { |a| a[:s] }} stale queries in cache:",
|
|
189
199
|
stale(list),
|
|
@@ -326,6 +336,33 @@ class Pgtk::Stash
|
|
|
326
336
|
end
|
|
327
337
|
end
|
|
328
338
|
|
|
339
|
+
# Estimate the total heap footprint, in bytes, of everything held in
|
|
340
|
+
# +@stash[:queries]+.
|
|
341
|
+
#
|
|
342
|
+
# The count of cached entries alone is misleading: a single query with many
|
|
343
|
+
# parameter combinations keeps a distinct +PG::Result+ per combination, and
|
|
344
|
+
# the +cap+ limits the number of entries, not their weight. This gauge sums
|
|
345
|
+
# +ObjectSpace.memsize_of+ over each cached entry and its +:ret+, +:params+,
|
|
346
|
+
# and key strings, so an operator can tell whether the cache is about to
|
|
347
|
+
# exhaust memory. +Marshal+ is not an option here since neither +PG::Result+
|
|
348
|
+
# nor +Concurrent::AtomicFixnum+ is marshallable.
|
|
349
|
+
#
|
|
350
|
+
# The result is approximate: +memsize_of+ is shallow, so nested contents
|
|
351
|
+
# (rows inside a +PG::Result+) are only partially accounted for. A rough
|
|
352
|
+
# order of magnitude is enough.
|
|
353
|
+
#
|
|
354
|
+
# @return [Integer] Approximate total bytes of RAM held by the cache
|
|
355
|
+
def footprint
|
|
356
|
+
@entrance.with_read_lock do
|
|
357
|
+
@stash[:queries].sum do |q, kk|
|
|
358
|
+
ObjectSpace.memsize_of(q) +
|
|
359
|
+
kk.sum do |params, entry|
|
|
360
|
+
[params, entry, entry[:ret], entry[:params]].sum { |o| ObjectSpace.memsize_of(o) }
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
329
366
|
# Discover ON DELETE CASCADE / ON UPDATE CASCADE foreign keys so that a
|
|
330
367
|
# modify on the parent table also invalidates cached queries on children.
|
|
331
368
|
def cascade!
|
data/lib/pgtk/version.rb
CHANGED