pgtk 0.22.1 → 0.24.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: 0dc255f274b10a4bc7fe0a0e4d6c2a68a918957e571a147062325baf77d91db1
4
- data.tar.gz: 971da231e5ff704bf28d28f3655f170ab986327da714cd3f27bade9ff784c6bd
3
+ metadata.gz: b16d736e3b32a69aabf5a87fff3d4611f0af14c6c87e72fb7dc3e84d2ee027ee
4
+ data.tar.gz: 1c989c0af1b8ce28e325b0b9e146295e3cd24f02bb2cec0fee7ed2ac714ed2d6
5
5
  SHA512:
6
- metadata.gz: d65bd86d962357d89353daa86da5cd10f255cd1840198217963fe854cc83805c44c5fbff48e4c109871a29d17f8d6c93932a2a186b4067d6be28348cc631e174
7
- data.tar.gz: ed61c3ae252043956524564e4a4a6b087d9df684b83c509d3f92291d721e2840cdd21fbc0535bbd027bb6b436c3164518b710a8f7ecae68ff4b6a12980e83864
6
+ metadata.gz: a6dcaac89f9ea200f9030505df810fb4a0a2136387e98833960f830be10d19aa9f1b55f7c3f8e6fd447c8df30235233aafd8f7730bde99c8751cb910416e660f
7
+ data.tar.gz: 34e7b0682c858ce58397abf4c8694cdb849453f70d0d0fe1c220d96eb800a425602ae693a149b53b7b2a1fc1b8afafee3f9c72a494b1214393d21d2d1dbafebd
data/Gemfile.lock CHANGED
@@ -26,7 +26,7 @@ GEM
26
26
  loog (~> 0.6)
27
27
  tago (~> 0.1)
28
28
  joined (0.4.0)
29
- json (2.15.2)
29
+ json (2.16.0)
30
30
  language_server-protocol (3.17.0.5)
31
31
  lint_roller (1.1.0)
32
32
  logger (1.7.0)
@@ -52,7 +52,7 @@ GEM
52
52
  pg (1.6.2-x64-mingw-ucrt)
53
53
  pg (1.6.2-x86_64-linux)
54
54
  prism (1.6.0)
55
- qbash (0.4.5)
55
+ qbash (0.4.7)
56
56
  backtrace (> 0)
57
57
  elapsed (> 0)
58
58
  loog (> 0)
@@ -76,7 +76,7 @@ GEM
76
76
  rubocop-ast (>= 1.47.1, < 2.0)
77
77
  ruby-progressbar (~> 1.7)
78
78
  unicode-display_width (>= 2.4.0, < 4.0)
79
- rubocop-ast (1.47.1)
79
+ rubocop-ast (1.48.0)
80
80
  parser (>= 3.3.7.2)
81
81
  prism (~> 1.4)
82
82
  rubocop-minitest (0.38.2)
@@ -101,7 +101,7 @@ GEM
101
101
  simplecov-html (0.13.2)
102
102
  simplecov_json_formatter (0.1.4)
103
103
  slop (4.10.1)
104
- tago (0.3.0)
104
+ tago (0.4.0)
105
105
  threads (0.4.1)
106
106
  backtrace (~> 0)
107
107
  concurrent-ruby (~> 1.0)
data/README.md CHANGED
@@ -115,8 +115,8 @@ From inside your app you may find this class useful:
115
115
 
116
116
  ```ruby
117
117
  require 'pgtk/pool'
118
- pgsql = Pgtk::Pool.new(Pgtk::Wire::Yaml.new('config.yml'))
119
- pgsql.start!(5) # Start it with five simultaneous connections
118
+ pgsql = Pgtk::Pool.new(Pgtk::Wire::Yaml.new('config.yml'), max: 5)
119
+ pgsql.start! # Start it with five simultaneous connections
120
120
  ```
121
121
 
122
122
  You can also let it pick the connection parameters from the environment
@@ -19,8 +19,8 @@ require_relative '../pgtk'
19
19
  # Basic usage:
20
20
  #
21
21
  # # Create and configure a regular pool
22
- # pool = Pgtk::Pool.new(wire)
23
- # pool.start!(4)
22
+ # pool = Pgtk::Pool.new(wire, max: 4)
23
+ # pool.start!
24
24
  #
25
25
  # # Wrap the pool in an impatient decorator with a 2-second timeout
26
26
  # impatient = Pgtk::Impatient.new(pool, 2)
@@ -69,8 +69,8 @@ class Pgtk::Impatient
69
69
  end
70
70
 
71
71
  # Start a new connection pool with the given arguments.
72
- def start!(*)
73
- @pool.start!(*)
72
+ def start!
73
+ @pool.start!
74
74
  end
75
75
 
76
76
  # Get the version of PostgreSQL server.
data/lib/pgtk/pool.rb CHANGED
@@ -7,6 +7,7 @@ require 'pg'
7
7
  require 'loog'
8
8
  require 'tago'
9
9
  require_relative '../pgtk'
10
+ require_relative 'version'
10
11
  require_relative 'wire'
11
12
 
12
13
  # Pool provides a connection pool for PostgreSQL database connections.
@@ -29,8 +30,8 @@ require_relative 'wire'
29
30
  # )
30
31
  #
31
32
  # # Create and start a connection pool with 4 connections
32
- # pool = Pgtk::Pool.new(wire)
33
- # pool.start!(4)
33
+ # pool = Pgtk::Pool.new(wire, max: 4)
34
+ # pool.start!
34
35
  #
35
36
  # # Execute a simple query
36
37
  # pool.exec('SELECT * FROM users')
@@ -51,11 +52,14 @@ class Pgtk::Pool
51
52
  # Constructor.
52
53
  #
53
54
  # @param [Pgtk::Wire] wire The wire
55
+ # @param [Integer] max Total amount of PostgreSQL connections in the pool
54
56
  # @param [Object] log The log
55
- def initialize(wire, log: Loog::NULL)
57
+ def initialize(wire, max: 8, log: Loog::NULL)
56
58
  @wire = wire
59
+ @max = max
57
60
  @log = log
58
- @pool = IterableQueue.new
61
+ @pool = IterableQueue.new(max)
62
+ @started = false
59
63
  end
60
64
 
61
65
  # Get the version of PostgreSQL server.
@@ -71,6 +75,7 @@ class Pgtk::Pool
71
75
  def dump
72
76
  [
73
77
  'Pgtk::Pool',
78
+ " Pgtk version: #{Pgtk::VERSION}",
74
79
  " PgSQL version: #{version}",
75
80
  " #{@pool.size} connections:",
76
81
  @pool.map do |c|
@@ -120,13 +125,13 @@ class Pgtk::Pool
120
125
  # keep in mind that not all servers will allow you to have many connections
121
126
  # open at the same time. For example, Heroku free PostgreSQL database
122
127
  # allows only one connection open.
123
- #
124
- # @param [Integer] max Total amount of PostgreSQL connections in the pool
125
- def start!(max = 8)
126
- max.times do
128
+ def start!
129
+ return if @started
130
+ @max.times do
127
131
  @pool << @wire.connection
128
132
  end
129
- @log.debug("PostgreSQL pool started with #{max} connections")
133
+ @started = true
134
+ @log.debug("PostgreSQL pool started with #{@max} connections")
130
135
  end
131
136
 
132
137
  # Make a query and return the result as an array of hashes. For example,
@@ -219,24 +224,43 @@ class Pgtk::Pool
219
224
  #
220
225
  # This class is used internally by Pool to store database connections
221
226
  # and provide the ability to iterate over them for inspection purposes.
227
+ #
228
+ # The queue is bounded by size. When an item is taken out, it remains in
229
+ # the internal array but is marked as "taken". When returned, it's placed
230
+ # back in its original slot and marked as available.
222
231
  class IterableQueue
223
- def initialize
232
+ def initialize(size)
233
+ @size = size
224
234
  @items = []
235
+ @taken = []
225
236
  @mutex = Mutex.new
226
237
  @condition = ConditionVariable.new
227
238
  end
228
239
 
229
240
  def <<(item)
230
241
  @mutex.synchronize do
231
- @items << item
242
+ if @items.size < @size
243
+ @items << item
244
+ @taken << false
245
+ else
246
+ index = @items.index(item)
247
+ if index.nil?
248
+ index = @taken.index(true)
249
+ raise 'No taken slot found' if index.nil?
250
+ @items[index] = item
251
+ end
252
+ @taken[index] = false
253
+ end
232
254
  @condition.signal
233
255
  end
234
256
  end
235
257
 
236
258
  def pop
237
259
  @mutex.synchronize do
238
- @condition.wait(@mutex) while @items.empty?
239
- @items.shift
260
+ @condition.wait(@mutex) while @taken.all? || @items.empty?
261
+ index = @taken.index(false)
262
+ @taken[index] = true
263
+ @items[index]
240
264
  end
241
265
  end
242
266
 
data/lib/pgtk/retry.rb CHANGED
@@ -16,8 +16,8 @@ require_relative '../pgtk'
16
16
  # Basic usage:
17
17
  #
18
18
  # # Create and configure a regular pool
19
- # pool = Pgtk::Pool.new(wire)
20
- # pool.start!(4)
19
+ # pool = Pgtk::Pool.new(wire, max: 4)
20
+ # pool.start!
21
21
  #
22
22
  # # Wrap the pool in a retry decorator with 3 attempts
23
23
  # retry_pool = Pgtk::Retry.new(pool, attempts: 3)
@@ -58,8 +58,8 @@ class Pgtk::Retry
58
58
  end
59
59
 
60
60
  # Start a new connection pool with the given arguments.
61
- def start!(*)
62
- @pool.start!(*)
61
+ def start!
62
+ @pool.start!
63
63
  end
64
64
 
65
65
  # Get the version of PostgreSQL server.
data/lib/pgtk/spy.rb CHANGED
@@ -19,8 +19,8 @@ require_relative 'wire'
19
19
  # Basic usage:
20
20
  #
21
21
  # # Create and configure a regular pool
22
- # pool = Pgtk::Pool.new(wire)
23
- # pool.start!(4)
22
+ # pool = Pgtk::Pool.new(wire, max: 4)
23
+ # pool.start!
24
24
  #
25
25
  # # Wrap the pool in a spy that tracks all executed queries
26
26
  # queries = []
@@ -57,8 +57,8 @@ class Pgtk::Spy
57
57
  end
58
58
 
59
59
  # Start a new connection pool with the given arguments.
60
- def start!(*)
61
- @pool.start!(*)
60
+ def start!
61
+ @pool.start!
62
62
  end
63
63
 
64
64
  # Get the version of PostgreSQL server.
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 'tago'
9
10
  require_relative '../pgtk'
10
11
 
11
12
  # Database query cache implementation.
@@ -39,6 +40,8 @@ class Pgtk::Stash
39
40
  # @option [Hash] queries Internal cache data (default: {})
40
41
  # @option [Hash] tables Internal cache data (default: {})
41
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)
42
45
  # @param [Integer] max_queue_length Number of refilling tasks in the queue
43
46
  # @param [Integer] threads Number of threads in tpool
44
47
  # @param [Loog] loog Logger for debugging (default: null logger)
@@ -48,6 +51,8 @@ class Pgtk::Stash
48
51
  refill_interval: 16,
49
52
  max_queue_length: 128,
50
53
  threads: 4,
54
+ cap: 10_000,
55
+ cap_interval: 60,
51
56
  loog: Loog::NULL,
52
57
  entrance: Concurrent::ReentrantReadWriteLock.new,
53
58
  launched: Concurrent::AtomicBoolean.new(false)
@@ -59,14 +64,16 @@ class Pgtk::Stash
59
64
  @refill_interval = refill_interval
60
65
  @max_queue_length = max_queue_length
61
66
  @threads = threads
67
+ @cap = cap
68
+ @cap_interval = cap_interval
62
69
  @loog = loog
63
70
  @tpool = Concurrent::FixedThreadPool.new(@threads)
64
71
  end
65
72
 
66
73
  # Start a new connection pool with the given arguments.
67
- def start!(*)
74
+ def start!
68
75
  launch!
69
- @pool.start!(*)
76
+ @pool.start!
70
77
  end
71
78
 
72
79
  # Get the PostgreSQL server version.
@@ -78,13 +85,14 @@ class Pgtk::Stash
78
85
  # Convert internal state into text.
79
86
  def dump
80
87
  qq =
81
- @stash[:queries].map do |k, v|
82
- [
83
- k.dup, # the query
84
- v.values.count, # how many keys?
85
- v.values.sum { |vv| vv[:popularity] }, # total popularity of all keys
86
- v.values.count { |vv| vv[:stale] } # how many stale keys?
87
- ]
88
+ @stash[:queries].map do |q, kk|
89
+ {
90
+ q: q.dup, # the query
91
+ c: kk.values.count, # how many keys?
92
+ p: kk.values.sum { |vv| vv[:popularity] }, # total popularity of all keys
93
+ s: kk.values.count { |vv| vv[:stale] }, # how many stale keys?
94
+ u: kk.values.map { |vv| vv[:used] }.max || Time.now # when was it used
95
+ }
88
96
  end
89
97
  [
90
98
  @pool.dump,
@@ -93,10 +101,14 @@ class Pgtk::Stash
93
101
  " #{'not ' if @launched.false?}launched",
94
102
  " #{@tpool.queue_length} task(s) in the thread pool",
95
103
  " #{@stash[:tables].count} table(s) in cache",
96
- " #{qq.sum { |a| a[3] }} stale quer(ies) in cache:",
97
- qq.select { |a| a[3].positive? }.sort_by { -_1[2] }.take(16).map { |a| " #{a[1]}/#{a[2]}p/#{a[3]}s: #{a[0]}" },
98
- " #{qq.count { |a| a[3].zero? }} other quer(ies) in cache:",
99
- qq.select { |a| a[3].zero? }.sort_by { -_1[2] }.take(8).map { |a| " #{a[1]}/#{a[2]}p/#{a[3]}s: #{a[0]}" }
104
+ " #{qq.sum { |a| a[:s] }} stale quer(ies) in cache:",
105
+ qq.select { |a| a[:s].positive? }.sort_by { -_1[:p] }.take(8).map do |a|
106
+ " #{a[:c]}/#{a[:p]}p/#{a[:s]}s/#{a[:u].ago}: #{a[:q]}"
107
+ end,
108
+ " #{qq.count { |a| a[:s].zero? }} other quer(ies) in cache:",
109
+ qq.select { |a| a[:s].zero? }.sort_by { -_1[:p] }.take(16).map do |a|
110
+ " #{a[:c]}/#{a[:p]}p/#{a[:s]}s/#{a[:u].ago}: #{a[:q]}"
111
+ end
100
112
  ].join("\n")
101
113
  end
102
114
 
@@ -136,7 +148,7 @@ class Pgtk::Stash
136
148
  @stash[:tables][t].append(pure).uniq!
137
149
  end
138
150
  @stash[:queries][pure] ||= {}
139
- @stash[:queries][pure][key] = { ret:, params:, result: }
151
+ @stash[:queries][pure][key] = { ret:, params:, result:, used: Time.now }
140
152
  end
141
153
  end
142
154
  end
@@ -144,6 +156,7 @@ class Pgtk::Stash
144
156
  @entrance.with_write_lock do
145
157
  @stash[:queries][pure][key][:popularity] ||= 0
146
158
  @stash[:queries][pure][key][:popularity] += 1
159
+ @stash[:queries][pure][key][:used] = Time.now
147
160
  end
148
161
  end
149
162
  end
@@ -175,11 +188,16 @@ class Pgtk::Stash
175
188
 
176
189
  def launch!
177
190
  raise 'Cannot launch multiple times on same cache data' unless @launched.make_true
178
- Concurrent::TimerTask.execute(execution_interval: 60 * 60, executor: @tpool) do
179
- @entrance.with_write_lock do
180
- @stash[:queries].each_key do |q|
181
- @stash[:queries][q].each_key do |k|
182
- @stash[:queries][q][k][:popularity] = 0
191
+ Concurrent::TimerTask.execute(execution_interval: @cap_interval, executor: @tpool) do
192
+ loop do
193
+ s = @stash[:queries].values.sum { |kk| kk.values.size }
194
+ break if s <= @cap
195
+ @entrance.with_write_lock do
196
+ @stash[:queries].each_key do |q|
197
+ m = @stash[:queries][q].values.map { |h| h[:used] }.min
198
+ next unless m
199
+ @stash[:queries][q].delete_if { |_, h| h[:used] == m }
200
+ @stash[:queries].delete_if { |_, kk| kk.empty? }
183
201
  end
184
202
  end
185
203
  end
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.22.1'
14
+ VERSION = '0.24.0'
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.22.1
4
+ version: 0.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko