que-view 0.3.1 → 0.3.2
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/README.md +5 -4
- data/app/assets/stylesheets/que/view/application.css +22 -1
- data/app/controllers/que/view/jobs_controller.rb +1 -1
- data/app/controllers/que/view/queue_metrics_controller.rb +17 -0
- data/app/controllers/que/view/welcome_controller.rb +1 -0
- data/app/views/layouts/que/view/application.html.erb +3 -0
- data/app/views/que/view/jobs/index.html.erb +58 -61
- data/app/views/que/view/queue_metrics/index.html.erb +29 -0
- data/app/views/que/view/welcome/index.html.erb +26 -0
- data/config/routes.rb +1 -0
- data/lib/que/view/dsl.rb +50 -1
- data/lib/que/view/version.rb +1 -1
- data/lib/que/view.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b7b44890b63b063c130537e5fcc117f40bf1236dd89b156ca48e87a78d71248
|
4
|
+
data.tar.gz: 629fd230eb265fdbe7a41f0aec19e37f4047d797750eb635612687cceb7d5af9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1412a8a378aaa977fd92b40c79dad153267cbf50d1e7ac7409380469106269e5b96484d12193ac57ee78187ad172e79a61783e54e0948aff9242e4947bf6d381
|
7
|
+
data.tar.gz: ac32754839e4105584d330cc34a19cb5fccb48f8ce60afd58c1c3f5bb9b933a7ab4bd8616e109d7a2a5b91b384dff878b4a5699ee576f5d5fa1822c60d4392b0
|
data/README.md
CHANGED
@@ -4,12 +4,13 @@ SQL queries came from Que::Web, some styling from there too.
|
|
4
4
|
|
5
5
|
Benefits for using this one:
|
6
6
|
- no Sinatra (que-web based on Sinatra),
|
7
|
-
- no Foundation for styles,
|
8
|
-
- no jQuery,
|
7
|
+
- no Foundation for styles (and no external styles at all),
|
8
|
+
- no jQuery (and no java scripts at all),
|
9
9
|
- Que::Web was last updated 2 years ago.
|
10
10
|
|
11
|
-
<img width="
|
12
|
-
<img width="
|
11
|
+
<img width="1731" alt="Снимок экрана 2024-03-03 в 08 28 09" src="https://github.com/kortirso/que-view/assets/6195394/fdaf315d-6c6e-40ee-a60f-cd740cc7ec93">
|
12
|
+
<img width="1734" alt="Снимок экрана 2024-03-03 в 08 27 47" src="https://github.com/kortirso/que-view/assets/6195394/e45d334e-7637-41f8-af16-d6a9ac35f263">
|
13
|
+
<img width="1730" alt="Снимок экрана 2024-03-03 в 23 09 57" src="https://github.com/kortirso/que-view/assets/6195394/d790066f-3e96-4775-afd6-018750d0afd3">
|
13
14
|
|
14
15
|
|
15
16
|
## Installation
|
@@ -29,6 +29,10 @@ body {
|
|
29
29
|
background: #fafafa;
|
30
30
|
}
|
31
31
|
|
32
|
+
h1 {
|
33
|
+
margin-top: 0;
|
34
|
+
}
|
35
|
+
|
32
36
|
.row {
|
33
37
|
display: flex;
|
34
38
|
}
|
@@ -112,7 +116,7 @@ body {
|
|
112
116
|
}
|
113
117
|
|
114
118
|
.pagination {
|
115
|
-
margin:
|
119
|
+
margin: 0 auto 1rem;
|
116
120
|
}
|
117
121
|
|
118
122
|
.pagination .pagination-link {
|
@@ -181,6 +185,10 @@ body {
|
|
181
185
|
background: #e7e5e4;
|
182
186
|
}
|
183
187
|
|
188
|
+
.search-form {
|
189
|
+
margin-bottom: 1rem;
|
190
|
+
}
|
191
|
+
|
184
192
|
.search-form .form-select {
|
185
193
|
margin-right: 1rem;
|
186
194
|
padding: .5rem 1rem;
|
@@ -221,6 +229,19 @@ table p {
|
|
221
229
|
margin: 0;
|
222
230
|
}
|
223
231
|
|
232
|
+
.lockers {
|
233
|
+
margin-top: 2rem;
|
234
|
+
}
|
235
|
+
|
236
|
+
.lockers .locker-process {
|
237
|
+
display: flex;
|
238
|
+
flex-direction: column;
|
239
|
+
}
|
240
|
+
|
241
|
+
.metrics a {
|
242
|
+
color: #000;
|
243
|
+
}
|
244
|
+
|
224
245
|
@media screen and (max-width: 768px) {
|
225
246
|
.dashboard-row {
|
226
247
|
flex-direction: column;
|
@@ -84,7 +84,7 @@ module Que
|
|
84
84
|
|
85
85
|
def find_jobs(params)
|
86
86
|
case params[:status]&.to_sym
|
87
|
-
when :running then ::Que::View.fetch_running_jobs
|
87
|
+
when :running then ::Que::View.fetch_running_jobs
|
88
88
|
when :failing then ::Que::View.fetch_failing_jobs(PER_PAGE, offset, params)
|
89
89
|
when :scheduled then ::Que::View.fetch_scheduled_jobs(PER_PAGE, offset, params)
|
90
90
|
when :finished then ::Que::View.fetch_finished_jobs(PER_PAGE, offset, params)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Que
|
4
|
+
module View
|
5
|
+
class QueueMetricsController < Que::View::ApplicationController
|
6
|
+
before_action :find_queue_metrics, only: %i[index]
|
7
|
+
|
8
|
+
def index; end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def find_queue_metrics
|
13
|
+
@queue_metrics = ::Que::View.fetch_queue_metrics
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -26,6 +26,9 @@
|
|
26
26
|
<li class="<%= 'active' if params[:status] == 'expired' %>">
|
27
27
|
<%= link_to 'Expired', jobs_path(status: 'expired') %>
|
28
28
|
</li>
|
29
|
+
<li class="<%= 'active' if current_page?(queue_metrics_path) %>">
|
30
|
+
<%= link_to 'Queue metrics', queue_metrics_path %>
|
31
|
+
</li>
|
29
32
|
</ul>
|
30
33
|
<ul class="version">
|
31
34
|
<li>Que <%= Que::VERSION %></li>
|
@@ -1,3 +1,4 @@
|
|
1
|
+
<h1><%= params[:status].capitalize %> jobs</h1>
|
1
2
|
<%= form_with url: jobs_path, method: :get, class: 'search-form' do |form| %>
|
2
3
|
<%= form.hidden_field :status, value: params[:status] %>
|
3
4
|
<div class="row">
|
@@ -6,77 +7,73 @@
|
|
6
7
|
<%= form.submit 'Search', class: 'btn-primary' %>
|
7
8
|
</div>
|
8
9
|
<% end %>
|
9
|
-
<% if @
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
10
|
+
<% if @pagination %>
|
11
|
+
<div class="row pagination">
|
12
|
+
<% if @pagination.previous_page? %>
|
13
|
+
<%= link_to 'First', jobs_path(status: params[:status], page: 1), class: 'pagination-link' %>
|
14
|
+
<%= link_to '< Previous', jobs_path(status: params[:status], page: @pagination.previous_page), class: 'pagination-link' %>
|
15
|
+
<% else %>
|
16
|
+
<p class="pagination-link disabled">First</p>
|
17
|
+
<p class="pagination-link disabled">< Previous</p>
|
18
|
+
<% end %>
|
19
|
+
<p class="total-pages"><%= "Page #{@pagination.page} of #{@pagination.total_pages}" %></p>
|
20
|
+
<% if @pagination.next_page? %>
|
21
|
+
<%= link_to 'Next >', jobs_path(status: params[:status], page: @pagination.next_page), class: 'pagination-link' %>
|
22
|
+
<%= link_to 'Last', jobs_path(status: params[:status], page: @pagination.total_pages), class: 'pagination-link' %>
|
23
|
+
<% else %>
|
24
|
+
<p class="pagination-link disabled">Next ></p>
|
25
|
+
<p class="pagination-link disabled">Last </p>
|
26
|
+
<% end %>
|
27
|
+
</div>
|
28
|
+
<% end %>
|
29
|
+
<table cellspacing="0" class="jobs-list">
|
30
|
+
<thead>
|
31
|
+
<tr>
|
32
|
+
<th>ID</th>
|
33
|
+
<th>Job</th>
|
34
|
+
<th>Queue</th>
|
35
|
+
<th>Run at</th>
|
36
|
+
<th>Arguments</th>
|
37
|
+
<% if params[:status] == 'failing' %>
|
38
|
+
<th>Failures</th>
|
39
|
+
<th>Error</th>
|
18
40
|
<% end %>
|
19
|
-
|
20
|
-
|
21
|
-
<%= link_to 'Next >', jobs_path(status: params[:status], page: @pagination.next_page), class: 'pagination-link' %>
|
22
|
-
<%= link_to 'Last', jobs_path(status: params[:status], page: @pagination.total_pages), class: 'pagination-link' %>
|
23
|
-
<% else %>
|
24
|
-
<p class="pagination-link disabled">Next ></p>
|
25
|
-
<p class="pagination-link disabled">Last </p>
|
41
|
+
<% if %w[failing scheduled].include?(params[:status]) %>
|
42
|
+
<th>Actions</th>
|
26
43
|
<% end %>
|
27
|
-
</
|
28
|
-
|
29
|
-
<
|
30
|
-
|
44
|
+
</tr>
|
45
|
+
</thead>
|
46
|
+
<tbody>
|
47
|
+
<% @jobs.each do |job| %>
|
31
48
|
<tr>
|
32
|
-
<
|
33
|
-
<
|
34
|
-
<
|
35
|
-
<
|
36
|
-
<
|
49
|
+
<td><%= link_to job[:id], job_path(job[:id]) %></td>
|
50
|
+
<td><%= humanized_job_class(job) %></td>
|
51
|
+
<td><%= job[:queue] %></td>
|
52
|
+
<td><%= job[:run_at].strftime("%Y-%m-%d %H:%M:%S") %></td>
|
53
|
+
<td>
|
54
|
+
<% job.dig(:args, 0, :arguments).each do |argument| %>
|
55
|
+
<p><%= argument %></p>
|
56
|
+
<% end %>
|
57
|
+
</td>
|
37
58
|
<% if params[:status] == 'failing' %>
|
38
|
-
<
|
39
|
-
<
|
59
|
+
<td><%= job[:error_count] %></td>
|
60
|
+
<td><%= format_error(job) %></td>
|
40
61
|
<% end %>
|
41
62
|
<% if %w[failing scheduled].include?(params[:status]) %>
|
42
|
-
<th>Actions</th>
|
43
|
-
<% end %>
|
44
|
-
</tr>
|
45
|
-
</thead>
|
46
|
-
<tbody>
|
47
|
-
<% @jobs.each do |job| %>
|
48
|
-
<tr>
|
49
|
-
<td><%= link_to job[:id], job_path(job[:id]) %></td>
|
50
|
-
<td><%= humanized_job_class(job) %></td>
|
51
|
-
<td><%= job[:queue] %></td>
|
52
|
-
<td><%= job[:run_at].strftime("%Y-%m-%d %H:%M:%S") %></td>
|
53
63
|
<td>
|
54
|
-
|
55
|
-
|
56
|
-
|
64
|
+
<div class="actions">
|
65
|
+
<%= button_to 'Run', job_path(job[:id]), class: 'btn-danger', method: :patch, onclick: "return confirm('Are you sure you wish to reschedule job?')" %>
|
66
|
+
<%= button_to 'Delete', job_path(job[:id]), class: 'btn-danger', method: :delete, onclick: "return confirm('Are you sure you wish to delete job?')" %>
|
67
|
+
</div>
|
57
68
|
</td>
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
<div class="actions">
|
65
|
-
<%= button_to 'Run', job_path(job[:id]), class: 'btn-danger', method: :patch, onclick: "return confirm('Are you sure you wish to reschedule job?')" %>
|
66
|
-
<%= button_to 'Delete', job_path(job[:id]), class: 'btn-danger', method: :delete, onclick: "return confirm('Are you sure you wish to delete job?')" %>
|
67
|
-
</div>
|
68
|
-
</td>
|
69
|
-
<% end %>
|
70
|
-
</tr>
|
71
|
-
<% end %>
|
72
|
-
</tbody>
|
73
|
-
</table>
|
69
|
+
<% end %>
|
70
|
+
</tr>
|
71
|
+
<% end %>
|
72
|
+
</tbody>
|
73
|
+
</table>
|
74
|
+
<% if @pagination && %w[failing scheduled].include?(params[:status]) %>
|
74
75
|
<div class="row">
|
75
76
|
<%= button_to 'Run All', reschedule_all_jobs_path(status: params[:status]), class: 'btn-danger', method: :post, onclick: "return confirm('Are you sure you wish to reschedule all jobs?')" %>
|
76
77
|
<%= button_to 'Delete All', destroy_all_jobs_path(status: params[:status]), class: 'btn-danger', method: :delete, onclick: "return confirm('Are you sure you wish to delete all jobs?')" %>
|
77
78
|
</div>
|
78
|
-
<% else %>
|
79
|
-
<div class="row">
|
80
|
-
<p>No jobs found</p>
|
81
|
-
</div>
|
82
79
|
<% end %>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<div class="metrics">
|
2
|
+
<h1>Queue Metrics</h1>
|
3
|
+
<table cellspacing="0">
|
4
|
+
<thead>
|
5
|
+
<tr>
|
6
|
+
<th>Queue</th>
|
7
|
+
<th>Scheduled</th>
|
8
|
+
<th>Failing</th>
|
9
|
+
<th>Running</th>
|
10
|
+
<th>Finished</th>
|
11
|
+
<th>Expired</th>
|
12
|
+
<th>Latency</th>
|
13
|
+
</tr>
|
14
|
+
</thead>
|
15
|
+
<tbody>
|
16
|
+
<% @queue_metrics.each do |queue, values| %>
|
17
|
+
<tr>
|
18
|
+
<td><%= queue %></td>
|
19
|
+
<td><%= link_to values[:scheduled], jobs_path(status: 'scheduled', queue_name: queue) %></td>
|
20
|
+
<td><%= link_to values[:failing], jobs_path(status: 'failing', queue_name: queue) %></td>
|
21
|
+
<td><%= link_to values[:running], jobs_path(status: 'running', queue_name: queue) %></td>
|
22
|
+
<td><%= link_to values[:finished], jobs_path(status: 'finished', queue_name: queue) %></td>
|
23
|
+
<td><%= link_to values[:expired], jobs_path(status: 'expired', queue_name: queue) %></td>
|
24
|
+
<td></td>
|
25
|
+
</tr>
|
26
|
+
<% end %>
|
27
|
+
</tbody>
|
28
|
+
</table>
|
29
|
+
</div>
|
@@ -58,3 +58,29 @@
|
|
58
58
|
<% end %>
|
59
59
|
</div>
|
60
60
|
</div>
|
61
|
+
<div class="lockers">
|
62
|
+
<h1>Processes</h1>
|
63
|
+
<table cellspacing="0">
|
64
|
+
<thead>
|
65
|
+
<tr>
|
66
|
+
<th>ID</th>
|
67
|
+
<th>Worker count</th>
|
68
|
+
<th>Worker priorities</th>
|
69
|
+
<th>Listening</th>
|
70
|
+
</tr>
|
71
|
+
</thead>
|
72
|
+
<tbody>
|
73
|
+
<% @lockers.each.with_index do |locker, index| %>
|
74
|
+
<tr>
|
75
|
+
<td class="locker-process">
|
76
|
+
<span>Process <%= index + 1 %></span>
|
77
|
+
<span><strong>Queues:</strong> <%= locker[:queues][1..-2].split(',').join(', ') %></span>
|
78
|
+
</td>
|
79
|
+
<td><%= locker[:worker_count] %></td>
|
80
|
+
<td><%= locker[:worker_priorities] %></td>
|
81
|
+
<td><%= locker[:listening] %></td>
|
82
|
+
</tr>
|
83
|
+
<% end %>
|
84
|
+
</tbody>
|
85
|
+
</table>
|
86
|
+
</div>
|
data/config/routes.rb
CHANGED
data/lib/que/view/dsl.rb
CHANGED
@@ -8,6 +8,17 @@ module Que
|
|
8
8
|
execute(fetch_dashboard_stats_sql)
|
9
9
|
end
|
10
10
|
|
11
|
+
def fetch_que_lockers
|
12
|
+
execute(fetch_que_lockers_sql)
|
13
|
+
end
|
14
|
+
|
15
|
+
def fetch_queue_metrics
|
16
|
+
execute(fetch_queue_metrics_sql).each_with_object({}) { |element, acc|
|
17
|
+
acc[element[:queue_name].to_sym] ||= basis_queue_stats
|
18
|
+
acc[element[:queue_name].to_sym][element[:status].to_sym] = element[:count_all]
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
11
22
|
def fetch_queue_names
|
12
23
|
execute(fetch_queue_names_sql).map { |queues_data|
|
13
24
|
["#{queues_data[:queue_name]} (#{queues_data[:count_all]})", queues_data[:queue_name]]
|
@@ -20,7 +31,7 @@ module Que
|
|
20
31
|
}
|
21
32
|
end
|
22
33
|
|
23
|
-
def fetch_running_jobs
|
34
|
+
def fetch_running_jobs
|
24
35
|
Que.job_states
|
25
36
|
end
|
26
37
|
|
@@ -70,6 +81,10 @@ module Que
|
|
70
81
|
|
71
82
|
private
|
72
83
|
|
84
|
+
def basis_queue_stats
|
85
|
+
{ scheduled: 0, failing: 0, running: 0, finished: 0, expired: 0 }
|
86
|
+
end
|
87
|
+
|
73
88
|
# rubocop: disable Metrics/MethodLength
|
74
89
|
def fetch_dashboard_stats_sql
|
75
90
|
<<-SQL.squish
|
@@ -88,6 +103,40 @@ module Que
|
|
88
103
|
SQL
|
89
104
|
end
|
90
105
|
|
106
|
+
def fetch_que_lockers_sql
|
107
|
+
<<-SQL.squish
|
108
|
+
SELECT *
|
109
|
+
FROM que_lockers
|
110
|
+
SQL
|
111
|
+
end
|
112
|
+
|
113
|
+
def fetch_queue_metrics_sql
|
114
|
+
<<-SQL.squish
|
115
|
+
SELECT COUNT(*) AS count_all, queue AS queue_name,
|
116
|
+
CASE
|
117
|
+
WHEN expired_at IS NOT NULL THEN 'expired'
|
118
|
+
WHEN finished_at IS NOT NULL THEN 'finished'
|
119
|
+
WHEN locks.job_id IS NULL AND error_count > 0 THEN 'failing'
|
120
|
+
WHEN locks.job_id IS NULL AND error_count = 0 THEN 'scheduled'
|
121
|
+
ELSE 'running'
|
122
|
+
END status
|
123
|
+
FROM que_jobs
|
124
|
+
LEFT JOIN (
|
125
|
+
SELECT (classid::bigint << 32) + objid::bigint AS job_id
|
126
|
+
FROM pg_locks
|
127
|
+
WHERE locktype = 'advisory'
|
128
|
+
) locks ON (que_jobs.id=locks.job_id)
|
129
|
+
GROUP BY queue,
|
130
|
+
CASE
|
131
|
+
WHEN expired_at IS NOT NULL THEN 'expired'
|
132
|
+
WHEN finished_at IS NOT NULL THEN 'finished'
|
133
|
+
WHEN locks.job_id IS NULL AND error_count > 0 THEN 'failing'
|
134
|
+
WHEN locks.job_id IS NULL AND error_count = 0 THEN 'scheduled'
|
135
|
+
ELSE 'running'
|
136
|
+
END
|
137
|
+
SQL
|
138
|
+
end
|
139
|
+
|
91
140
|
def fetch_queue_names_sql
|
92
141
|
<<-SQL.squish
|
93
142
|
SELECT COUNT(*) AS count_all, queue AS queue_name
|
data/lib/que/view/version.rb
CHANGED
data/lib/que/view.rb
CHANGED
@@ -34,7 +34,7 @@ module Que
|
|
34
34
|
end
|
35
35
|
|
36
36
|
# Public: All the methods delegated to instance. These should match the interface of Que::View::DSL.
|
37
|
-
def_delegators :instance, :fetch_dashboard_stats,
|
37
|
+
def_delegators :instance, :fetch_dashboard_stats, :fetch_que_lockers, :fetch_queue_metrics,
|
38
38
|
:fetch_queue_names, :fetch_job_names,
|
39
39
|
:fetch_running_jobs, :fetch_failing_jobs, :fetch_scheduled_jobs, :fetch_finished_jobs,
|
40
40
|
:fetch_expired_jobs, :fetch_job,
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: que-view
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bogdanov Anton
|
@@ -164,11 +164,13 @@ files:
|
|
164
164
|
- app/assets/stylesheets/que/view/application.css
|
165
165
|
- app/controllers/que/view/application_controller.rb
|
166
166
|
- app/controllers/que/view/jobs_controller.rb
|
167
|
+
- app/controllers/que/view/queue_metrics_controller.rb
|
167
168
|
- app/controllers/que/view/welcome_controller.rb
|
168
169
|
- app/helpers/que/view/application_helper.rb
|
169
170
|
- app/views/layouts/que/view/application.html.erb
|
170
171
|
- app/views/que/view/jobs/index.html.erb
|
171
172
|
- app/views/que/view/jobs/show.html.erb
|
173
|
+
- app/views/que/view/queue_metrics/index.html.erb
|
172
174
|
- app/views/que/view/welcome/index.html.erb
|
173
175
|
- config/routes.rb
|
174
176
|
- lib/que/view.rb
|