pgtk 0.24.0 → 0.24.1
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 +13 -0
- data/lib/pgtk/stash.rb +65 -18
- 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: 88c5e888b9c116ec175abea3c43fa02d2a878a3edbc61c901be4a07f5a7730d4
|
|
4
|
+
data.tar.gz: 608f79d18ee7799c1a4bd200d8157a64f25033431ab972fe41072ac7223dae44
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 788a641261c89c9da31133e8d38a9cfcb1bbb91d6d1e2b43161ade3827930ff54baf6096e1ac7c54f13d7921928aefc163d2a5750000e3a916a17c212ea8097e
|
|
7
|
+
data.tar.gz: 237557831c63cc930743f152abd2a08cd5783d664f4a56f3a5faf24bd359097799463fa0eb66440085b99c46e50a338faf3b76677addc36b0a3f21fb0b415fbe
|
data/README.md
CHANGED
|
@@ -220,6 +220,19 @@ require 'pgtk/stash'
|
|
|
220
220
|
stash = Pgtk::Stash.new(pgsql)
|
|
221
221
|
```
|
|
222
222
|
|
|
223
|
+
You can configure `Stash` with optional parameters:
|
|
224
|
+
|
|
225
|
+
```ruby
|
|
226
|
+
stash = Pgtk::Stash.new(
|
|
227
|
+
pgsql,
|
|
228
|
+
cap: 10_000, # Maximum cached query results (default: 10,000)
|
|
229
|
+
cap_interval: 60, # Seconds between cache size enforcement (default: 60)
|
|
230
|
+
refill_interval: 16, # Seconds between stale query refilling (default: 16)
|
|
231
|
+
threads: 4, # Worker threads for background refilling (default: 4)
|
|
232
|
+
max_queue_length: 128 # Maximum refilling tasks in queue (default: 128)
|
|
233
|
+
)
|
|
234
|
+
```
|
|
235
|
+
|
|
223
236
|
`Stash` automatically caches read queries and invalidates the cache
|
|
224
237
|
when tables are modified:
|
|
225
238
|
|
data/lib/pgtk/stash.rb
CHANGED
|
@@ -19,6 +19,12 @@ require_relative '../pgtk'
|
|
|
19
19
|
#
|
|
20
20
|
# The implementation is very naive! Use it at your own risk.
|
|
21
21
|
#
|
|
22
|
+
# @example Basic usage
|
|
23
|
+
# pool = Pgtk::Pool.new(...)
|
|
24
|
+
# stash = Pgtk::Stash.new(pool, cap: 1000, refill_interval: 30)
|
|
25
|
+
# stash.start!
|
|
26
|
+
# result = stash.exec('SELECT * FROM users WHERE id = $1', [42])
|
|
27
|
+
#
|
|
22
28
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
23
29
|
# Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
|
|
24
30
|
# License:: MIT
|
|
@@ -35,16 +41,24 @@ class Pgtk::Stash
|
|
|
35
41
|
|
|
36
42
|
# Initialize a new Stash with query caching.
|
|
37
43
|
#
|
|
38
|
-
# @param [Object] pool
|
|
39
|
-
# @param [Hash] stash
|
|
40
|
-
#
|
|
41
|
-
# @
|
|
42
|
-
#
|
|
43
|
-
# @param [Integer]
|
|
44
|
-
#
|
|
45
|
-
# @param [Integer]
|
|
46
|
-
#
|
|
47
|
-
# @param [
|
|
44
|
+
# @param [Object] pool The underlying connection pool that executes actual database queries
|
|
45
|
+
# @param [Hash] stash Internal cache structure containing queries and tables hashes for sharing state
|
|
46
|
+
# across transactions
|
|
47
|
+
# @param [Integer] refill_interval Interval in seconds between background tasks that recalculate stale
|
|
48
|
+
# cached queries
|
|
49
|
+
# @param [Integer] max_queue_length Maximum number of refilling tasks allowed in the thread pool queue
|
|
50
|
+
# before new tasks are skipped
|
|
51
|
+
# @param [Integer] threads Number of worker threads in the background thread pool for cache refilling
|
|
52
|
+
# operations
|
|
53
|
+
# @param [Integer] cap Maximum number of cached query results to retain; oldest queries are evicted when
|
|
54
|
+
# this limit is exceeded
|
|
55
|
+
# @param [Integer] cap_interval Interval in seconds between background tasks that enforce the cache size
|
|
56
|
+
# cap by removing old queries
|
|
57
|
+
# @param [Loog] loog Logger instance for debugging and monitoring cache operations (default: null logger)
|
|
58
|
+
# @param [Concurrent::ReentrantReadWriteLock] entrance Read-write lock for thread-safe cache access
|
|
59
|
+
# shared across instances
|
|
60
|
+
# @param [Concurrent::AtomicBoolean] launched Atomic boolean flag tracking whether background tasks have
|
|
61
|
+
# been started to prevent multiple launches
|
|
48
62
|
def initialize(
|
|
49
63
|
pool,
|
|
50
64
|
stash: { queries: {}, tables: {} },
|
|
@@ -70,7 +84,13 @@ class Pgtk::Stash
|
|
|
70
84
|
@tpool = Concurrent::FixedThreadPool.new(@threads)
|
|
71
85
|
end
|
|
72
86
|
|
|
73
|
-
# Start
|
|
87
|
+
# Start the connection pool and launch background cache management tasks.
|
|
88
|
+
#
|
|
89
|
+
# Initializes background timer tasks for cache refilling and size capping.
|
|
90
|
+
# The refill task periodically updates stale cached queries based on popularity.
|
|
91
|
+
# The cap task removes oldest queries when cache size exceeds the configured limit.
|
|
92
|
+
#
|
|
93
|
+
# @return [void]
|
|
74
94
|
def start!
|
|
75
95
|
launch!
|
|
76
96
|
@pool.start!
|
|
@@ -83,6 +103,11 @@ class Pgtk::Stash
|
|
|
83
103
|
end
|
|
84
104
|
|
|
85
105
|
# Convert internal state into text.
|
|
106
|
+
#
|
|
107
|
+
# Generates a detailed report of the cache state including query counts,
|
|
108
|
+
# popularity scores, stale queries, and thread pool status.
|
|
109
|
+
#
|
|
110
|
+
# @return [String] Multi-line text representation of the current cache state
|
|
86
111
|
def dump
|
|
87
112
|
qq =
|
|
88
113
|
@stash[:queries].map do |q, kk|
|
|
@@ -97,8 +122,11 @@ class Pgtk::Stash
|
|
|
97
122
|
[
|
|
98
123
|
@pool.dump,
|
|
99
124
|
'',
|
|
100
|
-
|
|
125
|
+
# rubocop:disable Layout/LineLength
|
|
126
|
+
"Pgtk::Stash (refill_interval=#{@refill_interval}s, max_queue_length=#{@max_queue_length}, threads=#{@threads}, cap=#{@cap}, cap_interval=#{@cap_interval}s):",
|
|
127
|
+
# rubocop:enable Layout/LineLength
|
|
101
128
|
" #{'not ' if @launched.false?}launched",
|
|
129
|
+
" #{stash_size} queries stashed (#{stash_size > @cap ? 'above' : 'below'} the cap)",
|
|
102
130
|
" #{@tpool.queue_length} task(s) in the thread pool",
|
|
103
131
|
" #{@stash[:tables].count} table(s) in cache",
|
|
104
132
|
" #{qq.sum { |a| a[:s] }} stale quer(ies) in cache:",
|
|
@@ -115,11 +143,14 @@ class Pgtk::Stash
|
|
|
115
143
|
# Execute a SQL query with optional caching.
|
|
116
144
|
#
|
|
117
145
|
# Read queries are cached, while write queries bypass the cache and invalidate related entries.
|
|
146
|
+
# Queries containing modification keywords (INSERT, UPDATE, DELETE, etc.) are executed directly
|
|
147
|
+
# and trigger invalidation of cached queries for affected tables. Read queries (SELECT)
|
|
148
|
+
# are cached by query text and parameter values. Queries containing NOW() are never cached.
|
|
118
149
|
#
|
|
119
|
-
# @param [String, Array<String>] query The SQL query to execute
|
|
120
|
-
# @param [Array] params Query parameters
|
|
121
|
-
# @param [Integer] result
|
|
122
|
-
# @return [PG::Result] Query result
|
|
150
|
+
# @param [String, Array<String>] query The SQL query to execute as a string or array of strings to be joined
|
|
151
|
+
# @param [Array] params Query parameters for placeholder substitution in prepared statements (default: empty array)
|
|
152
|
+
# @param [Integer] result Result format code where 0 requests text format and 1 requests binary format (default: 0)
|
|
153
|
+
# @return [PG::Result] Query result object containing rows and metadata from the database
|
|
123
154
|
def exec(query, params = [], result = 0)
|
|
124
155
|
pure = (query.is_a?(Array) ? query.join(' ') : query).gsub(/\s+/, ' ').strip
|
|
125
156
|
if MODS_RE.match?(pure) || /(^|\s)pg_[a-z_]+\(/.match?(pure)
|
|
@@ -186,12 +217,28 @@ class Pgtk::Stash
|
|
|
186
217
|
|
|
187
218
|
private
|
|
188
219
|
|
|
220
|
+
# Calculate total number of cached query results.
|
|
221
|
+
#
|
|
222
|
+
# Counts all cached query-parameter combinations across all queries.
|
|
223
|
+
#
|
|
224
|
+
# @return [Integer] Total count of cached query results
|
|
225
|
+
def stash_size
|
|
226
|
+
@stash[:queries].values.sum { |kk| kk.values.size }
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Launch background tasks for cache management.
|
|
230
|
+
#
|
|
231
|
+
# Starts two concurrent timer tasks: one for enforcing cache size cap by removing
|
|
232
|
+
# oldest queries, and another for refilling stale cached queries based on popularity.
|
|
233
|
+
# This method can only be called once per cache instance.
|
|
234
|
+
#
|
|
235
|
+
# @return [nil]
|
|
236
|
+
# @raise [RuntimeError] if background tasks have already been launched on this cache instance
|
|
189
237
|
def launch!
|
|
190
238
|
raise 'Cannot launch multiple times on same cache data' unless @launched.make_true
|
|
191
239
|
Concurrent::TimerTask.execute(execution_interval: @cap_interval, executor: @tpool) do
|
|
192
240
|
loop do
|
|
193
|
-
|
|
194
|
-
break if s <= @cap
|
|
241
|
+
break if stash_size <= @cap
|
|
195
242
|
@entrance.with_write_lock do
|
|
196
243
|
@stash[:queries].each_key do |q|
|
|
197
244
|
m = @stash[:queries][q].values.map { |h| h[:used] }.min
|
data/lib/pgtk/version.rb
CHANGED