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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b16d736e3b32a69aabf5a87fff3d4611f0af14c6c87e72fb7dc3e84d2ee027ee
4
- data.tar.gz: 1c989c0af1b8ce28e325b0b9e146295e3cd24f02bb2cec0fee7ed2ac714ed2d6
3
+ metadata.gz: 88c5e888b9c116ec175abea3c43fa02d2a878a3edbc61c901be4a07f5a7730d4
4
+ data.tar.gz: 608f79d18ee7799c1a4bd200d8157a64f25033431ab972fe41072ac7223dae44
5
5
  SHA512:
6
- metadata.gz: a6dcaac89f9ea200f9030505df810fb4a0a2136387e98833960f830be10d19aa9f1b55f7c3f8e6fd447c8df30235233aafd8f7730bde99c8751cb910416e660f
7
- data.tar.gz: 34e7b0682c858ce58397abf4c8694cdb849453f70d0d0fe1c220d96eb800a425602ae693a149b53b7b2a1fc1b8afafee3f9c72a494b1214393d21d2d1dbafebd
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 Original object
39
- # @param [Hash] stash Optional existing stash to use (default: new empty stash)
40
- # @option [Hash] queries Internal cache data (default: {})
41
- # @option [Hash] tables Internal cache data (default: {})
42
- # @param [Integer] refill_interval Interval in seconds for recalculate stale queries
43
- # @param [Integer] cap How many queries to keep in cache (if more, oldest ones are deleted)
44
- # @param [Integer] cap_interval Interval in seconds for cap the cache (remove old queries)
45
- # @param [Integer] max_queue_length Number of refilling tasks in the queue
46
- # @param [Integer] threads Number of threads in tpool
47
- # @param [Loog] loog Logger for debugging (default: null logger)
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 a new connection pool with the given arguments.
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
- "Pgtk::Stash (refill_interval=#{@refill_interval}s, max_queue_length=#{@max_queue_length}, threads=#{@threads}):",
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 Should be 0 for text results, 1 for binary
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
- s = @stash[:queries].values.sum { |kk| kk.values.size }
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
@@ -11,5 +11,5 @@ require_relative '../pgtk'
11
11
  # License:: MIT
12
12
  module Pgtk
13
13
  # Current version of the library.
14
- VERSION = '0.24.0'
14
+ VERSION = '0.24.1'
15
15
  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.24.0
4
+ version: 0.24.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko