rack-mini-profiler 2.3.4 → 3.0.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 +4 -0
- data/README.md +3 -2
- data/lib/mini_profiler/config.rb +4 -3
- data/lib/mini_profiler/profiler.rb +13 -9
- data/lib/mini_profiler/storage/abstract_store.rb +30 -57
- data/lib/mini_profiler/storage/memory_store.rb +54 -12
- data/lib/mini_profiler/storage/redis_store.rb +143 -61
- data/lib/mini_profiler/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cde7281c7a63d3d3ac5bc7605ba2cf81dedf6b598ff17ed568610a54a50b517f
|
4
|
+
data.tar.gz: 7af763d5136493c71cc4321fe54db79372e5b9aaac4c10d31bc2f27d22f324b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: abb415f975552a1256753e15ba9d6623aeb55fe4b058c775701f2b7d3a955f62d2a906a93929f1a22545f8558750d48c78f3b7e185f774c224d12e6a256a69a6
|
7
|
+
data.tar.gz: f5ff03170537e5dc17a826909d9b77609bc8d6f2390f42b29be9ff1af538332eaa33c269f7433d87142ba7e05315741b3ea5b4c69fcc76980a56c225338d843c
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 3.0.0 - 2022-02-24
|
4
|
+
|
5
|
+
- PERF: Improve snapshots page performance (#518) (introduces breaking changes to the API of `AbstractStore`, `MemoryStore` and `RedisStore`, and removes the `snapshots_limit` config option.)
|
6
|
+
|
3
7
|
## 2.3.4 - 2022-02-23
|
4
8
|
|
5
9
|
- [FEATURE] Add cookie path support for subfolder sites
|
data/README.md
CHANGED
@@ -207,7 +207,7 @@ After enabling snapshots sampling, you can see the snapshots that have been coll
|
|
207
207
|
|
208
208
|
Access to the snapshots page is restricted to only those who can see the speed badge on their own requests, see the section below this one about access control.
|
209
209
|
|
210
|
-
Mini Profiler will keep a maximum of
|
210
|
+
Mini Profiler will keep a maximum of 50 snapshot groups and a maximum of 15 snapshots per group making the default maximum number of snapshots in the system 750. The default group and per group limits can be changed via the `max_snapshot_groups` and `max_snapshots_per_group` configuration options, see the configurations table below.
|
211
211
|
|
212
212
|
#### Snapshots Transporter
|
213
213
|
|
@@ -430,7 +430,8 @@ show_total_sql_count|`false`|Displays the total number of SQL executions.
|
|
430
430
|
enable_advanced_debugging_tools|`false`|Enables sensitive debugging tools that can be used via the UI. In production we recommend keeping this disabled as memory and environment debugging tools can expose contents of memory that may contain passwords. Defaults to `true` in development.
|
431
431
|
assets_url|`nil`|See the "Register MiniProfiler's assets in the Rails assets pipeline" section above.
|
432
432
|
snapshot_every_n_requests|`-1`|Determines how frequently snapshots are taken. See the "Snapshots Sampling" above for more details.
|
433
|
-
|
433
|
+
max_snapshot_groups|`50`|Determines how many snapshot groups Mini Profiler is allowed to keep.
|
434
|
+
max_snapshots_per_group|`15`|Determines how many snapshots per group Mini Profiler is allowed to keep.
|
434
435
|
snapshot_hidden_custom_fields|`[]`|Each snapshot custom field will have a dedicated column in the UI by default. Use this config to exclude certain custom fields from having their own columns.
|
435
436
|
snapshots_transport_destination_url|`nil`|Set this config to a valid URL to enable snapshots transporter which will `POST` snapshots to the given URL. The transporter requires `snapshots_transport_auth_key` config to be set as well.
|
436
437
|
snapshots_transport_auth_key|`nil`|`POST` requests made by the snapshots transporter to the destination URL will have a `Mini-Profiler-Transport-Auth` header with the value of this config. Make sure you use a secure and random key for this config.
|
data/lib/mini_profiler/config.rb
CHANGED
@@ -40,7 +40,8 @@ module Rack
|
|
40
40
|
@skip_sql_param_names = /password/ # skips parameters with the name password by default
|
41
41
|
@enable_advanced_debugging_tools = false
|
42
42
|
@snapshot_every_n_requests = -1
|
43
|
-
@
|
43
|
+
@max_snapshot_groups = 50
|
44
|
+
@max_snapshots_per_group = 15
|
44
45
|
|
45
46
|
# ui parameters
|
46
47
|
@autorized = true
|
@@ -81,10 +82,10 @@ module Rack
|
|
81
82
|
:start_hidden, :toggle_shortcut, :html_container
|
82
83
|
|
83
84
|
# snapshot related config
|
84
|
-
attr_accessor :snapshot_every_n_requests, :
|
85
|
+
attr_accessor :snapshot_every_n_requests, :max_snapshots_per_group,
|
85
86
|
:snapshot_hidden_custom_fields, :snapshots_transport_destination_url,
|
86
87
|
:snapshots_transport_auth_key, :snapshots_redact_sql_queries,
|
87
|
-
:snapshots_transport_gzip_requests
|
88
|
+
:snapshots_transport_gzip_requests, :max_snapshot_groups
|
88
89
|
|
89
90
|
# Deprecated options
|
90
91
|
attr_accessor :use_existing_jquery
|
@@ -130,10 +130,10 @@ module Rack
|
|
130
130
|
def serve_results(env)
|
131
131
|
request = Rack::Request.new(env)
|
132
132
|
id = request.params['id']
|
133
|
-
|
134
|
-
is_snapshot =
|
133
|
+
group_name = request.params['group']
|
134
|
+
is_snapshot = group_name && group_name.size > 0
|
135
135
|
if is_snapshot
|
136
|
-
page_struct = @storage.load_snapshot(id)
|
136
|
+
page_struct = @storage.load_snapshot(id, group_name)
|
137
137
|
else
|
138
138
|
page_struct = @storage.load(id)
|
139
139
|
end
|
@@ -802,16 +802,16 @@ This is the help menu of the <a href='#{Rack::MiniProfiler::SOURCE_CODE_URI}'>ra
|
|
802
802
|
headers = { 'Content-Type' => 'text/html' }
|
803
803
|
qp = Rack::Utils.parse_nested_query(env['QUERY_STRING'])
|
804
804
|
if group_name = qp["group_name"]
|
805
|
-
list = @storage.
|
805
|
+
list = @storage.snapshots_group(group_name)
|
806
806
|
list.each do |snapshot|
|
807
|
-
snapshot[:url] = url_for_snapshot(snapshot[:id])
|
807
|
+
snapshot[:url] = url_for_snapshot(snapshot[:id], group_name)
|
808
808
|
end
|
809
809
|
data = {
|
810
810
|
group_name: group_name,
|
811
811
|
list: list
|
812
812
|
}
|
813
813
|
else
|
814
|
-
list = @storage.
|
814
|
+
list = @storage.snapshots_overview
|
815
815
|
list.each do |group|
|
816
816
|
group[:url] = url_for_snapshots_group(group[:name])
|
817
817
|
end
|
@@ -864,7 +864,7 @@ This is the help menu of the <a href='#{Rack::MiniProfiler::SOURCE_CODE_URI}'>ra
|
|
864
864
|
if defined?(Rails) && defined?(ActionController::RoutingError)
|
865
865
|
hash = Rails.application.routes.recognize_path(path, method: method)
|
866
866
|
if hash && hash[:controller] && hash[:action]
|
867
|
-
"#{
|
867
|
+
"#{hash[:controller]}##{hash[:action]}"
|
868
868
|
end
|
869
869
|
end
|
870
870
|
rescue ActionController::RoutingError
|
@@ -876,8 +876,8 @@ This is the help menu of the <a href='#{Rack::MiniProfiler::SOURCE_CODE_URI}'>ra
|
|
876
876
|
"/#{@config.base_url_path.gsub('/', '')}/snapshots?#{qs}"
|
877
877
|
end
|
878
878
|
|
879
|
-
def url_for_snapshot(id)
|
880
|
-
qs = Rack::Utils.build_query({ id: id,
|
879
|
+
def url_for_snapshot(id, group_name)
|
880
|
+
qs = Rack::Utils.build_query({ id: id, group: group_name })
|
881
881
|
"/#{@config.base_url_path.gsub('/', '')}/results?#{qs}"
|
882
882
|
end
|
883
883
|
|
@@ -902,8 +902,12 @@ This is the help menu of the <a href='#{Rack::MiniProfiler::SOURCE_CODE_URI}'>ra
|
|
902
902
|
if Rack::MiniProfiler.snapshots_transporter?
|
903
903
|
Rack::MiniProfiler::SnapshotsTransporter.transport(page_struct)
|
904
904
|
else
|
905
|
+
group_name = rails_route_from_path(page_struct[:request_path], page_struct[:request_method])
|
906
|
+
group_name ||= page_struct[:request_path]
|
907
|
+
group_name = "#{page_struct[:request_method]} #{group_name}"
|
905
908
|
@storage.push_snapshot(
|
906
909
|
page_struct,
|
910
|
+
group_name,
|
907
911
|
@config
|
908
912
|
)
|
909
913
|
end
|
@@ -45,80 +45,53 @@ module Rack
|
|
45
45
|
raise NotImplementedError.new("should_take_snapshot? is not implemented")
|
46
46
|
end
|
47
47
|
|
48
|
-
def push_snapshot(page_struct, config)
|
48
|
+
def push_snapshot(page_struct, group_name, config)
|
49
49
|
raise NotImplementedError.new("push_snapshot is not implemented")
|
50
50
|
end
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
# returns a hash where the keys are group names and the values
|
53
|
+
# are hashes that contain 3 keys:
|
54
|
+
# 1. `:worst_score` => the duration of the worst/slowest snapshot in the group (float)
|
55
|
+
# 2. `:best_score` => the duration of the best/fastest snapshot in the group (float)
|
56
|
+
# 3. `:snapshots_count` => the number of snapshots in the group (integer)
|
57
|
+
def fetch_snapshots_overview
|
58
|
+
raise NotImplementedError.new("fetch_snapshots_overview is not implemented")
|
54
59
|
end
|
55
60
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
groups[group_name][:best_score] = snapshot.duration_ms
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
groups = groups.to_a
|
61
|
+
# @param group_name [String]
|
62
|
+
# @return [Array<Rack::MiniProfiler::TimerStruct::Page>] list of snapshots of the group. Blank array if the group doesn't exist.
|
63
|
+
def fetch_snapshots_group(group_name)
|
64
|
+
raise NotImplementedError.new("fetch_snapshots_group is not implemented")
|
65
|
+
end
|
66
|
+
|
67
|
+
def load_snapshot(id, group_name)
|
68
|
+
raise NotImplementedError.new("load_snapshot is not implemented")
|
69
|
+
end
|
70
|
+
|
71
|
+
def snapshots_overview
|
72
|
+
groups = fetch_snapshots_overview.to_a
|
73
73
|
groups.sort_by! { |name, hash| hash[:worst_score] }
|
74
74
|
groups.reverse!
|
75
75
|
groups.map! { |name, hash| hash.merge(name: name) }
|
76
76
|
groups
|
77
77
|
end
|
78
78
|
|
79
|
-
def
|
79
|
+
def snapshots_group(group_name)
|
80
|
+
snapshots = fetch_snapshots_group(group_name)
|
80
81
|
data = []
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
timestamp: snapshot[:started_at],
|
90
|
-
custom_fields: snapshot[:custom_fields]
|
91
|
-
}
|
92
|
-
end
|
93
|
-
end
|
82
|
+
snapshots.each do |snapshot|
|
83
|
+
data << {
|
84
|
+
id: snapshot[:id],
|
85
|
+
duration: snapshot.duration_ms,
|
86
|
+
sql_count: snapshot[:sql_count],
|
87
|
+
timestamp: snapshot[:started_at],
|
88
|
+
custom_fields: snapshot[:custom_fields]
|
89
|
+
}
|
94
90
|
end
|
95
91
|
data.sort_by! { |s| s[:duration] }
|
96
92
|
data.reverse!
|
97
93
|
data
|
98
94
|
end
|
99
|
-
|
100
|
-
def load_snapshot(id)
|
101
|
-
raise NotImplementedError.new("load_snapshot is not implemented")
|
102
|
-
end
|
103
|
-
|
104
|
-
private
|
105
|
-
|
106
|
-
def default_snapshot_grouping(snapshot)
|
107
|
-
group_name = rails_route_from_path(snapshot[:request_path], snapshot[:request_method])
|
108
|
-
group_name ||= snapshot[:request_path]
|
109
|
-
"#{snapshot[:request_method]} #{group_name}"
|
110
|
-
end
|
111
|
-
|
112
|
-
def rails_route_from_path(path, method)
|
113
|
-
if defined?(Rails) && defined?(ActionController::RoutingError)
|
114
|
-
hash = Rails.application.routes.recognize_path(path, method: method)
|
115
|
-
if hash && hash[:controller] && hash[:action]
|
116
|
-
"#{hash[:controller]}##{hash[:action]}"
|
117
|
-
end
|
118
|
-
end
|
119
|
-
rescue ActionController::RoutingError
|
120
|
-
nil
|
121
|
-
end
|
122
95
|
end
|
123
96
|
end
|
124
97
|
end
|
@@ -53,6 +53,7 @@ module Rack
|
|
53
53
|
|
54
54
|
@token1, @token2, @cycle_at = nil
|
55
55
|
@snapshots_cycle = 0
|
56
|
+
@snapshot_groups = {}
|
56
57
|
@snapshots = []
|
57
58
|
|
58
59
|
initialize_locks
|
@@ -152,28 +153,69 @@ module Rack
|
|
152
153
|
end
|
153
154
|
end
|
154
155
|
|
155
|
-
def push_snapshot(page_struct, config)
|
156
|
+
def push_snapshot(page_struct, group_name, config)
|
156
157
|
@snapshots_lock.synchronize do
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
158
|
+
group = @snapshot_groups[group_name]
|
159
|
+
if !group
|
160
|
+
@snapshot_groups[group_name] = {
|
161
|
+
worst_score: page_struct.duration_ms,
|
162
|
+
best_score: page_struct.duration_ms,
|
163
|
+
snapshots: [page_struct]
|
164
|
+
}
|
165
|
+
if @snapshot_groups.size > config.max_snapshot_groups
|
166
|
+
group_keys = @snapshot_groups.keys
|
167
|
+
group_keys.sort_by! do |key|
|
168
|
+
@snapshot_groups[key][:worst_score]
|
169
|
+
end
|
170
|
+
group_keys.reverse!
|
171
|
+
group_keys.pop(group_keys.size - config.max_snapshot_groups)
|
172
|
+
@snapshot_groups = @snapshot_groups.slice(*group_keys)
|
173
|
+
end
|
174
|
+
else
|
175
|
+
snapshots = group[:snapshots]
|
176
|
+
snapshots << page_struct
|
177
|
+
snapshots.sort_by!(&:duration_ms)
|
178
|
+
snapshots.reverse!
|
179
|
+
if snapshots.size > config.max_snapshots_per_group
|
180
|
+
snapshots.pop(snapshots.size - config.max_snapshots_per_group)
|
181
|
+
end
|
182
|
+
group[:worst_score] = snapshots[0].duration_ms
|
183
|
+
group[:best_score] = snapshots[-1].duration_ms
|
162
184
|
end
|
163
185
|
end
|
164
186
|
end
|
165
187
|
|
166
|
-
def
|
188
|
+
def fetch_snapshots_overview
|
167
189
|
@snapshots_lock.synchronize do
|
168
|
-
|
169
|
-
|
190
|
+
groups = {}
|
191
|
+
@snapshot_groups.each do |name, group|
|
192
|
+
groups[name] = {
|
193
|
+
worst_score: group[:worst_score],
|
194
|
+
best_score: group[:best_score],
|
195
|
+
snapshots_count: group[:snapshots].size
|
196
|
+
}
|
170
197
|
end
|
198
|
+
groups
|
171
199
|
end
|
172
200
|
end
|
173
201
|
|
174
|
-
def
|
202
|
+
def fetch_snapshots_group(group_name)
|
175
203
|
@snapshots_lock.synchronize do
|
176
|
-
|
204
|
+
group = @snapshot_groups[group_name]
|
205
|
+
if group
|
206
|
+
group[:snapshots].dup
|
207
|
+
else
|
208
|
+
[]
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def load_snapshot(id, group_name)
|
214
|
+
@snapshots_lock.synchronize do
|
215
|
+
group = @snapshot_groups[group_name]
|
216
|
+
if group
|
217
|
+
group[:snapshots].find { |s| s[:id] == id }
|
218
|
+
end
|
177
219
|
end
|
178
220
|
end
|
179
221
|
|
@@ -182,7 +224,7 @@ module Rack
|
|
182
224
|
# used in tests only
|
183
225
|
def wipe_snapshots_data
|
184
226
|
@snapshots_cycle = 0
|
185
|
-
@
|
227
|
+
@snapshot_groups = {}
|
186
228
|
end
|
187
229
|
end
|
188
230
|
end
|
@@ -133,85 +133,127 @@ unviewed_ids: #{get_unviewed_ids(user)}
|
|
133
133
|
)
|
134
134
|
end
|
135
135
|
|
136
|
-
def push_snapshot(page_struct, config)
|
137
|
-
|
138
|
-
|
136
|
+
def push_snapshot(page_struct, group_name, config)
|
137
|
+
group_zset_key = group_snapshot_zset_key(group_name)
|
138
|
+
group_hash_key = group_snapshot_hash_key(group_name)
|
139
|
+
overview_zset_key = snapshot_overview_zset_key
|
139
140
|
|
140
141
|
id = page_struct[:id]
|
141
|
-
score = page_struct.duration_ms
|
142
|
-
|
142
|
+
score = page_struct.duration_ms.to_s
|
143
|
+
|
144
|
+
per_group_limit = config.max_snapshots_per_group.to_s
|
145
|
+
groups_limit = config.max_snapshot_groups.to_s
|
143
146
|
bytes = Marshal.dump(page_struct)
|
144
147
|
|
145
148
|
lua = <<~LUA
|
146
|
-
local
|
147
|
-
local
|
149
|
+
local group_zset_key = KEYS[1]
|
150
|
+
local group_hash_key = KEYS[2]
|
151
|
+
local overview_zset_key = KEYS[3]
|
152
|
+
|
148
153
|
local id = ARGV[1]
|
149
154
|
local score = tonumber(ARGV[2])
|
150
|
-
local
|
151
|
-
local
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
155
|
+
local group_name = ARGV[3]
|
156
|
+
local per_group_limit = tonumber(ARGV[4])
|
157
|
+
local groups_limit = tonumber(ARGV[5])
|
158
|
+
local prefix = ARGV[6]
|
159
|
+
local bytes = ARGV[7]
|
160
|
+
|
161
|
+
local current_group_score = redis.call("ZSCORE", overview_zset_key, group_name)
|
162
|
+
if current_group_score == false or score > tonumber(current_group_score) then
|
163
|
+
redis.call("ZADD", overview_zset_key, score, group_name)
|
164
|
+
end
|
165
|
+
|
166
|
+
local do_save = true
|
167
|
+
local overview_size = redis.call("ZCARD", overview_zset_key)
|
168
|
+
while (overview_size > groups_limit) do
|
169
|
+
local lowest_group = redis.call("ZRANGE", overview_zset_key, 0, 0)[1]
|
170
|
+
redis.call("ZREM", overview_zset_key, lowest_group)
|
171
|
+
if lowest_group == group_name then
|
172
|
+
do_save = false
|
173
|
+
else
|
174
|
+
local lowest_group_zset_key = prefix .. "-mp-group-snapshot-zset-key-" .. lowest_group
|
175
|
+
local lowest_group_hash_key = prefix .. "-mp-group-snapshot-hash-key-" .. lowest_group
|
176
|
+
redis.call("DEL", lowest_group_zset_key, lowest_group_hash_key)
|
177
|
+
end
|
178
|
+
overview_size = overview_size - 1
|
179
|
+
end
|
180
|
+
|
181
|
+
if do_save then
|
182
|
+
redis.call("ZADD", group_zset_key, score, id)
|
183
|
+
local group_size = redis.call("ZCARD", group_zset_key)
|
184
|
+
while (group_size > per_group_limit) do
|
185
|
+
local lowest_snapshot_id = redis.call("ZRANGE", group_zset_key, 0, 0)[1]
|
186
|
+
redis.call("ZREM", group_zset_key, lowest_snapshot_id)
|
187
|
+
if lowest_snapshot_id == id then
|
188
|
+
do_save = false
|
189
|
+
else
|
190
|
+
redis.call("HDEL", group_hash_key, lowest_snapshot_id)
|
191
|
+
end
|
192
|
+
group_size = group_size - 1
|
193
|
+
end
|
194
|
+
if do_save then
|
195
|
+
redis.call("HSET", group_hash_key, id, bytes)
|
196
|
+
end
|
158
197
|
end
|
159
198
|
LUA
|
160
199
|
redis.eval(
|
161
200
|
lua,
|
162
|
-
keys: [
|
163
|
-
argv: [id, score,
|
201
|
+
keys: [group_zset_key, group_hash_key, overview_zset_key],
|
202
|
+
argv: [id, score, group_name, per_group_limit, groups_limit, @prefix, bytes]
|
164
203
|
)
|
165
204
|
end
|
166
205
|
|
167
|
-
def
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
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
|
206
|
+
def fetch_snapshots_overview
|
207
|
+
overview_zset_key = snapshot_overview_zset_key
|
208
|
+
groups = redis
|
209
|
+
.zrange(overview_zset_key, 0, -1, withscores: true)
|
210
|
+
.map { |(name, worst_score)| [name, { worst_score: worst_score }] }
|
211
|
+
|
212
|
+
prefixed_group_names = groups.map { |(group_name, _)| group_snapshot_zset_key(group_name) }
|
213
|
+
metadata = redis.eval(<<~LUA, keys: prefixed_group_names)
|
214
|
+
local metadata = {}
|
215
|
+
for i, k in ipairs(KEYS) do
|
216
|
+
local best = redis.call("ZRANGE", k, 0, 0, "WITHSCORES")[2]
|
217
|
+
local count = redis.call("ZCARD", k)
|
218
|
+
metadata[i] = {best, count}
|
189
219
|
end
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
220
|
+
return metadata
|
221
|
+
LUA
|
222
|
+
groups.each.with_index do |(_, hash), index|
|
223
|
+
best, count = metadata[index]
|
224
|
+
hash[:best_score] = best.to_f
|
225
|
+
hash[:snapshots_count] = count.to_i
|
226
|
+
end
|
227
|
+
groups.to_h
|
228
|
+
end
|
229
|
+
|
230
|
+
def fetch_snapshots_group(group_name)
|
231
|
+
group_hash_key = group_snapshot_hash_key(group_name)
|
232
|
+
snapshots = []
|
233
|
+
corrupt_snapshots = []
|
234
|
+
redis.hgetall(group_hash_key).each do |id, bytes|
|
235
|
+
# rubocop:disable Security/MarshalLoad
|
236
|
+
snapshots << Marshal.load(bytes)
|
237
|
+
# rubocop:enable Security/MarshalLoad
|
238
|
+
rescue
|
239
|
+
corrupt_snapshots << id
|
194
240
|
end
|
195
241
|
if corrupt_snapshots.size > 0
|
196
|
-
|
197
|
-
pipeline.zrem(zset_key, corrupt_snapshots)
|
198
|
-
pipeline.hdel(hash_key, corrupt_snapshots)
|
199
|
-
end
|
242
|
+
cleanup_corrupt_snapshots(corrupt_snapshots, group_name)
|
200
243
|
end
|
244
|
+
snapshots
|
201
245
|
end
|
202
246
|
|
203
|
-
def load_snapshot(id)
|
204
|
-
|
205
|
-
bytes = redis.hget(
|
247
|
+
def load_snapshot(id, group_name)
|
248
|
+
group_hash_key = group_snapshot_hash_key(group_name)
|
249
|
+
bytes = redis.hget(group_hash_key, id)
|
250
|
+
return if !bytes
|
206
251
|
begin
|
207
252
|
# rubocop:disable Security/MarshalLoad
|
208
253
|
Marshal.load(bytes)
|
209
254
|
# rubocop:enable Security/MarshalLoad
|
210
255
|
rescue
|
211
|
-
|
212
|
-
pipeline.zrem(snapshot_zset_key(), id)
|
213
|
-
pipeline.hdel(hash_key, id)
|
214
|
-
end
|
256
|
+
cleanup_corrupt_snapshots([id], group_name)
|
215
257
|
nil
|
216
258
|
end
|
217
259
|
end
|
@@ -237,12 +279,20 @@ unviewed_ids: #{get_unviewed_ids(user)}
|
|
237
279
|
@snapshot_counter_key ||= "#{@prefix}-mini-profiler-snapshots-counter"
|
238
280
|
end
|
239
281
|
|
240
|
-
def
|
241
|
-
|
282
|
+
def group_snapshot_zset_key(group_name)
|
283
|
+
# if you change this key, remember to change it in the LUA script in
|
284
|
+
# the push_snapshot method as well
|
285
|
+
"#{@prefix}-mp-group-snapshot-zset-key-#{group_name}"
|
242
286
|
end
|
243
287
|
|
244
|
-
def
|
245
|
-
|
288
|
+
def group_snapshot_hash_key(group_name)
|
289
|
+
# if you change this key, remember to change it in the LUA script in
|
290
|
+
# the push_snapshot method as well
|
291
|
+
"#{@prefix}-mp-group-snapshot-hash-key-#{group_name}"
|
292
|
+
end
|
293
|
+
|
294
|
+
def snapshot_overview_zset_key
|
295
|
+
"#{@prefix}-mp-overviewgroup-snapshot-zset-key"
|
246
296
|
end
|
247
297
|
|
248
298
|
def cached_redis_eval(script, script_sha, reraise: true, argv: [], keys: [])
|
@@ -257,12 +307,44 @@ unviewed_ids: #{get_unviewed_ids(user)}
|
|
257
307
|
end
|
258
308
|
end
|
259
309
|
|
310
|
+
def cleanup_corrupt_snapshots(corrupt_snapshots_ids, group_name)
|
311
|
+
group_hash_key = group_snapshot_hash_key(group_name)
|
312
|
+
group_zset_key = group_snapshot_zset_key(group_name)
|
313
|
+
overview_zset_key = snapshot_overview_zset_key
|
314
|
+
lua = <<~LUA
|
315
|
+
local group_hash_key = KEYS[1]
|
316
|
+
local group_zset_key = KEYS[2]
|
317
|
+
local overview_zset_key = KEYS[3]
|
318
|
+
local group_name = ARGV[1]
|
319
|
+
for i, k in ipairs(ARGV) do
|
320
|
+
if k ~= group_name then
|
321
|
+
redis.call("HDEL", group_hash_key, k)
|
322
|
+
redis.call("ZREM", group_zset_key, k)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
if redis.call("ZCARD", group_zset_key) == 0 then
|
326
|
+
redis.call("ZREM", overview_zset_key, group_name)
|
327
|
+
redis.call("DEL", group_hash_key, group_zset_key)
|
328
|
+
else
|
329
|
+
local worst_score = tonumber(redis.call("ZRANGE", group_zset_key, -1, -1, "WITHSCORES")[2])
|
330
|
+
redis.call("ZADD", overview_zset_key, worst_score, group_name)
|
331
|
+
end
|
332
|
+
LUA
|
333
|
+
redis.eval(
|
334
|
+
lua,
|
335
|
+
keys: [group_hash_key, group_zset_key, overview_zset_key],
|
336
|
+
argv: [group_name, *corrupt_snapshots_ids]
|
337
|
+
)
|
338
|
+
end
|
339
|
+
|
260
340
|
# only used in tests
|
261
341
|
def wipe_snapshots_data
|
342
|
+
keys = redis.keys(group_snapshot_hash_key('*'))
|
343
|
+
keys += redis.keys(group_snapshot_zset_key('*'))
|
262
344
|
redis.del(
|
263
|
-
|
264
|
-
|
265
|
-
|
345
|
+
keys,
|
346
|
+
snapshot_overview_zset_key,
|
347
|
+
snapshot_counter_key
|
266
348
|
)
|
267
349
|
end
|
268
350
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-mini-profiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2022-02-
|
13
|
+
date: 2022-02-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rack
|