pgtk 0.22.0 → 0.23.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 +1 -0
- data/Gemfile.lock +4 -0
- data/README.md +2 -2
- data/lib/pgtk/impatient.rb +4 -4
- data/lib/pgtk/pool.rb +71 -14
- data/lib/pgtk/retry.rb +4 -4
- data/lib/pgtk/spy.rb +4 -4
- data/lib/pgtk/stash.rb +33 -31
- 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: 57546e93aaf2c029a795f61302d943e4f64e7d1e03b77d339636bdfb73d271d5
|
|
4
|
+
data.tar.gz: 467c0493e69e8402b83ea6b537ac69760d5e712ac088b856307d2418449878f9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 83b61249a1888eed186c7faa7814c9a07f28fa9268d8d7fad1dfc0b9d7f4b304d4c835116fce6e32b6e651d7a2fc12731417c52922daae8ad9759419b32d2344
|
|
7
|
+
data.tar.gz: 5107f90f035a11e3ab17c8718f759c6869ea24d3c195dafb1845d546587244ad1e6f82dca7da5b505055cefdafa97f7b0e6390f17374e2458f773e54f7f9dd75
|
data/Gemfile
CHANGED
|
@@ -16,6 +16,7 @@ gem 'rubocop-performance', '~>1.25', require: false
|
|
|
16
16
|
gem 'rubocop-rake', '~>0.7', require: false
|
|
17
17
|
gem 'simplecov', '~>0.22', require: false
|
|
18
18
|
gem 'simplecov-cobertura', '~>3.0'
|
|
19
|
+
gem 'threads', '~>0.4'
|
|
19
20
|
gem 'timeout', '~>0.4'
|
|
20
21
|
gem 'xcop', '~>0.8', require: false
|
|
21
22
|
gem 'yard', '~>0.9', require: false
|
data/Gemfile.lock
CHANGED
|
@@ -102,6 +102,9 @@ GEM
|
|
|
102
102
|
simplecov_json_formatter (0.1.4)
|
|
103
103
|
slop (4.10.1)
|
|
104
104
|
tago (0.3.0)
|
|
105
|
+
threads (0.4.1)
|
|
106
|
+
backtrace (~> 0)
|
|
107
|
+
concurrent-ruby (~> 1.0)
|
|
105
108
|
timeout (0.4.4)
|
|
106
109
|
unicode-display_width (3.2.0)
|
|
107
110
|
unicode-emoji (~> 4.1)
|
|
@@ -132,6 +135,7 @@ DEPENDENCIES
|
|
|
132
135
|
rubocop-rake (~> 0.7)
|
|
133
136
|
simplecov (~> 0.22)
|
|
134
137
|
simplecov-cobertura (~> 3.0)
|
|
138
|
+
threads (~> 0.4)
|
|
135
139
|
timeout (~> 0.4)
|
|
136
140
|
xcop (~> 0.8)
|
|
137
141
|
yard (~> 0.9)
|
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
|
@@ -29,8 +29,8 @@ require_relative 'wire'
|
|
|
29
29
|
# )
|
|
30
30
|
#
|
|
31
31
|
# # Create and start a connection pool with 4 connections
|
|
32
|
-
# pool = Pgtk::Pool.new(wire)
|
|
33
|
-
# pool.start!
|
|
32
|
+
# pool = Pgtk::Pool.new(wire, max: 4)
|
|
33
|
+
# pool.start!
|
|
34
34
|
#
|
|
35
35
|
# # Execute a simple query
|
|
36
36
|
# pool.exec('SELECT * FROM users')
|
|
@@ -51,11 +51,14 @@ class Pgtk::Pool
|
|
|
51
51
|
# Constructor.
|
|
52
52
|
#
|
|
53
53
|
# @param [Pgtk::Wire] wire The wire
|
|
54
|
+
# @param [Integer] max Total amount of PostgreSQL connections in the pool
|
|
54
55
|
# @param [Object] log The log
|
|
55
|
-
def initialize(wire, log: Loog::NULL)
|
|
56
|
+
def initialize(wire, max: 8, log: Loog::NULL)
|
|
56
57
|
@wire = wire
|
|
58
|
+
@max = max
|
|
57
59
|
@log = log
|
|
58
|
-
@pool = IterableQueue.new
|
|
60
|
+
@pool = IterableQueue.new(max)
|
|
61
|
+
@started = false
|
|
59
62
|
end
|
|
60
63
|
|
|
61
64
|
# Get the version of PostgreSQL server.
|
|
@@ -74,7 +77,42 @@ class Pgtk::Pool
|
|
|
74
77
|
" PgSQL version: #{version}",
|
|
75
78
|
" #{@pool.size} connections:",
|
|
76
79
|
@pool.map do |c|
|
|
77
|
-
|
|
80
|
+
[
|
|
81
|
+
' ',
|
|
82
|
+
"##{c.backend_pid}",
|
|
83
|
+
case c.pipeline_status
|
|
84
|
+
when PG::Constants::PQ_PIPELINE_ON
|
|
85
|
+
'ON'
|
|
86
|
+
when PG::Constants::PQ_PIPELINE_OFF
|
|
87
|
+
'OFF'
|
|
88
|
+
when PG::Constants::PQ_PIPELINE_ABORTED
|
|
89
|
+
'ABORTED'
|
|
90
|
+
else
|
|
91
|
+
"pipeline_status=#{c.pipeline_status}"
|
|
92
|
+
end,
|
|
93
|
+
case c.status
|
|
94
|
+
when PG::Constants::CONNECTION_OK
|
|
95
|
+
'OK'
|
|
96
|
+
when PG::Constants::CONNECTION_BAD
|
|
97
|
+
'BAD'
|
|
98
|
+
else
|
|
99
|
+
"status=#{c.status}"
|
|
100
|
+
end,
|
|
101
|
+
case c.transaction_status
|
|
102
|
+
when PG::Constants::PQTRANS_IDLE
|
|
103
|
+
'IDLE'
|
|
104
|
+
when PG::Constants::PQTRANS_ACTIVE
|
|
105
|
+
'ACTIVE'
|
|
106
|
+
when PG::Constants::PQTRANS_INTRANS
|
|
107
|
+
'INTRANS'
|
|
108
|
+
when PG::Constants::PQTRANS_INERROR
|
|
109
|
+
'INERROR'
|
|
110
|
+
when PG::Constants::PQTRANS_UNKNOWN
|
|
111
|
+
'UNKNOWN'
|
|
112
|
+
else
|
|
113
|
+
"transaction_status=#{c.transaction_status}"
|
|
114
|
+
end
|
|
115
|
+
].join(' ')
|
|
78
116
|
end
|
|
79
117
|
].flatten.join("\n")
|
|
80
118
|
end
|
|
@@ -85,13 +123,13 @@ class Pgtk::Pool
|
|
|
85
123
|
# keep in mind that not all servers will allow you to have many connections
|
|
86
124
|
# open at the same time. For example, Heroku free PostgreSQL database
|
|
87
125
|
# allows only one connection open.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
max.times do
|
|
126
|
+
def start!
|
|
127
|
+
return if @started
|
|
128
|
+
@max.times do
|
|
92
129
|
@pool << @wire.connection
|
|
93
130
|
end
|
|
94
|
-
@
|
|
131
|
+
@started = true
|
|
132
|
+
@log.debug("PostgreSQL pool started with #{@max} connections")
|
|
95
133
|
end
|
|
96
134
|
|
|
97
135
|
# Make a query and return the result as an array of hashes. For example,
|
|
@@ -184,24 +222,43 @@ class Pgtk::Pool
|
|
|
184
222
|
#
|
|
185
223
|
# This class is used internally by Pool to store database connections
|
|
186
224
|
# and provide the ability to iterate over them for inspection purposes.
|
|
225
|
+
#
|
|
226
|
+
# The queue is bounded by size. When an item is taken out, it remains in
|
|
227
|
+
# the internal array but is marked as "taken". When returned, it's placed
|
|
228
|
+
# back in its original slot and marked as available.
|
|
187
229
|
class IterableQueue
|
|
188
|
-
def initialize
|
|
230
|
+
def initialize(size)
|
|
231
|
+
@size = size
|
|
189
232
|
@items = []
|
|
233
|
+
@taken = []
|
|
190
234
|
@mutex = Mutex.new
|
|
191
235
|
@condition = ConditionVariable.new
|
|
192
236
|
end
|
|
193
237
|
|
|
194
238
|
def <<(item)
|
|
195
239
|
@mutex.synchronize do
|
|
196
|
-
@items
|
|
240
|
+
if @items.size < @size
|
|
241
|
+
@items << item
|
|
242
|
+
@taken << false
|
|
243
|
+
else
|
|
244
|
+
index = @items.index(item)
|
|
245
|
+
if index.nil?
|
|
246
|
+
index = @taken.index(true)
|
|
247
|
+
raise 'No taken slot found' if index.nil?
|
|
248
|
+
@items[index] = item
|
|
249
|
+
end
|
|
250
|
+
@taken[index] = false
|
|
251
|
+
end
|
|
197
252
|
@condition.signal
|
|
198
253
|
end
|
|
199
254
|
end
|
|
200
255
|
|
|
201
256
|
def pop
|
|
202
257
|
@mutex.synchronize do
|
|
203
|
-
@condition.wait(@mutex) while @items.empty?
|
|
204
|
-
@
|
|
258
|
+
@condition.wait(@mutex) while @taken.all? || @items.empty?
|
|
259
|
+
index = @taken.index(false)
|
|
260
|
+
@taken[index] = true
|
|
261
|
+
@items[index]
|
|
205
262
|
end
|
|
206
263
|
end
|
|
207
264
|
|
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
|
@@ -60,12 +60,13 @@ class Pgtk::Stash
|
|
|
60
60
|
@max_queue_length = max_queue_length
|
|
61
61
|
@threads = threads
|
|
62
62
|
@loog = loog
|
|
63
|
+
@tpool = Concurrent::FixedThreadPool.new(@threads)
|
|
63
64
|
end
|
|
64
65
|
|
|
65
66
|
# Start a new connection pool with the given arguments.
|
|
66
|
-
def start!
|
|
67
|
+
def start!
|
|
67
68
|
launch!
|
|
68
|
-
@pool.start!
|
|
69
|
+
@pool.start!
|
|
69
70
|
end
|
|
70
71
|
|
|
71
72
|
# Get the PostgreSQL server version.
|
|
@@ -90,9 +91,12 @@ class Pgtk::Stash
|
|
|
90
91
|
'',
|
|
91
92
|
"Pgtk::Stash (refill_interval=#{@refill_interval}s, max_queue_length=#{@max_queue_length}, threads=#{@threads}):",
|
|
92
93
|
" #{'not ' if @launched.false?}launched",
|
|
93
|
-
" #{@
|
|
94
|
-
" #{@stash[:
|
|
95
|
-
qq.
|
|
94
|
+
" #{@tpool.queue_length} task(s) in the thread pool",
|
|
95
|
+
" #{@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]}" }
|
|
96
100
|
].join("\n")
|
|
97
101
|
end
|
|
98
102
|
|
|
@@ -132,7 +136,7 @@ class Pgtk::Stash
|
|
|
132
136
|
@stash[:tables][t].append(pure).uniq!
|
|
133
137
|
end
|
|
134
138
|
@stash[:queries][pure] ||= {}
|
|
135
|
-
@stash[:queries][pure][key] = { ret:, params:, result: }
|
|
139
|
+
@stash[:queries][pure][key] = { ret:, params:, result:, used: Time.now }
|
|
136
140
|
end
|
|
137
141
|
end
|
|
138
142
|
end
|
|
@@ -140,6 +144,7 @@ class Pgtk::Stash
|
|
|
140
144
|
@entrance.with_write_lock do
|
|
141
145
|
@stash[:queries][pure][key][:popularity] ||= 0
|
|
142
146
|
@stash[:queries][pure][key][:popularity] += 1
|
|
147
|
+
@stash[:queries][pure][key][:used] = Time.now
|
|
143
148
|
end
|
|
144
149
|
end
|
|
145
150
|
end
|
|
@@ -171,34 +176,31 @@ class Pgtk::Stash
|
|
|
171
176
|
|
|
172
177
|
def launch!
|
|
173
178
|
raise 'Cannot launch multiple times on same cache data' unless @launched.make_true
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
@stash[:queries][q][k][:popularity] = 0
|
|
180
|
-
end
|
|
181
|
-
end
|
|
179
|
+
retire = 60 * 60
|
|
180
|
+
Concurrent::TimerTask.execute(execution_interval: retire, executor: @tpool) do
|
|
181
|
+
@entrance.with_write_lock do
|
|
182
|
+
@stash[:queries].each_key do |q|
|
|
183
|
+
@stash[:queries][q].delete_if { |_, h| h[:used] < Time.now - retire }
|
|
182
184
|
end
|
|
183
185
|
end
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
186
|
+
end
|
|
187
|
+
Concurrent::TimerTask.execute(execution_interval: @refill_interval, executor: @tpool) do
|
|
188
|
+
@stash[:queries]
|
|
189
|
+
.map { |k, v| [k, v.values.sum { |vv| vv[:popularity] }, v.values.any? { |vv| vv[:stale] }] }
|
|
190
|
+
.select { _1[2] }
|
|
191
|
+
.sort_by { -_1[1] }
|
|
192
|
+
.each do |a|
|
|
193
|
+
q = a[0]
|
|
194
|
+
@stash[:queries][q].each_key do |k|
|
|
195
|
+
next unless @stash[:queries][q][k][:stale]
|
|
196
|
+
next if @tpool.queue_length >= @max_queue_length
|
|
197
|
+
@tpool.post do
|
|
198
|
+
h = @stash[:queries][q][k]
|
|
199
|
+
ret = @pool.exec(q, h[:params], h[:result])
|
|
200
|
+
@entrance.with_write_lock do
|
|
195
201
|
h = @stash[:queries][q][k]
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
h = @stash[:queries][q][k]
|
|
199
|
-
h[:stale] = false
|
|
200
|
-
h[:ret] = ret
|
|
201
|
-
end
|
|
202
|
+
h[:stale] = false
|
|
203
|
+
h[:ret] = ret
|
|
202
204
|
end
|
|
203
205
|
end
|
|
204
206
|
end
|
data/lib/pgtk/version.rb
CHANGED