rack-mini-profiler 2.0.3 → 2.3.0
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 +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
|