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 +4 -4
- data/Gemfile.lock +4 -4
- data/README.md +2 -2
- data/lib/pgtk/impatient.rb +4 -4
- data/lib/pgtk/pool.rb +37 -13
- data/lib/pgtk/retry.rb +4 -4
- data/lib/pgtk/spy.rb +4 -4
- data/lib/pgtk/stash.rb +37 -19
- 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: b16d736e3b32a69aabf5a87fff3d4611f0af14c6c87e72fb7dc3e84d2ee027ee
|
|
4
|
+
data.tar.gz: 1c989c0af1b8ce28e325b0b9e146295e3cd24f02bb2cec0fee7ed2ac714ed2d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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!
|
|
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
|
data/lib/pgtk/impatient.rb
CHANGED
|
@@ -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!
|
|
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!
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
max.times do
|
|
128
|
+
def start!
|
|
129
|
+
return if @started
|
|
130
|
+
@max.times do
|
|
127
131
|
@pool << @wire.connection
|
|
128
132
|
end
|
|
129
|
-
@
|
|
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
|
|
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
|
-
@
|
|
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!
|
|
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!
|
|
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 |
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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[
|
|
97
|
-
qq.select { |a| a[
|
|
98
|
-
|
|
99
|
-
|
|
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:
|
|
179
|
-
|
|
180
|
-
@stash[:queries].
|
|
181
|
-
|
|
182
|
-
|
|
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