rack-mini-profiler 2.0.3 → 2.3.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 +27 -0
- data/README.md +43 -5
- data/lib/html/includes.css +38 -0
- data/lib/html/includes.js +265 -175
- data/lib/html/includes.scss +35 -4
- data/lib/html/includes.tmpl +92 -2
- data/lib/html/profile_handler.js +1 -1
- data/lib/html/rack-mini-profiler.css +3 -0
- data/lib/html/rack-mini-profiler.js +2 -0
- data/lib/html/speedscope/LICENSE +21 -0
- data/lib/html/speedscope/README.md +3 -0
- data/lib/html/speedscope/demangle-cpp.1768f4cc.js +4 -0
- data/lib/html/speedscope/favicon-16x16.f74b3187.png +0 -0
- data/lib/html/speedscope/favicon-32x32.bc503437.png +0 -0
- data/lib/html/speedscope/file-format-schema.json +324 -0
- data/lib/html/speedscope/import.cf0fa83f.js +115 -0
- data/lib/html/speedscope/index.html +2 -0
- data/lib/html/speedscope/release.txt +3 -0
- data/lib/html/speedscope/reset.8c46b7a1.css +2 -0
- data/lib/html/speedscope/source-map.438fa06b.js +24 -0
- data/lib/html/speedscope/speedscope.44364064.js +200 -0
- data/lib/html/vendor.js +10 -2
- data/lib/mini_profiler/asset_version.rb +1 -1
- data/lib/mini_profiler/client_settings.rb +3 -2
- data/lib/mini_profiler/config.rb +24 -2
- data/lib/mini_profiler/profiler.rb +214 -22
- data/lib/mini_profiler/profiling_methods.rb +8 -1
- data/lib/mini_profiler/snapshots_transporter.rb +109 -0
- data/lib/mini_profiler/storage/abstract_store.rb +78 -0
- data/lib/mini_profiler/storage/memory_store.rb +54 -5
- data/lib/mini_profiler/storage/redis_store.rb +134 -0
- data/lib/mini_profiler/timer_struct/page.rb +52 -2
- data/lib/mini_profiler/timer_struct/sql.rb +2 -2
- data/lib/mini_profiler/version.rb +1 -1
- data/lib/mini_profiler_rails/railtie.rb +11 -0
- data/lib/mini_profiler_rails/railtie_methods.rb +1 -1
- data/lib/rack-mini-profiler.rb +1 -0
- data/rack-mini-profiler.gemspec +5 -4
- metadata +43 -14
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'cgi'
|
4
|
+
|
3
5
|
module Rack
|
4
6
|
class MiniProfiler
|
5
7
|
class << self
|
@@ -38,9 +40,21 @@ module Rack
|
|
38
40
|
|
39
41
|
def current=(c)
|
40
42
|
# we use TLS cause we need access to this from sql blocks and code blocks that have no access to env
|
43
|
+
Thread.current[:mini_profiler_snapshot_custom_fields] = nil
|
44
|
+
Thread.current[:mp_ongoing_snapshot] = nil
|
41
45
|
Thread.current[:mini_profiler_private] = c
|
42
46
|
end
|
43
47
|
|
48
|
+
def add_snapshot_custom_field(key, value)
|
49
|
+
thread_var_key = :mini_profiler_snapshot_custom_fields
|
50
|
+
Thread.current[thread_var_key] ||= {}
|
51
|
+
Thread.current[thread_var_key][key] = value
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_snapshot_custom_fields
|
55
|
+
Thread.current[:mini_profiler_snapshot_custom_fields]
|
56
|
+
end
|
57
|
+
|
44
58
|
# discard existing results, don't track this request
|
45
59
|
def discard_results
|
46
60
|
self.current.discard = true if current
|
@@ -83,6 +97,16 @@ module Rack
|
|
83
97
|
params
|
84
98
|
end
|
85
99
|
end
|
100
|
+
|
101
|
+
def snapshots_transporter?
|
102
|
+
!!config.snapshots_transport_destination_url &&
|
103
|
+
!!config.snapshots_transport_auth_key
|
104
|
+
end
|
105
|
+
|
106
|
+
def redact_sql_queries?
|
107
|
+
Thread.current[:mp_ongoing_snapshot] == true &&
|
108
|
+
Rack::MiniProfiler.config.snapshots_redact_sql_queries
|
109
|
+
end
|
86
110
|
end
|
87
111
|
|
88
112
|
#
|
@@ -106,14 +130,23 @@ module Rack
|
|
106
130
|
def serve_results(env)
|
107
131
|
request = Rack::Request.new(env)
|
108
132
|
id = request.params['id']
|
109
|
-
|
110
|
-
|
133
|
+
is_snapshot = request.params['snapshot']
|
134
|
+
is_snapshot = [true, "true"].include?(is_snapshot)
|
135
|
+
if is_snapshot
|
136
|
+
page_struct = @storage.load_snapshot(id)
|
137
|
+
else
|
138
|
+
page_struct = @storage.load(id)
|
139
|
+
end
|
140
|
+
if !page_struct && is_snapshot
|
141
|
+
id = ERB::Util.html_escape(id)
|
142
|
+
return [404, {}, ["Snapshot with id '#{id}' not found"]]
|
143
|
+
elsif !page_struct
|
111
144
|
@storage.set_viewed(user(env), id)
|
112
|
-
id = ERB::Util.html_escape(
|
145
|
+
id = ERB::Util.html_escape(id)
|
113
146
|
user_info = ERB::Util.html_escape(user(env))
|
114
147
|
return [404, {}, ["Request not found: #{id} - user #{user_info}"]]
|
115
148
|
end
|
116
|
-
|
149
|
+
if !page_struct[:has_user_viewed] && !is_snapshot
|
117
150
|
page_struct[:client_timings] = TimerStruct::Client.init_from_form_data(env, page_struct)
|
118
151
|
page_struct[:has_user_viewed] = true
|
119
152
|
@storage.save(page_struct)
|
@@ -148,11 +181,12 @@ module Rack
|
|
148
181
|
file_name = path.sub(@config.base_url_path, '')
|
149
182
|
|
150
183
|
return serve_results(env) if file_name.eql?('results')
|
184
|
+
return handle_snapshots_request(env) if file_name.eql?('snapshots')
|
151
185
|
|
152
186
|
resources_env = env.dup
|
153
187
|
resources_env['PATH_INFO'] = file_name
|
154
188
|
|
155
|
-
rack_file = Rack::File.new(MiniProfiler.resources_root, 'Cache-Control' => "max-age
|
189
|
+
rack_file = Rack::File.new(MiniProfiler.resources_root, 'Cache-Control' => "max-age=#{cache_control_value}")
|
156
190
|
rack_file.call(resources_env)
|
157
191
|
end
|
158
192
|
|
@@ -177,7 +211,6 @@ module Rack
|
|
177
211
|
end
|
178
212
|
|
179
213
|
def call(env)
|
180
|
-
|
181
214
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
182
215
|
client_settings = ClientSettings.new(env, @storage, start)
|
183
216
|
MiniProfiler.deauthorize_request if @config.authorization_mode == :whitelist
|
@@ -189,15 +222,31 @@ module Rack
|
|
189
222
|
# Someone (e.g. Rails engine) could change the SCRIPT_NAME so we save it
|
190
223
|
env['RACK_MINI_PROFILER_ORIGINAL_SCRIPT_NAME'] = ENV['PASSENGER_BASE_URI'] || env['SCRIPT_NAME']
|
191
224
|
|
192
|
-
skip_it = (
|
193
|
-
|
194
|
-
|
225
|
+
skip_it = /pp=skip/.match?(query_string) || (
|
226
|
+
@config.skip_paths &&
|
227
|
+
@config.skip_paths.any? do |p|
|
228
|
+
if p.instance_of?(String)
|
229
|
+
path.start_with?(p)
|
230
|
+
elsif p.instance_of?(Regexp)
|
231
|
+
p.match?(path)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
)
|
235
|
+
if skip_it
|
236
|
+
return client_settings.handle_cookie(@app.call(env))
|
237
|
+
end
|
238
|
+
|
239
|
+
skip_it = (@config.pre_authorize_cb && !@config.pre_authorize_cb.call(env))
|
195
240
|
|
196
241
|
if skip_it || (
|
197
242
|
@config.authorization_mode == :whitelist &&
|
198
243
|
!client_settings.has_valid_cookie?
|
199
244
|
)
|
200
|
-
|
245
|
+
if take_snapshot?(path)
|
246
|
+
return client_settings.handle_cookie(take_snapshot(env, start))
|
247
|
+
else
|
248
|
+
return client_settings.handle_cookie(@app.call(env))
|
249
|
+
end
|
201
250
|
end
|
202
251
|
|
203
252
|
# handle all /mini-profiler requests here
|
@@ -288,23 +337,26 @@ module Rack
|
|
288
337
|
env['HTTP_ACCEPT_ENCODING'] = 'identity' if config.suppress_encoding
|
289
338
|
|
290
339
|
if query_string =~ /pp=flamegraph/
|
291
|
-
unless defined?(
|
340
|
+
unless defined?(StackProf) && StackProf.respond_to?(:run)
|
292
341
|
|
293
|
-
flamegraph = "Please install the
|
342
|
+
flamegraph = "Please install the stackprof gem and require it: add gem 'stackprof' to your Gemfile"
|
294
343
|
status, headers, body = @app.call(env)
|
295
344
|
else
|
296
345
|
# do not sully our profile with mini profiler timings
|
297
346
|
current.measure = false
|
298
347
|
match_data = query_string.match(/flamegraph_sample_rate=([\d\.]+)/)
|
299
348
|
|
300
|
-
mode = query_string =~ /mode=c/ ? :c : :ruby
|
301
|
-
|
302
349
|
if match_data && !match_data[1].to_f.zero?
|
303
350
|
sample_rate = match_data[1].to_f
|
304
351
|
else
|
305
352
|
sample_rate = config.flamegraph_sample_rate
|
306
353
|
end
|
307
|
-
flamegraph =
|
354
|
+
flamegraph = StackProf.run(
|
355
|
+
mode: :wall,
|
356
|
+
raw: true,
|
357
|
+
aggregate: false,
|
358
|
+
interval: (sample_rate * 1000).to_i
|
359
|
+
) do
|
308
360
|
status, headers, body = @app.call(env)
|
309
361
|
end
|
310
362
|
end
|
@@ -370,7 +422,7 @@ module Rack
|
|
370
422
|
|
371
423
|
if flamegraph
|
372
424
|
body.close if body.respond_to? :close
|
373
|
-
return client_settings.handle_cookie(self.flamegraph(flamegraph))
|
425
|
+
return client_settings.handle_cookie(self.flamegraph(flamegraph, path))
|
374
426
|
end
|
375
427
|
|
376
428
|
begin
|
@@ -589,9 +641,9 @@ Append the following to your query string:
|
|
589
641
|
#{make_link "enable", env} : enable profiling for this session (if previously disabled)
|
590
642
|
#{make_link "profile-gc", env} : perform gc profiling on this request, analyzes ObjectSpace generated by request (ruby 1.9.3 only)
|
591
643
|
#{make_link "profile-memory", env} : requires the memory_profiler gem, new location based report
|
592
|
-
#{make_link "flamegraph", env} :
|
644
|
+
#{make_link "flamegraph", env} : requires Ruby 2.2, a graph representing sampled activity (requires the stackprof gem).
|
593
645
|
#{make_link "flamegraph&flamegraph_sample_rate=1", env}: creates a flamegraph with the specified sample rate (in ms). Overrides value set in config
|
594
|
-
#{make_link "flamegraph_embed", env} :
|
646
|
+
#{make_link "flamegraph_embed", env} : requires Ruby 2.2, a graph representing sampled activity (requires the stackprof gem), embedded resources for use on an intranet.
|
595
647
|
#{make_link "trace-exceptions", env} : requires Ruby 2.0, will return all the spots where your application raises exceptions
|
596
648
|
#{make_link "analyze-memory", env} : requires Ruby 2.0, will perform basic memory analysis of heap
|
597
649
|
</pre>
|
@@ -602,9 +654,37 @@ Append the following to your query string:
|
|
602
654
|
[200, headers, [body]]
|
603
655
|
end
|
604
656
|
|
605
|
-
def flamegraph(graph)
|
657
|
+
def flamegraph(graph, path)
|
606
658
|
headers = { 'Content-Type' => 'text/html' }
|
607
|
-
|
659
|
+
if Hash === graph
|
660
|
+
html = <<~HTML
|
661
|
+
<!DOCTYPE html>
|
662
|
+
<html>
|
663
|
+
<head>
|
664
|
+
<style>
|
665
|
+
body { margin: 0; height: 100vh; }
|
666
|
+
#speedscope-iframe { width: 100%; height: 100%; border: none; }
|
667
|
+
</style>
|
668
|
+
</head>
|
669
|
+
<body>
|
670
|
+
<script type="text/javascript">
|
671
|
+
var graph = #{JSON.generate(graph)};
|
672
|
+
var json = JSON.stringify(graph);
|
673
|
+
var blob = new Blob([json], { type: 'text/plain' });
|
674
|
+
var objUrl = encodeURIComponent(URL.createObjectURL(blob));
|
675
|
+
var iframe = document.createElement('IFRAME');
|
676
|
+
iframe.setAttribute('id', 'speedscope-iframe');
|
677
|
+
document.body.appendChild(iframe);
|
678
|
+
var iframeUrl = '#{@config.base_url_path}speedscope/index.html#profileURL=' + objUrl + '&title=' + 'Flamegraph for #{CGI.escape(path)}';
|
679
|
+
iframe.setAttribute('src', iframeUrl);
|
680
|
+
</script>
|
681
|
+
</body>
|
682
|
+
</html>
|
683
|
+
HTML
|
684
|
+
[200, headers, [html]]
|
685
|
+
else
|
686
|
+
[200, headers, [graph]]
|
687
|
+
end
|
608
688
|
end
|
609
689
|
|
610
690
|
def ids(env)
|
@@ -628,10 +708,20 @@ Append the following to your query string:
|
|
628
708
|
# * you do not want script to be automatically appended for the current page. You can also call cancel_auto_inject
|
629
709
|
def get_profile_script(env)
|
630
710
|
path = "#{env['RACK_MINI_PROFILER_ORIGINAL_SCRIPT_NAME']}#{@config.base_url_path}"
|
711
|
+
version = MiniProfiler::ASSET_VERSION
|
712
|
+
if @config.assets_url
|
713
|
+
url = @config.assets_url.call('rack-mini-profiler.js', version, env)
|
714
|
+
css_url = @config.assets_url.call('rack-mini-profiler.css', version, env)
|
715
|
+
end
|
716
|
+
|
717
|
+
url = "#{path}includes.js?v=#{version}" if !url
|
718
|
+
css_url = "#{path}includes.css?v=#{version}" if !css_url
|
631
719
|
|
632
720
|
settings = {
|
633
721
|
path: path,
|
634
|
-
|
722
|
+
url: url,
|
723
|
+
cssUrl: css_url,
|
724
|
+
version: version,
|
635
725
|
verticalPosition: @config.vertical_position,
|
636
726
|
horizontalPosition: @config.horizontal_position,
|
637
727
|
showTrivial: @config.show_trivial,
|
@@ -643,7 +733,8 @@ Append the following to your query string:
|
|
643
733
|
toggleShortcut: @config.toggle_shortcut,
|
644
734
|
startHidden: @config.start_hidden,
|
645
735
|
collapseResults: @config.collapse_results,
|
646
|
-
htmlContainer: @config.html_container
|
736
|
+
htmlContainer: @config.html_container,
|
737
|
+
hiddenCustomFields: @config.snapshot_hidden_custom_fields.join(',')
|
647
738
|
}
|
648
739
|
|
649
740
|
if current && current.page_struct
|
@@ -674,5 +765,106 @@ Append the following to your query string:
|
|
674
765
|
def cache_control_value
|
675
766
|
86400
|
676
767
|
end
|
768
|
+
|
769
|
+
private
|
770
|
+
|
771
|
+
def handle_snapshots_request(env)
|
772
|
+
self.current = nil
|
773
|
+
MiniProfiler.authorize_request
|
774
|
+
status = 200
|
775
|
+
headers = { 'Content-Type' => 'text/html' }
|
776
|
+
qp = Rack::Utils.parse_nested_query(env['QUERY_STRING'])
|
777
|
+
if group_name = qp["group_name"]
|
778
|
+
list = @storage.find_snapshots_group(group_name)
|
779
|
+
list.each do |snapshot|
|
780
|
+
snapshot[:url] = url_for_snapshot(snapshot[:id])
|
781
|
+
end
|
782
|
+
data = {
|
783
|
+
group_name: group_name,
|
784
|
+
list: list
|
785
|
+
}
|
786
|
+
else
|
787
|
+
list = @storage.snapshot_groups_overview
|
788
|
+
list.each do |group|
|
789
|
+
group[:url] = url_for_snapshots_group(group[:name])
|
790
|
+
end
|
791
|
+
data = {
|
792
|
+
page: "overview",
|
793
|
+
list: list
|
794
|
+
}
|
795
|
+
end
|
796
|
+
data_html = <<~HTML
|
797
|
+
<div style="display: none;" id="snapshots-data">
|
798
|
+
#{data.to_json}
|
799
|
+
</div>
|
800
|
+
HTML
|
801
|
+
response = Rack::Response.new([], status, headers)
|
802
|
+
|
803
|
+
response.write <<~HTML
|
804
|
+
<html>
|
805
|
+
<head></head>
|
806
|
+
<body class="mp-snapshots">
|
807
|
+
HTML
|
808
|
+
response.write(data_html)
|
809
|
+
script = self.get_profile_script(env)
|
810
|
+
response.write(script)
|
811
|
+
response.write <<~HTML
|
812
|
+
</body>
|
813
|
+
</html>
|
814
|
+
HTML
|
815
|
+
response.finish
|
816
|
+
end
|
817
|
+
|
818
|
+
def rails_route_from_path(path, method)
|
819
|
+
if defined?(Rails) && defined?(ActionController::RoutingError)
|
820
|
+
hash = Rails.application.routes.recognize_path(path, method: method)
|
821
|
+
if hash && hash[:controller] && hash[:action]
|
822
|
+
"#{method} #{hash[:controller]}##{hash[:action]}"
|
823
|
+
end
|
824
|
+
end
|
825
|
+
rescue ActionController::RoutingError
|
826
|
+
nil
|
827
|
+
end
|
828
|
+
|
829
|
+
def url_for_snapshots_group(group_name)
|
830
|
+
qs = Rack::Utils.build_query({ group_name: group_name })
|
831
|
+
"/#{@config.base_url_path.gsub('/', '')}/snapshots?#{qs}"
|
832
|
+
end
|
833
|
+
|
834
|
+
def url_for_snapshot(id)
|
835
|
+
qs = Rack::Utils.build_query({ id: id, snapshot: true })
|
836
|
+
"/#{@config.base_url_path.gsub('/', '')}/results?#{qs}"
|
837
|
+
end
|
838
|
+
|
839
|
+
def take_snapshot?(path)
|
840
|
+
@config.snapshot_every_n_requests > 0 &&
|
841
|
+
!path.start_with?(@config.base_url_path) &&
|
842
|
+
@storage.should_take_snapshot?(@config.snapshot_every_n_requests)
|
843
|
+
end
|
844
|
+
|
845
|
+
def take_snapshot(env, start)
|
846
|
+
MiniProfiler.create_current(env, @config)
|
847
|
+
Thread.current[:mp_ongoing_snapshot] = true
|
848
|
+
results = @app.call(env)
|
849
|
+
status = results[0].to_i
|
850
|
+
if status >= 200 && status < 300
|
851
|
+
page_struct = current.page_struct
|
852
|
+
page_struct[:root].record_time(
|
853
|
+
(Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000
|
854
|
+
)
|
855
|
+
custom_fields = MiniProfiler.get_snapshot_custom_fields
|
856
|
+
page_struct[:custom_fields] = custom_fields if custom_fields
|
857
|
+
if Rack::MiniProfiler.snapshots_transporter?
|
858
|
+
Rack::MiniProfiler::SnapshotsTransporter.transport(page_struct)
|
859
|
+
else
|
860
|
+
@storage.push_snapshot(
|
861
|
+
page_struct,
|
862
|
+
@config
|
863
|
+
)
|
864
|
+
end
|
865
|
+
end
|
866
|
+
self.current = nil
|
867
|
+
results
|
868
|
+
end
|
677
869
|
end
|
678
870
|
end
|
@@ -7,7 +7,14 @@ module Rack
|
|
7
7
|
def record_sql(query, elapsed_ms, params = nil)
|
8
8
|
return unless current && current.current_timer
|
9
9
|
c = current
|
10
|
-
c.current_timer.add_sql(
|
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
|
+
)
|
11
18
|
end
|
12
19
|
|
13
20
|
def start_step(name)
|
@@ -0,0 +1,109 @@
|
|
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
|
+
def successful_http_requests_count
|
13
|
+
@@successful_http_requests_count
|
14
|
+
end
|
15
|
+
def failed_http_requests_count
|
16
|
+
@@failed_http_requests_count
|
17
|
+
end
|
18
|
+
|
19
|
+
def transport(snapshot)
|
20
|
+
@transporter ||= self.new(Rack::MiniProfiler.config)
|
21
|
+
@transporter.ship(snapshot)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :buffer
|
26
|
+
attr_accessor :max_buffer_size, :gzip_requests
|
27
|
+
|
28
|
+
def initialize(config)
|
29
|
+
@uri = URI(config.snapshots_transport_destination_url)
|
30
|
+
@auth_key = config.snapshots_transport_auth_key
|
31
|
+
@gzip_requests = config.snapshots_transport_gzip_requests
|
32
|
+
@thread = nil
|
33
|
+
@thread_mutex = Mutex.new
|
34
|
+
@buffer = []
|
35
|
+
@buffer_mutex = Mutex.new
|
36
|
+
@max_buffer_size = 100
|
37
|
+
@consecutive_failures_count = 0
|
38
|
+
@testing = false
|
39
|
+
end
|
40
|
+
|
41
|
+
def ship(snapshot)
|
42
|
+
@buffer_mutex.synchronize do
|
43
|
+
@buffer << snapshot
|
44
|
+
@buffer.shift if @buffer.size > @max_buffer_size
|
45
|
+
end
|
46
|
+
@thread_mutex.synchronize { start_thread }
|
47
|
+
end
|
48
|
+
|
49
|
+
def flush_buffer
|
50
|
+
buffer_content = @buffer_mutex.synchronize do
|
51
|
+
@buffer.dup if @buffer.size > 0
|
52
|
+
end
|
53
|
+
if buffer_content
|
54
|
+
headers = {
|
55
|
+
'Content-Type' => 'application/json',
|
56
|
+
'Mini-Profiler-Transport-Auth' => @auth_key
|
57
|
+
}
|
58
|
+
json = { snapshots: buffer_content }.to_json
|
59
|
+
body = if @gzip_requests
|
60
|
+
require 'zlib'
|
61
|
+
io = StringIO.new
|
62
|
+
gzip_writer = Zlib::GzipWriter.new(io)
|
63
|
+
gzip_writer.write(json)
|
64
|
+
gzip_writer.close
|
65
|
+
headers['Content-Encoding'] = 'gzip'
|
66
|
+
io.string
|
67
|
+
else
|
68
|
+
json
|
69
|
+
end
|
70
|
+
request = Net::HTTP::Post.new(@uri, headers)
|
71
|
+
request.body = body
|
72
|
+
http = Net::HTTP.new(@uri.hostname, @uri.port)
|
73
|
+
http.use_ssl = @uri.scheme == 'https'
|
74
|
+
res = http.request(request)
|
75
|
+
if res.code.to_i == 200
|
76
|
+
@@successful_http_requests_count += 1
|
77
|
+
@@transported_snapshots_count += buffer_content.size
|
78
|
+
@buffer_mutex.synchronize do
|
79
|
+
@buffer -= buffer_content
|
80
|
+
end
|
81
|
+
@consecutive_failures_count = 0
|
82
|
+
else
|
83
|
+
@@failed_http_requests_count += 1
|
84
|
+
@consecutive_failures_count += 1
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def requests_interval
|
90
|
+
[30 + backoff_delay, 60 * 60].min
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def backoff_delay
|
96
|
+
return 0 if @consecutive_failures_count == 0
|
97
|
+
2**@consecutive_failures_count
|
98
|
+
end
|
99
|
+
|
100
|
+
def start_thread
|
101
|
+
return if @thread&.alive? || @testing
|
102
|
+
@thread = Thread.new do
|
103
|
+
while true
|
104
|
+
sleep requests_interval
|
105
|
+
flush_buffer
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|