patient_http-sidekiq 1.2.0 → 1.4.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/ARCHITECTURE.md +31 -10
- data/CHANGELOG.md +32 -0
- data/README.md +126 -2
- data/VERSION +1 -1
- data/lib/patient_http/sidekiq/configuration.rb +258 -1
- data/lib/patient_http/sidekiq/direct_task_handler.rb +46 -0
- data/lib/patient_http/sidekiq/processor_observer.rb +162 -21
- data/lib/patient_http/sidekiq/redis_pool.rb +88 -0
- data/lib/patient_http/sidekiq/request_executor.rb +40 -10
- data/lib/patient_http/sidekiq/request_worker.rb +5 -2
- data/lib/patient_http/sidekiq/stats.rb +230 -33
- data/lib/patient_http/sidekiq/task_handler.rb +9 -3
- data/lib/patient_http/sidekiq/task_monitor.rb +410 -121
- data/lib/patient_http/sidekiq/task_monitor_thread.rb +41 -7
- data/lib/patient_http/sidekiq/web_ui/assets/patient-http/css/patient_http.css +29 -71
- data/lib/patient_http/sidekiq/web_ui/locales/ar.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/cs.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/da.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/de.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/el.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/en.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/es.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/fa.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/fr.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/gd.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/he.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/hi.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/it.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/ja.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/ko.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/lt.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/nb.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/nl.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/pl.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/pt-BR.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/pt.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/ru.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/sv.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/ta.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/tr.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/uk.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/ur.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/vi.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/zh-CN.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/locales/zh-TW.yml +10 -5
- data/lib/patient_http/sidekiq/web_ui/views/patient_http.html.erb +99 -44
- data/lib/patient_http/sidekiq/web_ui.rb +53 -1
- data/lib/patient_http/sidekiq.rb +303 -31
- data/patient_http-sidekiq.gemspec +1 -1
- metadata +6 -4
|
@@ -31,60 +31,115 @@
|
|
|
31
31
|
<span><%= avg_duration %></span>
|
|
32
32
|
</div>
|
|
33
33
|
</div>
|
|
34
|
+
<div class="stat-card">
|
|
35
|
+
<div class="stat-label"><%= t('patient_http_max_capacity') %></div>
|
|
36
|
+
<div class="stat-value"><%= number_with_delimiter(max_capacity) %></div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="stat-card">
|
|
39
|
+
<div class="stat-label"><%= t('patient_http_currently_inflight') %></div>
|
|
40
|
+
<div class="stat-value"><%= number_with_delimiter(current_inflight) %></div>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="stat-card">
|
|
43
|
+
<div class="stat-label"><%= t('patient_http_capacity_utilization') %></div>
|
|
44
|
+
<div class="stat-value"><%= utilization %>%</div>
|
|
45
|
+
</div>
|
|
34
46
|
</div>
|
|
35
47
|
|
|
36
|
-
<!--
|
|
37
|
-
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
<div class="
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
<!-- Processor Breakdown -->
|
|
49
|
+
<% unless processors.empty? %>
|
|
50
|
+
<div class="breakdown-section">
|
|
51
|
+
<h3><%= t('patient_http_processors') %></h3>
|
|
52
|
+
<div class="table-scroll">
|
|
53
|
+
<table class="table">
|
|
54
|
+
<thead>
|
|
55
|
+
<tr>
|
|
56
|
+
<th><%= t('patient_http_processor') %></th>
|
|
57
|
+
<th><%= t('patient_http_inflight') %></th>
|
|
58
|
+
<th><%= t('patient_http_peak_inflight') %></th>
|
|
59
|
+
<th><%= t('patient_http_max_capacity') %></th>
|
|
60
|
+
<th><%= t('patient_http_utilization') %></th>
|
|
61
|
+
<th><%= t('patient_http_total_requests') %></th>
|
|
62
|
+
<th><%= t('patient_http_errors') %></th>
|
|
63
|
+
<th><%= t('patient_http_max_capacity_exceeded') %></th>
|
|
64
|
+
<th><%= t('patient_http_avg_duration') %></th>
|
|
65
|
+
</tr>
|
|
66
|
+
</thead>
|
|
67
|
+
<tbody>
|
|
68
|
+
<% processors.each do |processor| %>
|
|
69
|
+
<tr>
|
|
70
|
+
<td><%= h(processor[:name]) %></td>
|
|
71
|
+
<td><%= number_with_delimiter(processor[:inflight]) %></td>
|
|
72
|
+
<td><%= number_with_delimiter(processor[:peak_inflight]) %></td>
|
|
73
|
+
<td><%= number_with_delimiter(processor[:max_capacity]) %></td>
|
|
74
|
+
<td><%= processor[:utilization] %>%</td>
|
|
75
|
+
<td><%= number_with_delimiter(processor[:requests]) %></td>
|
|
76
|
+
<td><%= number_with_delimiter(processor[:errors]) %></td>
|
|
77
|
+
<td><%= number_with_delimiter(processor[:max_capacity_exceeded]) %></td>
|
|
78
|
+
<td><%= processor[:avg_duration] %></td>
|
|
79
|
+
</tr>
|
|
80
|
+
<% end %>
|
|
81
|
+
</tbody>
|
|
82
|
+
</table>
|
|
54
83
|
</div>
|
|
55
84
|
</div>
|
|
85
|
+
<% end %>
|
|
56
86
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<% if processes.empty? %>
|
|
71
|
-
<tr class="empty-state">
|
|
72
|
-
<td colspan="4"><%= t('patient_http_no_active_processes') %></td>
|
|
87
|
+
<!-- In-Flight Requests -->
|
|
88
|
+
<% unless inflight.empty? %>
|
|
89
|
+
<div class="breakdown-section">
|
|
90
|
+
<h3><%= t('patient_http_inflight_requests') %></h3>
|
|
91
|
+
<div class="table-scroll">
|
|
92
|
+
<table class="table">
|
|
93
|
+
<thead>
|
|
94
|
+
<tr>
|
|
95
|
+
<th><%= t('patient_http_age') %></th>
|
|
96
|
+
<th><%= t('patient_http_method') %></th>
|
|
97
|
+
<th><%= t('patient_http_url') %></th>
|
|
98
|
+
<th><%= t('patient_http_processor') %></th>
|
|
99
|
+
<th><%= t('patient_http_process') %></th>
|
|
73
100
|
</tr>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
101
|
+
</thead>
|
|
102
|
+
<tbody>
|
|
103
|
+
<% inflight.each do |request| %>
|
|
77
104
|
<tr>
|
|
78
|
-
<td><%=
|
|
79
|
-
<td><%=
|
|
80
|
-
<td><%=
|
|
81
|
-
<td><%=
|
|
105
|
+
<td><%= request[:age] %></td>
|
|
106
|
+
<td><%= h(request[:http_method].to_s.upcase) %></td>
|
|
107
|
+
<td class="url"><%= h(request[:url]) %></td>
|
|
108
|
+
<td><%= h(request[:processor]) %></td>
|
|
109
|
+
<td><%= h(request[:process_id]) %></td>
|
|
82
110
|
</tr>
|
|
83
111
|
<% end %>
|
|
84
|
-
|
|
85
|
-
</
|
|
86
|
-
</
|
|
112
|
+
</tbody>
|
|
113
|
+
</table>
|
|
114
|
+
</div>
|
|
87
115
|
</div>
|
|
116
|
+
<% end %>
|
|
117
|
+
|
|
118
|
+
<!-- Sidekiq Process Breakdown -->
|
|
119
|
+
<div class="breakdown-section">
|
|
120
|
+
<h3><%= t('patient_http_sidekiq_processes') %></h3>
|
|
121
|
+
<table class="table">
|
|
122
|
+
<thead>
|
|
123
|
+
<tr>
|
|
124
|
+
<th><%= t('patient_http_process') %></th>
|
|
125
|
+
<th><%= t('patient_http_inflight') %></th>
|
|
126
|
+
</tr>
|
|
127
|
+
</thead>
|
|
128
|
+
<tbody>
|
|
129
|
+
<% if processes.empty? %>
|
|
130
|
+
<tr class="empty-state">
|
|
131
|
+
<td colspan="2"><%= t('patient_http_no_active_processes') %></td>
|
|
132
|
+
</tr>
|
|
133
|
+
<% else %>
|
|
134
|
+
<% processes.each do |process_id, info| %>
|
|
135
|
+
<tr>
|
|
136
|
+
<td><%= h(process_id) %></td>
|
|
137
|
+
<td><%= number_with_delimiter(info[:inflight]) %></td>
|
|
138
|
+
</tr>
|
|
139
|
+
<% end %>
|
|
140
|
+
<% end %>
|
|
141
|
+
</tbody>
|
|
142
|
+
</table>
|
|
88
143
|
</div>
|
|
89
144
|
|
|
90
145
|
<!-- HTTP Status Codes Breakdown -->
|
|
@@ -20,6 +20,10 @@ module PatientHttp
|
|
|
20
20
|
ROOT = File.join(__dir__, "web_ui")
|
|
21
21
|
VIEWS = File.join(ROOT, "views")
|
|
22
22
|
|
|
23
|
+
# Number of in-flight requests listed on the dashboard. The oldest are
|
|
24
|
+
# listed, because those are the ones worth looking at.
|
|
25
|
+
INFLIGHT_LIMIT = 50
|
|
26
|
+
|
|
23
27
|
class << self
|
|
24
28
|
# Sidekiq resolves symbol view names to different file names depending
|
|
25
29
|
# on the version (*.erb before 8.1, *.html.erb from 8.1 on), so the
|
|
@@ -28,6 +32,42 @@ module PatientHttp
|
|
|
28
32
|
@template ||= File.read(File.join(VIEWS, "patient_http.html.erb"))
|
|
29
33
|
end
|
|
30
34
|
|
|
35
|
+
# Build the per-processor rows for the dashboard by combining the
|
|
36
|
+
# cumulative counters with the live capacity each processor reports.
|
|
37
|
+
#
|
|
38
|
+
# Statistics are not recorded per processor when only one is
|
|
39
|
+
# configured, because they would duplicate the overall totals, so the
|
|
40
|
+
# breakdown is empty in that case.
|
|
41
|
+
#
|
|
42
|
+
# @param stats_by_processor [Hash, nil] cumulative counters by processor name
|
|
43
|
+
# @param capacity_by_processor [Hash] inflight and capacity by processor name
|
|
44
|
+
# @return [Array<Hash>] one row per processor, sorted by name
|
|
45
|
+
def processor_rows(stats_by_processor, capacity_by_processor)
|
|
46
|
+
stats_by_processor ||= {}
|
|
47
|
+
return [] if stats_by_processor.empty? && capacity_by_processor.size < 2
|
|
48
|
+
|
|
49
|
+
names = (stats_by_processor.keys | capacity_by_processor.keys).sort
|
|
50
|
+
names.map do |name|
|
|
51
|
+
counts = stats_by_processor[name] || {}
|
|
52
|
+
capacity = capacity_by_processor[name] || {}
|
|
53
|
+
requests = counts["requests"].to_i
|
|
54
|
+
max_capacity = capacity[:max_capacity].to_i
|
|
55
|
+
inflight = capacity[:inflight].to_i
|
|
56
|
+
|
|
57
|
+
{
|
|
58
|
+
name: name,
|
|
59
|
+
inflight: inflight,
|
|
60
|
+
peak_inflight: counts["max_inflight"].to_i,
|
|
61
|
+
max_capacity: max_capacity,
|
|
62
|
+
utilization: (max_capacity > 0) ? (inflight.to_f / max_capacity * 100).round(1) : 0,
|
|
63
|
+
requests: requests,
|
|
64
|
+
errors: counts["errors"].to_i,
|
|
65
|
+
max_capacity_exceeded: counts["max_capacity_exceeded"].to_i,
|
|
66
|
+
avg_duration: (requests > 0) ? (counts["duration"].to_f / requests).round(3) : 0.0
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
31
71
|
# This method is called by Sidekiq::Web when registering the extension
|
|
32
72
|
def registered(app)
|
|
33
73
|
# GET route for the main PatientHttp dashboard page
|
|
@@ -47,6 +87,16 @@ module PatientHttp
|
|
|
47
87
|
current_inflight = processes.values.sum { |data| data[:inflight] }
|
|
48
88
|
utilization = (max_capacity > 0) ? (current_inflight.to_f / max_capacity * 100).round(1) : 0
|
|
49
89
|
|
|
90
|
+
# Per-processor breakdown, combining the cumulative counters with
|
|
91
|
+
# the capacity each processor reports.
|
|
92
|
+
processors = PatientHttp::Sidekiq::WebUI.processor_rows(
|
|
93
|
+
totals["processors"],
|
|
94
|
+
PatientHttp::Sidekiq::TaskMonitor.inflight_counts_by_processor(processes)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# The requests that have been in flight the longest.
|
|
98
|
+
inflight = PatientHttp::Sidekiq::TaskMonitor.inflight_details(limit: INFLIGHT_LIMIT)
|
|
99
|
+
|
|
50
100
|
erb(PatientHttp::Sidekiq::WebUI.template, locals: {
|
|
51
101
|
totals: totals,
|
|
52
102
|
total_requests: total_requests,
|
|
@@ -54,7 +104,9 @@ module PatientHttp
|
|
|
54
104
|
max_capacity: max_capacity,
|
|
55
105
|
current_inflight: current_inflight,
|
|
56
106
|
utilization: utilization,
|
|
57
|
-
processes: processes
|
|
107
|
+
processes: processes,
|
|
108
|
+
processors: processors,
|
|
109
|
+
inflight: inflight
|
|
58
110
|
})
|
|
59
111
|
end
|
|
60
112
|
|