rack-mini-profiler 2.3.3 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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'
|