rack-mini-profiler 2.3.4 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -133,85 +134,127 @@ unviewed_ids: #{get_unviewed_ids(user)}
133
134
  )
134
135
  end
135
136
 
136
- def push_snapshot(page_struct, config)
137
- zset_key = snapshot_zset_key()
138
- hash_key = snapshot_hash_key()
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
139
141
 
140
142
  id = page_struct[:id]
141
- score = page_struct.duration_ms
142
- limit = config.snapshots_limit
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
143
147
  bytes = Marshal.dump(page_struct)
144
148
 
145
149
  lua = <<~LUA
146
- local zset_key = KEYS[1]
147
- local hash_key = KEYS[2]
150
+ local group_zset_key = KEYS[1]
151
+ local group_hash_key = KEYS[2]
152
+ local overview_zset_key = KEYS[3]
153
+
148
154
  local id = ARGV[1]
149
155
  local score = tonumber(ARGV[2])
150
- local bytes = ARGV[3]
151
- local limit = tonumber(ARGV[4])
152
- redis.call("ZADD", zset_key, score, id)
153
- redis.call("HSET", hash_key, id, bytes)
154
- if redis.call("ZCARD", zset_key) > limit then
155
- local lowest_snapshot_id = redis.call("ZRANGE", zset_key, 0, 0)[1]
156
- redis.call("ZREM", zset_key, lowest_snapshot_id)
157
- redis.call("HDEL", hash_key, lowest_snapshot_id)
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
158
198
  end
159
199
  LUA
160
200
  redis.eval(
161
201
  lua,
162
- keys: [zset_key, hash_key],
163
- argv: [id, score, bytes, limit]
202
+ keys: [group_zset_key, group_hash_key, overview_zset_key],
203
+ argv: [id, score, group_name, per_group_limit, groups_limit, @prefix, bytes]
164
204
  )
165
205
  end
166
206
 
167
- def fetch_snapshots(batch_size: 200, &blk)
168
- zset_key = snapshot_zset_key()
169
- hash_key = snapshot_hash_key()
170
- iteration = 0
171
- corrupt_snapshots = []
172
- while true
173
- ids = redis.zrange(
174
- zset_key,
175
- batch_size * iteration,
176
- batch_size * iteration + batch_size - 1
177
- )
178
- break if ids.size == 0
179
- batch = redis.mapped_hmget(hash_key, *ids).to_a
180
- batch.map! do |id, bytes|
181
- begin
182
- # rubocop:disable Security/MarshalLoad
183
- Marshal.load(bytes)
184
- # rubocop:enable Security/MarshalLoad
185
- rescue
186
- corrupt_snapshots << id
187
- nil
188
- 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}
189
220
  end
190
- batch.compact!
191
- blk.call(batch) if batch.size != 0
192
- break if ids.size < batch_size
193
- iteration += 1
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
194
241
  end
195
242
  if corrupt_snapshots.size > 0
196
- redis.pipelined do |pipeline|
197
- pipeline.zrem(zset_key, corrupt_snapshots)
198
- pipeline.hdel(hash_key, corrupt_snapshots)
199
- end
243
+ cleanup_corrupt_snapshots(corrupt_snapshots, group_name)
200
244
  end
245
+ snapshots
201
246
  end
202
247
 
203
- def load_snapshot(id)
204
- hash_key = snapshot_hash_key()
205
- bytes = redis.hget(hash_key, id)
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
206
252
  begin
207
253
  # rubocop:disable Security/MarshalLoad
208
254
  Marshal.load(bytes)
209
255
  # rubocop:enable Security/MarshalLoad
210
256
  rescue
211
- redis.pipelined do |pipeline|
212
- pipeline.zrem(snapshot_zset_key(), id)
213
- pipeline.hdel(hash_key, id)
214
- end
257
+ cleanup_corrupt_snapshots([id], group_name)
215
258
  nil
216
259
  end
217
260
  end
@@ -237,12 +280,20 @@ unviewed_ids: #{get_unviewed_ids(user)}
237
280
  @snapshot_counter_key ||= "#{@prefix}-mini-profiler-snapshots-counter"
238
281
  end
239
282
 
240
- def snapshot_zset_key
241
- @snapshot_zset_key ||= "#{@prefix}-mini-profiler-snapshots-zset"
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}"
242
287
  end
243
288
 
244
- def snapshot_hash_key
245
- @snapshot_hash_key ||= "#{@prefix}-mini-profiler-snapshots-hash"
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"
246
297
  end
247
298
 
248
299
  def cached_redis_eval(script, script_sha, reraise: true, argv: [], keys: [])
@@ -257,12 +308,44 @@ unviewed_ids: #{get_unviewed_ids(user)}
257
308
  end
258
309
  end
259
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
+
260
341
  # only used in tests
261
342
  def wipe_snapshots_data
343
+ keys = redis.keys(group_snapshot_hash_key('*'))
344
+ keys += redis.keys(group_snapshot_zset_key('*'))
262
345
  redis.del(
263
- snapshot_counter_key(),
264
- snapshot_zset_key(),
265
- snapshot_hash_key(),
346
+ keys,
347
+ snapshot_overview_zset_key,
348
+ snapshot_counter_key
266
349
  )
267
350
  end
268
351
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mini_profiler/storage/abstract_store'
4
+ require 'mini_profiler/storage/memcache_store'
5
+ require 'mini_profiler/storage/memory_store'
6
+ require 'mini_profiler/storage/redis_store'
7
+ require 'mini_profiler/storage/file_store'
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  module Rack
4
6
  class MiniProfiler
5
7
  module TimerStruct
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'erb'
4
+
3
5
  module Rack
4
6
  class MiniProfiler
5
7
 
@@ -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'
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Rack
4
4
  class MiniProfiler
5
- VERSION = '2.3.4'
5
+ VERSION = '3.1.0'
6
6
  SOURCE_CODE_URI = 'https://github.com/MiniProfiler/rack-mini-profiler'
7
7
  end
8
8
  end