rack-mini-profiler 2.3.3 → 3.1.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/CHANGELOG.md +20 -0
- data/README.md +95 -78
- data/lib/generators/rack_mini_profiler/USAGE +9 -0
- data/lib/generators/rack_mini_profiler/install_generator.rb +13 -0
- data/lib/generators/{rack_profiler/templates/rack_profiler.rb → rack_mini_profiler/templates/rack_mini_profiler.rb} +1 -1
- data/lib/generators/rack_profiler/install_generator.rb +6 -3
- data/lib/mini_profiler/client_settings.rb +2 -2
- data/lib/mini_profiler/config.rb +9 -5
- data/lib/mini_profiler/storage/abstract_store.rb +30 -57
- data/lib/mini_profiler/storage/file_store.rb +4 -0
- data/lib/mini_profiler/storage/memcache_store.rb +11 -7
- data/lib/mini_profiler/storage/memory_store.rb +56 -12
- data/lib/mini_profiler/storage/redis_store.rb +151 -62
- data/lib/mini_profiler/storage.rb +7 -0
- data/lib/mini_profiler/timer_struct/base.rb +2 -0
- data/lib/mini_profiler/timer_struct/sql.rb +2 -0
- data/lib/mini_profiler/timer_struct.rb +8 -0
- data/lib/mini_profiler/version.rb +1 -1
- data/lib/{mini_profiler/profiler.rb → mini_profiler.rb} +103 -69
- data/lib/patches/net_patches.rb +18 -17
- data/lib/rack-mini-profiler.rb +1 -24
- data/rack-mini-profiler.gemspec +2 -2
- metadata +12 -8
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'securerandom'
|
4
|
+
|
3
5
|
module Rack
|
4
6
|
class MiniProfiler
|
5
7
|
class MemoryStore < AbstractStore
|
@@ -53,6 +55,7 @@ module Rack
|
|
53
55
|
|
54
56
|
@token1, @token2, @cycle_at = nil
|
55
57
|
@snapshots_cycle = 0
|
58
|
+
@snapshot_groups = {}
|
56
59
|
@snapshots = []
|
57
60
|
|
58
61
|
initialize_locks
|
@@ -152,28 +155,69 @@ module Rack
|
|
152
155
|
end
|
153
156
|
end
|
154
157
|
|
155
|
-
def push_snapshot(page_struct, config)
|
158
|
+
def push_snapshot(page_struct, group_name, config)
|
159
|
+
@snapshots_lock.synchronize do
|
160
|
+
group = @snapshot_groups[group_name]
|
161
|
+
if !group
|
162
|
+
@snapshot_groups[group_name] = {
|
163
|
+
worst_score: page_struct.duration_ms,
|
164
|
+
best_score: page_struct.duration_ms,
|
165
|
+
snapshots: [page_struct]
|
166
|
+
}
|
167
|
+
if @snapshot_groups.size > config.max_snapshot_groups
|
168
|
+
group_keys = @snapshot_groups.keys
|
169
|
+
group_keys.sort_by! do |key|
|
170
|
+
@snapshot_groups[key][:worst_score]
|
171
|
+
end
|
172
|
+
group_keys.reverse!
|
173
|
+
group_keys.pop(group_keys.size - config.max_snapshot_groups)
|
174
|
+
@snapshot_groups = @snapshot_groups.slice(*group_keys)
|
175
|
+
end
|
176
|
+
else
|
177
|
+
snapshots = group[:snapshots]
|
178
|
+
snapshots << page_struct
|
179
|
+
snapshots.sort_by!(&:duration_ms)
|
180
|
+
snapshots.reverse!
|
181
|
+
if snapshots.size > config.max_snapshots_per_group
|
182
|
+
snapshots.pop(snapshots.size - config.max_snapshots_per_group)
|
183
|
+
end
|
184
|
+
group[:worst_score] = snapshots[0].duration_ms
|
185
|
+
group[:best_score] = snapshots[-1].duration_ms
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def fetch_snapshots_overview
|
156
191
|
@snapshots_lock.synchronize do
|
157
|
-
|
158
|
-
@
|
159
|
-
|
160
|
-
|
161
|
-
|
192
|
+
groups = {}
|
193
|
+
@snapshot_groups.each do |name, group|
|
194
|
+
groups[name] = {
|
195
|
+
worst_score: group[:worst_score],
|
196
|
+
best_score: group[:best_score],
|
197
|
+
snapshots_count: group[:snapshots].size
|
198
|
+
}
|
162
199
|
end
|
200
|
+
groups
|
163
201
|
end
|
164
202
|
end
|
165
203
|
|
166
|
-
def
|
204
|
+
def fetch_snapshots_group(group_name)
|
167
205
|
@snapshots_lock.synchronize do
|
168
|
-
|
169
|
-
|
206
|
+
group = @snapshot_groups[group_name]
|
207
|
+
if group
|
208
|
+
group[:snapshots].dup
|
209
|
+
else
|
210
|
+
[]
|
170
211
|
end
|
171
212
|
end
|
172
213
|
end
|
173
214
|
|
174
|
-
def load_snapshot(id)
|
215
|
+
def load_snapshot(id, group_name)
|
175
216
|
@snapshots_lock.synchronize do
|
176
|
-
|
217
|
+
group = @snapshot_groups[group_name]
|
218
|
+
if group
|
219
|
+
group[:snapshots].find { |s| s[:id] == id }
|
220
|
+
end
|
177
221
|
end
|
178
222
|
end
|
179
223
|
|
@@ -182,7 +226,7 @@ module Rack
|
|
182
226
|
# used in tests only
|
183
227
|
def wipe_snapshots_data
|
184
228
|
@snapshots_cycle = 0
|
185
|
-
@
|
229
|
+
@snapshot_groups = {}
|
186
230
|
end
|
187
231
|
end
|
188
232
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'digest'
|
4
|
+
require 'securerandom'
|
4
5
|
|
5
6
|
module Rack
|
6
7
|
class MiniProfiler
|
@@ -25,7 +26,9 @@ module Rack
|
|
25
26
|
key = prefixed_id(id)
|
26
27
|
raw = redis.get key
|
27
28
|
begin
|
28
|
-
|
29
|
+
# rubocop:disable Security/MarshalLoad
|
30
|
+
Marshal.load(raw) if raw
|
31
|
+
# rubocop:enable Security/MarshalLoad
|
29
32
|
rescue
|
30
33
|
# bad format, junk old data
|
31
34
|
redis.del key
|
@@ -131,81 +134,127 @@ unviewed_ids: #{get_unviewed_ids(user)}
|
|
131
134
|
)
|
132
135
|
end
|
133
136
|
|
134
|
-
def push_snapshot(page_struct, config)
|
135
|
-
|
136
|
-
|
137
|
+
def push_snapshot(page_struct, group_name, config)
|
138
|
+
group_zset_key = group_snapshot_zset_key(group_name)
|
139
|
+
group_hash_key = group_snapshot_hash_key(group_name)
|
140
|
+
overview_zset_key = snapshot_overview_zset_key
|
137
141
|
|
138
142
|
id = page_struct[:id]
|
139
|
-
score = page_struct.duration_ms
|
140
|
-
|
143
|
+
score = page_struct.duration_ms.to_s
|
144
|
+
|
145
|
+
per_group_limit = config.max_snapshots_per_group.to_s
|
146
|
+
groups_limit = config.max_snapshot_groups.to_s
|
141
147
|
bytes = Marshal.dump(page_struct)
|
142
148
|
|
143
149
|
lua = <<~LUA
|
144
|
-
local
|
145
|
-
local
|
150
|
+
local group_zset_key = KEYS[1]
|
151
|
+
local group_hash_key = KEYS[2]
|
152
|
+
local overview_zset_key = KEYS[3]
|
153
|
+
|
146
154
|
local id = ARGV[1]
|
147
155
|
local score = tonumber(ARGV[2])
|
148
|
-
local
|
149
|
-
local
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
+
local group_name = ARGV[3]
|
157
|
+
local per_group_limit = tonumber(ARGV[4])
|
158
|
+
local groups_limit = tonumber(ARGV[5])
|
159
|
+
local prefix = ARGV[6]
|
160
|
+
local bytes = ARGV[7]
|
161
|
+
|
162
|
+
local current_group_score = redis.call("ZSCORE", overview_zset_key, group_name)
|
163
|
+
if current_group_score == false or score > tonumber(current_group_score) then
|
164
|
+
redis.call("ZADD", overview_zset_key, score, group_name)
|
165
|
+
end
|
166
|
+
|
167
|
+
local do_save = true
|
168
|
+
local overview_size = redis.call("ZCARD", overview_zset_key)
|
169
|
+
while (overview_size > groups_limit) do
|
170
|
+
local lowest_group = redis.call("ZRANGE", overview_zset_key, 0, 0)[1]
|
171
|
+
redis.call("ZREM", overview_zset_key, lowest_group)
|
172
|
+
if lowest_group == group_name then
|
173
|
+
do_save = false
|
174
|
+
else
|
175
|
+
local lowest_group_zset_key = prefix .. "-mp-group-snapshot-zset-key-" .. lowest_group
|
176
|
+
local lowest_group_hash_key = prefix .. "-mp-group-snapshot-hash-key-" .. lowest_group
|
177
|
+
redis.call("DEL", lowest_group_zset_key, lowest_group_hash_key)
|
178
|
+
end
|
179
|
+
overview_size = overview_size - 1
|
180
|
+
end
|
181
|
+
|
182
|
+
if do_save then
|
183
|
+
redis.call("ZADD", group_zset_key, score, id)
|
184
|
+
local group_size = redis.call("ZCARD", group_zset_key)
|
185
|
+
while (group_size > per_group_limit) do
|
186
|
+
local lowest_snapshot_id = redis.call("ZRANGE", group_zset_key, 0, 0)[1]
|
187
|
+
redis.call("ZREM", group_zset_key, lowest_snapshot_id)
|
188
|
+
if lowest_snapshot_id == id then
|
189
|
+
do_save = false
|
190
|
+
else
|
191
|
+
redis.call("HDEL", group_hash_key, lowest_snapshot_id)
|
192
|
+
end
|
193
|
+
group_size = group_size - 1
|
194
|
+
end
|
195
|
+
if do_save then
|
196
|
+
redis.call("HSET", group_hash_key, id, bytes)
|
197
|
+
end
|
156
198
|
end
|
157
199
|
LUA
|
158
200
|
redis.eval(
|
159
201
|
lua,
|
160
|
-
keys: [
|
161
|
-
argv: [id, score,
|
202
|
+
keys: [group_zset_key, group_hash_key, overview_zset_key],
|
203
|
+
argv: [id, score, group_name, per_group_limit, groups_limit, @prefix, bytes]
|
162
204
|
)
|
163
205
|
end
|
164
206
|
|
165
|
-
def
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
batch.map! do |id, bytes|
|
179
|
-
begin
|
180
|
-
Marshal.load(bytes)
|
181
|
-
rescue
|
182
|
-
corrupt_snapshots << id
|
183
|
-
nil
|
184
|
-
end
|
207
|
+
def fetch_snapshots_overview
|
208
|
+
overview_zset_key = snapshot_overview_zset_key
|
209
|
+
groups = redis
|
210
|
+
.zrange(overview_zset_key, 0, -1, withscores: true)
|
211
|
+
.map { |(name, worst_score)| [name, { worst_score: worst_score }] }
|
212
|
+
|
213
|
+
prefixed_group_names = groups.map { |(group_name, _)| group_snapshot_zset_key(group_name) }
|
214
|
+
metadata = redis.eval(<<~LUA, keys: prefixed_group_names)
|
215
|
+
local metadata = {}
|
216
|
+
for i, k in ipairs(KEYS) do
|
217
|
+
local best = redis.call("ZRANGE", k, 0, 0, "WITHSCORES")[2]
|
218
|
+
local count = redis.call("ZCARD", k)
|
219
|
+
metadata[i] = {best, count}
|
185
220
|
end
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
221
|
+
return metadata
|
222
|
+
LUA
|
223
|
+
groups.each.with_index do |(_, hash), index|
|
224
|
+
best, count = metadata[index]
|
225
|
+
hash[:best_score] = best.to_f
|
226
|
+
hash[:snapshots_count] = count.to_i
|
227
|
+
end
|
228
|
+
groups.to_h
|
229
|
+
end
|
230
|
+
|
231
|
+
def fetch_snapshots_group(group_name)
|
232
|
+
group_hash_key = group_snapshot_hash_key(group_name)
|
233
|
+
snapshots = []
|
234
|
+
corrupt_snapshots = []
|
235
|
+
redis.hgetall(group_hash_key).each do |id, bytes|
|
236
|
+
# rubocop:disable Security/MarshalLoad
|
237
|
+
snapshots << Marshal.load(bytes)
|
238
|
+
# rubocop:enable Security/MarshalLoad
|
239
|
+
rescue
|
240
|
+
corrupt_snapshots << id
|
190
241
|
end
|
191
242
|
if corrupt_snapshots.size > 0
|
192
|
-
|
193
|
-
redis.zrem(zset_key, corrupt_snapshots)
|
194
|
-
redis.hdel(hash_key, corrupt_snapshots)
|
195
|
-
end
|
243
|
+
cleanup_corrupt_snapshots(corrupt_snapshots, group_name)
|
196
244
|
end
|
245
|
+
snapshots
|
197
246
|
end
|
198
247
|
|
199
|
-
def load_snapshot(id)
|
200
|
-
|
201
|
-
bytes = redis.hget(
|
248
|
+
def load_snapshot(id, group_name)
|
249
|
+
group_hash_key = group_snapshot_hash_key(group_name)
|
250
|
+
bytes = redis.hget(group_hash_key, id)
|
251
|
+
return if !bytes
|
202
252
|
begin
|
253
|
+
# rubocop:disable Security/MarshalLoad
|
203
254
|
Marshal.load(bytes)
|
255
|
+
# rubocop:enable Security/MarshalLoad
|
204
256
|
rescue
|
205
|
-
|
206
|
-
redis.zrem(snapshot_zset_key(), id)
|
207
|
-
redis.hdel(hash_key, id)
|
208
|
-
end
|
257
|
+
cleanup_corrupt_snapshots([id], group_name)
|
209
258
|
nil
|
210
259
|
end
|
211
260
|
end
|
@@ -231,12 +280,20 @@ unviewed_ids: #{get_unviewed_ids(user)}
|
|
231
280
|
@snapshot_counter_key ||= "#{@prefix}-mini-profiler-snapshots-counter"
|
232
281
|
end
|
233
282
|
|
234
|
-
def
|
235
|
-
|
283
|
+
def group_snapshot_zset_key(group_name)
|
284
|
+
# if you change this key, remember to change it in the LUA script in
|
285
|
+
# the push_snapshot method as well
|
286
|
+
"#{@prefix}-mp-group-snapshot-zset-key-#{group_name}"
|
236
287
|
end
|
237
288
|
|
238
|
-
def
|
239
|
-
|
289
|
+
def group_snapshot_hash_key(group_name)
|
290
|
+
# if you change this key, remember to change it in the LUA script in
|
291
|
+
# the push_snapshot method as well
|
292
|
+
"#{@prefix}-mp-group-snapshot-hash-key-#{group_name}"
|
293
|
+
end
|
294
|
+
|
295
|
+
def snapshot_overview_zset_key
|
296
|
+
"#{@prefix}-mp-overviewgroup-snapshot-zset-key"
|
240
297
|
end
|
241
298
|
|
242
299
|
def cached_redis_eval(script, script_sha, reraise: true, argv: [], keys: [])
|
@@ -251,13 +308,45 @@ unviewed_ids: #{get_unviewed_ids(user)}
|
|
251
308
|
end
|
252
309
|
end
|
253
310
|
|
311
|
+
def cleanup_corrupt_snapshots(corrupt_snapshots_ids, group_name)
|
312
|
+
group_hash_key = group_snapshot_hash_key(group_name)
|
313
|
+
group_zset_key = group_snapshot_zset_key(group_name)
|
314
|
+
overview_zset_key = snapshot_overview_zset_key
|
315
|
+
lua = <<~LUA
|
316
|
+
local group_hash_key = KEYS[1]
|
317
|
+
local group_zset_key = KEYS[2]
|
318
|
+
local overview_zset_key = KEYS[3]
|
319
|
+
local group_name = ARGV[1]
|
320
|
+
for i, k in ipairs(ARGV) do
|
321
|
+
if k ~= group_name then
|
322
|
+
redis.call("HDEL", group_hash_key, k)
|
323
|
+
redis.call("ZREM", group_zset_key, k)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
if redis.call("ZCARD", group_zset_key) == 0 then
|
327
|
+
redis.call("ZREM", overview_zset_key, group_name)
|
328
|
+
redis.call("DEL", group_hash_key, group_zset_key)
|
329
|
+
else
|
330
|
+
local worst_score = tonumber(redis.call("ZRANGE", group_zset_key, -1, -1, "WITHSCORES")[2])
|
331
|
+
redis.call("ZADD", overview_zset_key, worst_score, group_name)
|
332
|
+
end
|
333
|
+
LUA
|
334
|
+
redis.eval(
|
335
|
+
lua,
|
336
|
+
keys: [group_hash_key, group_zset_key, overview_zset_key],
|
337
|
+
argv: [group_name, *corrupt_snapshots_ids]
|
338
|
+
)
|
339
|
+
end
|
340
|
+
|
254
341
|
# only used in tests
|
255
342
|
def wipe_snapshots_data
|
256
|
-
redis.
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
343
|
+
keys = redis.keys(group_snapshot_hash_key('*'))
|
344
|
+
keys += redis.keys(group_snapshot_zset_key('*'))
|
345
|
+
redis.del(
|
346
|
+
keys,
|
347
|
+
snapshot_overview_zset_key,
|
348
|
+
snapshot_counter_key
|
349
|
+
)
|
261
350
|
end
|
262
351
|
end
|
263
352
|
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mini_profiler/timer_struct/base'
|
4
|
+
require 'mini_profiler/timer_struct/page'
|
5
|
+
require 'mini_profiler/timer_struct/sql'
|
6
|
+
require 'mini_profiler/timer_struct/custom'
|
7
|
+
require 'mini_profiler/timer_struct/client'
|
8
|
+
require 'mini_profiler/timer_struct/request'
|