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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54a200f1f191d63b73c0137b1a1ace4aa34667f041d141365b47d9c90d96f191
4
- data.tar.gz: 1518e5031b71a69e3955cabe77397d05917398fb87a12b94ee5c3aa57ab107eb
3
+ metadata.gz: 7acb299c9e4e6cd1e272651041704fd89da2858bd1e4b2aa935eed9724815419
4
+ data.tar.gz: 1260a6fb42a11e34d1a733eb9d1bff8ba6a28f81724457e12986adccd134ebc5
5
5
  SHA512:
6
- metadata.gz: 28bbce0d3b2cfcbe2347f376738938e0e714f22b89fd2696052c56ecf7be83c242e9f0a87c76c235f109ec2008a45a1471ea157fc78f3ac9a8b8b71641ac2556
7
- data.tar.gz: e94795ee7e4de524c77b5fddefb7a0d2ea9e85ec05beb09ebf81000919afec9a7b54d8a0fb480d9e24c16990fed7b7b142769caefddaefdd32d504f8086f5f46
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.19.9)
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.87.0)
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.6.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
@@ -10,5 +10,5 @@ require_relative '../pgtk'
10
10
  # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
11
11
  # License:: MIT
12
12
  module Pgtk
13
- VERSION = '0.32.7' unless defined?(VERSION)
13
+ VERSION = '0.33.0' unless defined?(VERSION)
14
14
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgtk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.32.7
4
+ version: 0.33.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko