piko-sharp-kit 0.0.1
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 +7 -0
- data/piko-sharp-kit.gemspec +11 -0
- data/rack-mini-profiler-4.0.1/CHANGELOG.md +424 -0
- data/rack-mini-profiler-4.0.1/README.md +542 -0
- data/rack-mini-profiler-4.0.1/lib/enable_rails_patches.rb +5 -0
- data/rack-mini-profiler-4.0.1/lib/generators/rack_mini_profiler/USAGE +9 -0
- data/rack-mini-profiler-4.0.1/lib/generators/rack_mini_profiler/install_generator.rb +13 -0
- data/rack-mini-profiler-4.0.1/lib/generators/rack_mini_profiler/templates/rack_mini_profiler.rb +8 -0
- data/rack-mini-profiler-4.0.1/lib/generators/rack_profiler/USAGE +2 -0
- data/rack-mini-profiler-4.0.1/lib/generators/rack_profiler/install_generator.rb +16 -0
- data/rack-mini-profiler-4.0.1/lib/html/dot.1.1.2.min.js +2 -0
- data/rack-mini-profiler-4.0.1/lib/html/includes.css +474 -0
- data/rack-mini-profiler-4.0.1/lib/html/includes.js +1520 -0
- data/rack-mini-profiler-4.0.1/lib/html/includes.scss +698 -0
- data/rack-mini-profiler-4.0.1/lib/html/includes.tmpl +333 -0
- data/rack-mini-profiler-4.0.1/lib/html/pretty-print.js +810 -0
- data/rack-mini-profiler-4.0.1/lib/html/profile_handler.js +1 -0
- data/rack-mini-profiler-4.0.1/lib/html/rack-mini-profiler.css +3 -0
- data/rack-mini-profiler-4.0.1/lib/html/rack-mini-profiler.js +2 -0
- data/rack-mini-profiler-4.0.1/lib/html/share.html +10 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/LICENSE +21 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/README.md +3 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/demangle-cpp.1768f4cc.js +4 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/favicon-16x16.f74b3187.png +0 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/favicon-32x32.bc503437.png +0 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/file-format-schema.json +324 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/fonts/source-code-pro-regular.css +8 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/fonts/source-code-pro-v13-regular.woff +0 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/fonts/source-code-pro-v13-regular.woff2 +0 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/import.8ae8aa3d.js +123 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/index.html +2 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/release.txt +3 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/reset.8c46b7a1.css +2 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/source-map.438fa06b.js +24 -0
- data/rack-mini-profiler-4.0.1/lib/html/speedscope/speedscope.f27db165.js +193 -0
- data/rack-mini-profiler-4.0.1/lib/html/vendor.js +848 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/actions.rb +157 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/asset_version.rb +6 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/client_settings.rb +127 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/config.rb +146 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/context.rb +14 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/gc_profiler.rb +158 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/profiling_methods.rb +167 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/snapshots_transporter.rb +111 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/abstract_store.rb +97 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/file_store.rb +179 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/memcache_store.rb +105 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/memory_store.rb +233 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/redis_store.rb +353 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage.rb +7 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/base.rb +39 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/client.rb +90 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/custom.rb +25 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/page.rb +146 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/request.rb +186 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/sql.rb +89 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct.rb +8 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/version.rb +8 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler/views.rb +198 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler.rb +670 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler_rails/railtie.rb +234 -0
- data/rack-mini-profiler-4.0.1/lib/mini_profiler_rails/railtie_methods.rb +61 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/activerecord.rb +44 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/mongo.rb +18 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/moped.rb +14 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/mysql2/alias_method.rb +30 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/mysql2/prepend.rb +34 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/mysql2.rb +7 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/neo4j.rb +16 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/nobrainer.rb +31 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/oracle_enhanced.rb +70 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/pg/alias_method.rb +121 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/pg/prepend.rb +115 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/pg.rb +7 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/plucky.rb +49 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/riak.rb +105 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/rsolr.rb +26 -0
- data/rack-mini-profiler-4.0.1/lib/patches/db/sequel.rb +12 -0
- data/rack-mini-profiler-4.0.1/lib/patches/net_patches.rb +27 -0
- data/rack-mini-profiler-4.0.1/lib/patches/sql_patches.rb +74 -0
- data/rack-mini-profiler-4.0.1/lib/prepend_mysql2_patch.rb +5 -0
- data/rack-mini-profiler-4.0.1/lib/prepend_net_http_patch.rb +5 -0
- data/rack-mini-profiler-4.0.1/lib/prepend_pg_patch.rb +5 -0
- data/rack-mini-profiler-4.0.1/lib/rack-mini-profiler.rb +13 -0
- data/rack-mini-profiler-4.0.1/rack-mini-profiler.gemspec +49 -0
- metadata +124 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rack
|
|
4
|
+
class MiniProfiler
|
|
5
|
+
module ProfilingMethods
|
|
6
|
+
|
|
7
|
+
def record_sql(query, elapsed_ms, params = nil, cached = nil)
|
|
8
|
+
return unless current && current.current_timer
|
|
9
|
+
c = current
|
|
10
|
+
c.current_timer.add_sql(
|
|
11
|
+
redact_sql_queries? ? nil : query,
|
|
12
|
+
elapsed_ms,
|
|
13
|
+
c.page_struct,
|
|
14
|
+
redact_sql_queries? ? nil : params,
|
|
15
|
+
c.skip_backtrace,
|
|
16
|
+
c.full_backtrace,
|
|
17
|
+
cached
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def report_reader_duration(elapsed_ms, row_count = nil, class_name = nil)
|
|
22
|
+
current&.current_timer&.report_reader_duration(elapsed_ms, row_count, class_name)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def start_step(name)
|
|
26
|
+
return unless current
|
|
27
|
+
parent_timer = current.current_timer
|
|
28
|
+
current.current_timer = current_timer = current.current_timer.add_child(name)
|
|
29
|
+
[current_timer, parent_timer]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def finish_step(obj)
|
|
33
|
+
return unless obj && current
|
|
34
|
+
current_timer, parent_timer = obj
|
|
35
|
+
current_timer.record_time
|
|
36
|
+
current.current_timer = parent_timer
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# perform a profiling step on given block
|
|
40
|
+
def step(name, opts = nil)
|
|
41
|
+
if current
|
|
42
|
+
parent_timer = current.current_timer
|
|
43
|
+
current.current_timer = current_timer = current.current_timer.add_child(name)
|
|
44
|
+
begin
|
|
45
|
+
yield if block_given?
|
|
46
|
+
ensure
|
|
47
|
+
current_timer.record_time
|
|
48
|
+
current.current_timer = parent_timer
|
|
49
|
+
end
|
|
50
|
+
else
|
|
51
|
+
yield if block_given?
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def unprofile_method(klass, method)
|
|
56
|
+
|
|
57
|
+
clean = clean_method_name(method)
|
|
58
|
+
|
|
59
|
+
with_profiling = ("#{clean}_with_mini_profiler").intern
|
|
60
|
+
without_profiling = ("#{clean}_without_mini_profiler").intern
|
|
61
|
+
|
|
62
|
+
if klass.send :method_defined?, with_profiling
|
|
63
|
+
klass.send :alias_method, method, without_profiling
|
|
64
|
+
klass.send :remove_method, with_profiling
|
|
65
|
+
klass.send :remove_method, without_profiling
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def counter_method(klass, method, &blk)
|
|
70
|
+
self.profile_method(klass, method, :counter, &blk)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def uncounter_method(klass, method)
|
|
74
|
+
self.unprofile_method(klass, method)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def profile_method(klass, method, type = :profile, &blk)
|
|
78
|
+
default_name = type == :counter ? method.to_s : klass.to_s + " " + method.to_s
|
|
79
|
+
clean = clean_method_name(method)
|
|
80
|
+
|
|
81
|
+
with_profiling = ("#{clean}_with_mini_profiler").intern
|
|
82
|
+
without_profiling = ("#{clean}_without_mini_profiler").intern
|
|
83
|
+
|
|
84
|
+
if klass.send :method_defined?, with_profiling
|
|
85
|
+
return # dont double profile
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
klass.send :alias_method, without_profiling, method
|
|
89
|
+
klass.send :define_method, with_profiling do |*args, &orig|
|
|
90
|
+
return self.send without_profiling, *args, &orig unless Rack::MiniProfiler.current
|
|
91
|
+
|
|
92
|
+
name = default_name
|
|
93
|
+
if blk
|
|
94
|
+
name =
|
|
95
|
+
if respond_to?(:instance_exec)
|
|
96
|
+
instance_exec(*args, &blk)
|
|
97
|
+
else
|
|
98
|
+
# deprecated in Rails 4.x
|
|
99
|
+
blk.bind(self).call(*args)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
parent_timer = Rack::MiniProfiler.current.current_timer
|
|
104
|
+
|
|
105
|
+
if type == :counter
|
|
106
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
107
|
+
begin
|
|
108
|
+
self.send without_profiling, *args, &orig
|
|
109
|
+
ensure
|
|
110
|
+
duration_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).to_f * 1000
|
|
111
|
+
parent_timer.add_custom(name, duration_ms, Rack::MiniProfiler.current.page_struct)
|
|
112
|
+
end
|
|
113
|
+
else
|
|
114
|
+
Rack::MiniProfiler.current.current_timer = current_timer = parent_timer.add_child(name)
|
|
115
|
+
begin
|
|
116
|
+
self.send without_profiling, *args, &orig
|
|
117
|
+
ensure
|
|
118
|
+
current_timer.record_time
|
|
119
|
+
Rack::MiniProfiler.current.current_timer = parent_timer
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
if klass.respond_to?(:ruby2_keywords, true)
|
|
124
|
+
klass.send(:ruby2_keywords, with_profiling)
|
|
125
|
+
end
|
|
126
|
+
klass.send :alias_method, method, with_profiling
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def profile_singleton_method(klass, method, type = :profile, &blk)
|
|
130
|
+
profile_method(klass.singleton_class, method, type, &blk)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def unprofile_singleton_method(klass, method)
|
|
134
|
+
unprofile_method(klass.singleton_class, method)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Add a custom timing. These are displayed similar to SQL/query time in
|
|
138
|
+
# columns expanding to the right.
|
|
139
|
+
#
|
|
140
|
+
# type - String counter type. Each distinct type gets its own column.
|
|
141
|
+
# duration_ms - Duration of the call in ms. Either this or a block must be
|
|
142
|
+
# given but not both.
|
|
143
|
+
#
|
|
144
|
+
# When a block is given, calculate the duration by yielding to the block
|
|
145
|
+
# and keeping a record of its run time.
|
|
146
|
+
#
|
|
147
|
+
# Returns the result of the block, or nil when no block is given.
|
|
148
|
+
def counter(type, duration_ms = nil)
|
|
149
|
+
result = nil
|
|
150
|
+
if block_given?
|
|
151
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
152
|
+
result = yield
|
|
153
|
+
duration_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).to_f * 1000
|
|
154
|
+
end
|
|
155
|
+
return result if current.nil? || !request_authorized?
|
|
156
|
+
current.current_timer.add_custom(type, duration_ms, current.page_struct)
|
|
157
|
+
result
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
private
|
|
161
|
+
|
|
162
|
+
def clean_method_name(method)
|
|
163
|
+
method.to_s.gsub(/[\?\!]/, "")
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class ::Rack::MiniProfiler::SnapshotsTransporter
|
|
4
|
+
@@transported_snapshots_count = 0
|
|
5
|
+
@@successful_http_requests_count = 0
|
|
6
|
+
@@failed_http_requests_count = 0
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def transported_snapshots_count
|
|
10
|
+
@@transported_snapshots_count
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def successful_http_requests_count
|
|
14
|
+
@@successful_http_requests_count
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def failed_http_requests_count
|
|
18
|
+
@@failed_http_requests_count
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def transport(snapshot)
|
|
22
|
+
@transporter ||= self.new(Rack::MiniProfiler.config)
|
|
23
|
+
@transporter.ship(snapshot)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
attr_reader :buffer
|
|
28
|
+
attr_accessor :max_buffer_size, :gzip_requests
|
|
29
|
+
|
|
30
|
+
def initialize(config)
|
|
31
|
+
@uri = URI(config.snapshots_transport_destination_url)
|
|
32
|
+
@auth_key = config.snapshots_transport_auth_key
|
|
33
|
+
@gzip_requests = config.snapshots_transport_gzip_requests
|
|
34
|
+
@thread = nil
|
|
35
|
+
@thread_mutex = Mutex.new
|
|
36
|
+
@buffer = []
|
|
37
|
+
@buffer_mutex = Mutex.new
|
|
38
|
+
@max_buffer_size = 100
|
|
39
|
+
@consecutive_failures_count = 0
|
|
40
|
+
@testing = false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def ship(snapshot)
|
|
44
|
+
@buffer_mutex.synchronize do
|
|
45
|
+
@buffer << snapshot
|
|
46
|
+
@buffer.shift if @buffer.size > @max_buffer_size
|
|
47
|
+
end
|
|
48
|
+
@thread_mutex.synchronize { start_thread }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def flush_buffer
|
|
52
|
+
buffer_content = @buffer_mutex.synchronize do
|
|
53
|
+
@buffer.dup if @buffer.size > 0
|
|
54
|
+
end
|
|
55
|
+
if buffer_content
|
|
56
|
+
headers = {
|
|
57
|
+
'Content-Type' => 'application/json',
|
|
58
|
+
'Mini-Profiler-Transport-Auth' => @auth_key
|
|
59
|
+
}
|
|
60
|
+
json = { snapshots: buffer_content }.to_json
|
|
61
|
+
body = if @gzip_requests
|
|
62
|
+
require 'zlib'
|
|
63
|
+
io = StringIO.new
|
|
64
|
+
gzip_writer = Zlib::GzipWriter.new(io)
|
|
65
|
+
gzip_writer.write(json)
|
|
66
|
+
gzip_writer.close
|
|
67
|
+
headers['Content-Encoding'] = 'gzip'
|
|
68
|
+
io.string
|
|
69
|
+
else
|
|
70
|
+
json
|
|
71
|
+
end
|
|
72
|
+
request = Net::HTTP::Post.new(@uri, headers)
|
|
73
|
+
request.body = body
|
|
74
|
+
http = Net::HTTP.new(@uri.hostname, @uri.port)
|
|
75
|
+
http.use_ssl = @uri.scheme == 'https'
|
|
76
|
+
res = http.request(request)
|
|
77
|
+
if res.code.to_i == 200
|
|
78
|
+
@@successful_http_requests_count += 1
|
|
79
|
+
@@transported_snapshots_count += buffer_content.size
|
|
80
|
+
@buffer_mutex.synchronize do
|
|
81
|
+
@buffer -= buffer_content
|
|
82
|
+
end
|
|
83
|
+
@consecutive_failures_count = 0
|
|
84
|
+
else
|
|
85
|
+
@@failed_http_requests_count += 1
|
|
86
|
+
@consecutive_failures_count += 1
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def requests_interval
|
|
92
|
+
[30 + backoff_delay, 60 * 60].min
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def backoff_delay
|
|
98
|
+
return 0 if @consecutive_failures_count == 0
|
|
99
|
+
2**@consecutive_failures_count
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def start_thread
|
|
103
|
+
return if @thread&.alive? || @testing
|
|
104
|
+
@thread = Thread.new do
|
|
105
|
+
while true
|
|
106
|
+
sleep requests_interval
|
|
107
|
+
flush_buffer
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rack
|
|
4
|
+
class MiniProfiler
|
|
5
|
+
class AbstractStore
|
|
6
|
+
|
|
7
|
+
# maximum age of allowed tokens before cycling in seconds
|
|
8
|
+
MAX_TOKEN_AGE = 1800
|
|
9
|
+
|
|
10
|
+
def save(page_struct)
|
|
11
|
+
raise NotImplementedError.new("save is not implemented")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def load(id)
|
|
15
|
+
raise NotImplementedError.new("load is not implemented")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def set_unviewed(user, id)
|
|
19
|
+
raise NotImplementedError.new("set_unviewed is not implemented")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def set_viewed(user, id)
|
|
23
|
+
raise NotImplementedError.new("set_viewed is not implemented")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def set_all_unviewed(user, ids)
|
|
27
|
+
raise NotImplementedError.new("set_all_unviewed is not implemented")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def get_unviewed_ids(user)
|
|
31
|
+
raise NotImplementedError.new("get_unviewed_ids is not implemented")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def diagnostics(user)
|
|
35
|
+
# this is opt in, no need to explode if not implemented
|
|
36
|
+
""
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# a list of tokens that are permitted to access profiler in explicit mode
|
|
40
|
+
def allowed_tokens
|
|
41
|
+
raise NotImplementedError.new("allowed_tokens is not implemented")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def should_take_snapshot?(period)
|
|
45
|
+
raise NotImplementedError.new("should_take_snapshot? is not implemented")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def push_snapshot(page_struct, group_name, config)
|
|
49
|
+
raise NotImplementedError.new("push_snapshot is not implemented")
|
|
50
|
+
end
|
|
51
|
+
|
|
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")
|
|
59
|
+
end
|
|
60
|
+
|
|
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
|
+
groups.sort_by! { |name, hash| hash[:worst_score] }
|
|
74
|
+
groups.reverse!
|
|
75
|
+
groups.map! { |name, hash| hash.merge(name: name) }
|
|
76
|
+
groups
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def snapshots_group(group_name)
|
|
80
|
+
snapshots = fetch_snapshots_group(group_name)
|
|
81
|
+
data = []
|
|
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
|
+
}
|
|
90
|
+
end
|
|
91
|
+
data.sort_by! { |s| s[:duration] }
|
|
92
|
+
data.reverse!
|
|
93
|
+
data
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Rack
|
|
6
|
+
class MiniProfiler
|
|
7
|
+
class FileStore < AbstractStore
|
|
8
|
+
|
|
9
|
+
# Sub-class thread so we have a named thread (useful for debugging in Thread.list).
|
|
10
|
+
class CacheCleanupThread < Thread
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class FileCache
|
|
14
|
+
def initialize(path, prefix)
|
|
15
|
+
@path = path
|
|
16
|
+
@prefix = prefix
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def [](key)
|
|
20
|
+
begin
|
|
21
|
+
data = ::File.open(path(key), "rb") { |f| f.read }
|
|
22
|
+
# rubocop:disable Security/MarshalLoad
|
|
23
|
+
Marshal.load data
|
|
24
|
+
# rubocop:enable Security/MarshalLoad
|
|
25
|
+
rescue
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def []=(key, val)
|
|
31
|
+
::File.open(path(key), "wb+") do |f|
|
|
32
|
+
f.sync = true
|
|
33
|
+
f.write Marshal.dump(val)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
if Gem.win_platform?
|
|
39
|
+
def path(key)
|
|
40
|
+
@path.dup << "/" << @prefix << "_" << key.gsub(/:/, '_')
|
|
41
|
+
end
|
|
42
|
+
else
|
|
43
|
+
def path(key)
|
|
44
|
+
@path.dup << "/" << @prefix << "_" << key
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
EXPIRES_IN_SECONDS = 60 * 60 * 24
|
|
50
|
+
|
|
51
|
+
def initialize(args = nil)
|
|
52
|
+
args ||= {}
|
|
53
|
+
@path = args[:path]
|
|
54
|
+
@expires_in_seconds = args[:expires_in] || EXPIRES_IN_SECONDS
|
|
55
|
+
raise ArgumentError.new :path unless @path
|
|
56
|
+
FileUtils.mkdir_p(@path) unless ::File.exist?(@path)
|
|
57
|
+
|
|
58
|
+
@timer_struct_cache = FileCache.new(@path, "mp_timers")
|
|
59
|
+
@timer_struct_lock = Mutex.new
|
|
60
|
+
@user_view_cache = FileCache.new(@path, "mp_views")
|
|
61
|
+
@user_view_lock = Mutex.new
|
|
62
|
+
|
|
63
|
+
@auth_token_cache = FileCache.new(@path, "tokens")
|
|
64
|
+
@auth_token_lock = Mutex.new
|
|
65
|
+
|
|
66
|
+
me = self
|
|
67
|
+
t = CacheCleanupThread.new do
|
|
68
|
+
interval = 10
|
|
69
|
+
cleanup_cache_cycle = 3600
|
|
70
|
+
cycle_count = 1
|
|
71
|
+
|
|
72
|
+
begin
|
|
73
|
+
until Thread.current[:should_exit] do
|
|
74
|
+
# TODO: a sane retry count before bailing
|
|
75
|
+
|
|
76
|
+
# We don't want to hit the filesystem every 10s to clean up the cache so we need to do a bit of
|
|
77
|
+
# accounting to avoid sleeping that entire time. We don't want to sleep for the entire period because
|
|
78
|
+
# it means the thread will stay live in hot deployment scenarios, keeping a potentially large memory
|
|
79
|
+
# graph from being garbage collected upon undeploy.
|
|
80
|
+
if cycle_count * interval >= cleanup_cache_cycle
|
|
81
|
+
cycle_count = 1
|
|
82
|
+
me.cleanup_cache
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
sleep(interval)
|
|
86
|
+
cycle_count += 1
|
|
87
|
+
end
|
|
88
|
+
rescue
|
|
89
|
+
# don't crash the thread, we can clean up next time
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
at_exit { t[:should_exit] = true }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def save(page_struct)
|
|
97
|
+
@timer_struct_lock.synchronize {
|
|
98
|
+
@timer_struct_cache[page_struct[:id]] = page_struct
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def load(id)
|
|
103
|
+
@timer_struct_lock.synchronize {
|
|
104
|
+
@timer_struct_cache[id]
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def set_unviewed(user, id)
|
|
109
|
+
@user_view_lock.synchronize {
|
|
110
|
+
current = @user_view_cache[user]
|
|
111
|
+
current = [] unless Array === current
|
|
112
|
+
current << id
|
|
113
|
+
@user_view_cache[user] = current.uniq
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def set_viewed(user, id)
|
|
118
|
+
@user_view_lock.synchronize {
|
|
119
|
+
@user_view_cache[user] ||= []
|
|
120
|
+
current = @user_view_cache[user]
|
|
121
|
+
current = [] unless Array === current
|
|
122
|
+
current.delete(id)
|
|
123
|
+
@user_view_cache[user] = current.uniq
|
|
124
|
+
}
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def set_all_unviewed(user, ids)
|
|
128
|
+
@user_view_lock.synchronize {
|
|
129
|
+
@user_view_cache[user] = ids.uniq
|
|
130
|
+
}
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def get_unviewed_ids(user)
|
|
134
|
+
@user_view_lock.synchronize {
|
|
135
|
+
@user_view_cache[user]
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def flush_tokens
|
|
140
|
+
@auth_token_lock.synchronize {
|
|
141
|
+
@auth_token_cache[""] = nil
|
|
142
|
+
}
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def allowed_tokens
|
|
146
|
+
@auth_token_lock.synchronize {
|
|
147
|
+
token1, token2, cycle_at = @auth_token_cache[""]
|
|
148
|
+
|
|
149
|
+
unless cycle_at && (Float === cycle_at) && (cycle_at > Process.clock_gettime(Process::CLOCK_MONOTONIC))
|
|
150
|
+
token2 = token1
|
|
151
|
+
token1 = SecureRandom.hex
|
|
152
|
+
cycle_at = Process.clock_gettime(Process::CLOCK_MONOTONIC) + Rack::MiniProfiler::AbstractStore::MAX_TOKEN_AGE
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
@auth_token_cache[""] = [token1, token2, cycle_at]
|
|
156
|
+
|
|
157
|
+
[token1, token2].compact
|
|
158
|
+
}
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def cleanup_cache
|
|
162
|
+
files = Dir.entries(@path)
|
|
163
|
+
@timer_struct_lock.synchronize {
|
|
164
|
+
files.each do |f|
|
|
165
|
+
f = @path + '/' + f
|
|
166
|
+
::File.delete f if ::File.basename(f) =~ (/^mp_timers/) && ((Time.now - ::File.mtime(f)) > @expires_in_seconds)
|
|
167
|
+
end
|
|
168
|
+
}
|
|
169
|
+
@user_view_lock.synchronize {
|
|
170
|
+
files.each do |f|
|
|
171
|
+
f = @path + '/' + f
|
|
172
|
+
::File.delete f if ::File.basename(f) =~ (/^mp_views/) && ((Time.now - ::File.mtime(f)) > @expires_in_seconds)
|
|
173
|
+
end
|
|
174
|
+
}
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rack
|
|
4
|
+
class MiniProfiler
|
|
5
|
+
class MemcacheStore < AbstractStore
|
|
6
|
+
|
|
7
|
+
EXPIRES_IN_SECONDS = 60 * 60 * 24
|
|
8
|
+
MAX_RETRIES = 10
|
|
9
|
+
|
|
10
|
+
def initialize(args = nil)
|
|
11
|
+
require 'dalli' unless defined? Dalli
|
|
12
|
+
args ||= {}
|
|
13
|
+
|
|
14
|
+
@prefix = args[:prefix] || "MPMemcacheStore"
|
|
15
|
+
@prefix += "-#{Rack::MiniProfiler::VERSION}"
|
|
16
|
+
|
|
17
|
+
@client = args[:client] || Dalli::Client.new
|
|
18
|
+
@expires_in_seconds = args[:expires_in] || EXPIRES_IN_SECONDS
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def alive?
|
|
22
|
+
begin
|
|
23
|
+
@client.alive!
|
|
24
|
+
true
|
|
25
|
+
rescue Dalli::RingError
|
|
26
|
+
false
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def save(page_struct)
|
|
31
|
+
@client.set("#{@prefix}#{page_struct[:id]}", Marshal::dump(page_struct), @expires_in_seconds)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def load(id)
|
|
35
|
+
raw = @client.get("#{@prefix}#{id}")
|
|
36
|
+
# rubocop:disable Security/MarshalLoad
|
|
37
|
+
Marshal.load(raw) if raw
|
|
38
|
+
# rubocop:enable Security/MarshalLoad
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def set_unviewed(user, id)
|
|
42
|
+
@client.add("#{@prefix}-#{user}-v", [], @expires_in_seconds)
|
|
43
|
+
MAX_RETRIES.times do
|
|
44
|
+
break if @client.cas("#{@prefix}-#{user}-v", @expires_in_seconds) do |ids|
|
|
45
|
+
ids << id if !ids.include?(id)
|
|
46
|
+
ids
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def set_viewed(user, id)
|
|
52
|
+
@client.add("#{@prefix}-#{user}-v", [], @expires_in_seconds)
|
|
53
|
+
MAX_RETRIES.times do
|
|
54
|
+
break if @client.cas("#{@prefix}-#{user}-v", @expires_in_seconds) do |ids|
|
|
55
|
+
ids.delete id
|
|
56
|
+
ids
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def set_all_unviewed(user, ids)
|
|
62
|
+
@client.set("#{@prefix}-#{user}-v", ids, @expires_in_seconds)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def get_unviewed_ids(user)
|
|
66
|
+
@client.get("#{@prefix}-#{user}-v") || []
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def flush_tokens
|
|
70
|
+
@client.set("#{@prefix}-tokens", nil)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def allowed_tokens
|
|
74
|
+
|
|
75
|
+
token_info = @client.get("#{@prefix}-tokens")
|
|
76
|
+
key1, key2, cycle_at = nil
|
|
77
|
+
|
|
78
|
+
if token_info
|
|
79
|
+
# rubocop:disable Security/MarshalLoad
|
|
80
|
+
key1, key2, cycle_at = Marshal.load(token_info)
|
|
81
|
+
# rubocop:enable Security/MarshalLoad
|
|
82
|
+
|
|
83
|
+
key1 = nil unless key1 && key1.length == 32
|
|
84
|
+
key2 = nil unless key2 && key2.length == 32
|
|
85
|
+
|
|
86
|
+
if key1 && cycle_at && (cycle_at > Process.clock_gettime(Process::CLOCK_MONOTONIC))
|
|
87
|
+
return [key1, key2].compact
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
timeout = Rack::MiniProfiler::AbstractStore::MAX_TOKEN_AGE
|
|
92
|
+
|
|
93
|
+
# cycle keys
|
|
94
|
+
key2 = key1
|
|
95
|
+
key1 = SecureRandom.hex
|
|
96
|
+
cycle_at = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
|
|
97
|
+
|
|
98
|
+
@client.set("#{@prefix}-tokens", Marshal::dump([key1, key2, cycle_at]))
|
|
99
|
+
|
|
100
|
+
[key1, key2].compact
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|