solid_queue_monitor 0.2.0 → 0.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/README.md +24 -10
- data/Rakefile +8 -8
- data/app/controllers/solid_queue_monitor/application_controller.rb +25 -0
- data/app/controllers/solid_queue_monitor/base_controller.rb +70 -67
- data/app/controllers/solid_queue_monitor/failed_jobs_controller.rb +8 -7
- data/app/controllers/solid_queue_monitor/in_progress_jobs_controller.rb +16 -10
- data/app/controllers/solid_queue_monitor/overview_controller.rb +13 -12
- data/app/controllers/solid_queue_monitor/queues_controller.rb +4 -2
- data/app/controllers/solid_queue_monitor/ready_jobs_controller.rb +8 -7
- data/app/controllers/solid_queue_monitor/recurring_jobs_controller.rb +7 -6
- data/app/controllers/solid_queue_monitor/scheduled_jobs_controller.rb +8 -7
- data/app/presenters/solid_queue_monitor/base_presenter.rb +42 -41
- data/app/presenters/solid_queue_monitor/failed_jobs_presenter.rb +51 -41
- data/app/presenters/solid_queue_monitor/in_progress_jobs_presenter.rb +13 -2
- data/app/presenters/solid_queue_monitor/jobs_presenter.rb +18 -9
- data/app/presenters/solid_queue_monitor/queues_presenter.rb +5 -5
- data/app/presenters/solid_queue_monitor/ready_jobs_presenter.rb +10 -2
- data/app/presenters/solid_queue_monitor/recurring_jobs_presenter.rb +5 -2
- data/app/presenters/solid_queue_monitor/scheduled_jobs_presenter.rb +15 -9
- data/app/presenters/solid_queue_monitor/stats_presenter.rb +3 -1
- data/app/services/solid_queue_monitor/authentication_service.rb +7 -5
- data/app/services/solid_queue_monitor/execute_job_service.rb +3 -1
- data/app/services/solid_queue_monitor/failed_job_service.rb +38 -36
- data/app/services/solid_queue_monitor/html_generator.rb +5 -2
- data/app/services/solid_queue_monitor/pagination_service.rb +3 -1
- data/app/services/solid_queue_monitor/stats_calculator.rb +3 -1
- data/app/services/solid_queue_monitor/status_calculator.rb +4 -1
- data/app/services/solid_queue_monitor/stylesheet_generator.rb +23 -21
- data/config/initializers/solid_queue_monitor.rb +3 -1
- data/config/routes.rb +6 -4
- data/lib/generators/solid_queue_monitor/install_generator.rb +7 -5
- data/lib/generators/solid_queue_monitor/templates/initializer.rb +3 -1
- data/lib/solid_queue_monitor/engine.rb +5 -3
- data/lib/solid_queue_monitor/version.rb +2 -2
- data/lib/solid_queue_monitor.rb +9 -13
- data/lib/tasks/app.rake +42 -40
- metadata +9 -148
- data/app/controllers/solid_queue_monitor/monitor_controller.rb +0 -318
@@ -1,318 +0,0 @@
|
|
1
|
-
module SolidQueueMonitor
|
2
|
-
class MonitorController < ActionController::Base
|
3
|
-
include ActionController::HttpAuthentication::Basic::ControllerMethods
|
4
|
-
include ActionController::Flash
|
5
|
-
|
6
|
-
before_action :authenticate, if: -> { SolidQueueMonitor::AuthenticationService.authentication_required? }
|
7
|
-
layout false
|
8
|
-
skip_before_action :verify_authenticity_token, only: [:execute_jobs, :retry_failed_job, :discard_failed_job, :retry_failed_jobs, :discard_failed_jobs]
|
9
|
-
|
10
|
-
# Define a helper method for setting flash messages
|
11
|
-
def set_flash_message(message, type)
|
12
|
-
session[:flash_message] = message
|
13
|
-
session[:flash_type] = type
|
14
|
-
end
|
15
|
-
|
16
|
-
def index
|
17
|
-
@stats = SolidQueueMonitor::StatsCalculator.calculate
|
18
|
-
|
19
|
-
# Get all jobs with pagination
|
20
|
-
@recent_jobs = paginate(filter_jobs(SolidQueue::Job.order(created_at: :desc)))
|
21
|
-
|
22
|
-
# Preload failed job information
|
23
|
-
preload_job_statuses(@recent_jobs[:records])
|
24
|
-
|
25
|
-
render_page('Overview', generate_overview_content)
|
26
|
-
end
|
27
|
-
|
28
|
-
def ready_jobs
|
29
|
-
base_query = SolidQueue::ReadyExecution.includes(:job).order(created_at: :desc)
|
30
|
-
@ready_jobs = paginate(filter_ready_jobs(base_query))
|
31
|
-
render_page('Ready Jobs', SolidQueueMonitor::ReadyJobsPresenter.new(@ready_jobs[:records],
|
32
|
-
current_page: @ready_jobs[:current_page],
|
33
|
-
total_pages: @ready_jobs[:total_pages],
|
34
|
-
filters: filter_params
|
35
|
-
).render)
|
36
|
-
end
|
37
|
-
|
38
|
-
def scheduled_jobs
|
39
|
-
base_query = SolidQueue::ScheduledExecution.includes(:job).order(scheduled_at: :asc)
|
40
|
-
@scheduled_jobs = paginate(filter_scheduled_jobs(base_query))
|
41
|
-
render_page('Scheduled Jobs', SolidQueueMonitor::ScheduledJobsPresenter.new(@scheduled_jobs[:records],
|
42
|
-
current_page: @scheduled_jobs[:current_page],
|
43
|
-
total_pages: @scheduled_jobs[:total_pages],
|
44
|
-
filters: filter_params
|
45
|
-
).render)
|
46
|
-
end
|
47
|
-
|
48
|
-
def recurring_jobs
|
49
|
-
base_query = filter_recurring_jobs(SolidQueue::RecurringTask.order(:key))
|
50
|
-
@recurring_jobs = paginate(base_query)
|
51
|
-
render_page('Recurring Jobs', SolidQueueMonitor::RecurringJobsPresenter.new(@recurring_jobs[:records],
|
52
|
-
current_page: @recurring_jobs[:current_page],
|
53
|
-
total_pages: @recurring_jobs[:total_pages],
|
54
|
-
filters: filter_params
|
55
|
-
).render)
|
56
|
-
end
|
57
|
-
|
58
|
-
def failed_jobs
|
59
|
-
base_query = SolidQueue::FailedExecution.includes(:job).order(created_at: :desc)
|
60
|
-
@failed_jobs = paginate(filter_failed_jobs(base_query))
|
61
|
-
render_page('Failed Jobs', SolidQueueMonitor::FailedJobsPresenter.new(@failed_jobs[:records],
|
62
|
-
current_page: @failed_jobs[:current_page],
|
63
|
-
total_pages: @failed_jobs[:total_pages],
|
64
|
-
filters: filter_params
|
65
|
-
).render)
|
66
|
-
end
|
67
|
-
|
68
|
-
def queues
|
69
|
-
@queues = SolidQueue::Job.group(:queue_name)
|
70
|
-
.select('queue_name, COUNT(*) as job_count')
|
71
|
-
.order('job_count DESC')
|
72
|
-
render_page('Queues', SolidQueueMonitor::QueuesPresenter.new(@queues).render)
|
73
|
-
end
|
74
|
-
|
75
|
-
def execute_jobs
|
76
|
-
if params[:job_ids].present?
|
77
|
-
SolidQueueMonitor::ExecuteJobService.new.execute_many(params[:job_ids])
|
78
|
-
set_flash_message('Selected jobs moved to ready queue', 'success')
|
79
|
-
else
|
80
|
-
set_flash_message('No jobs selected', 'error')
|
81
|
-
end
|
82
|
-
redirect_to scheduled_jobs_path
|
83
|
-
end
|
84
|
-
|
85
|
-
def retry_failed_job
|
86
|
-
id = params[:id]
|
87
|
-
service = SolidQueueMonitor::FailedJobService.new
|
88
|
-
|
89
|
-
if service.retry_job(id)
|
90
|
-
set_flash_message("Job #{id} has been queued for retry.", 'success')
|
91
|
-
else
|
92
|
-
set_flash_message("Failed to retry job #{id}.", 'error')
|
93
|
-
end
|
94
|
-
redirect_to failed_jobs_path
|
95
|
-
end
|
96
|
-
|
97
|
-
def discard_failed_job
|
98
|
-
id = params[:id]
|
99
|
-
service = SolidQueueMonitor::FailedJobService.new
|
100
|
-
|
101
|
-
if service.discard_job(id)
|
102
|
-
set_flash_message("Job #{id} has been discarded.", 'success')
|
103
|
-
else
|
104
|
-
set_flash_message("Failed to discard job #{id}.", 'error')
|
105
|
-
end
|
106
|
-
redirect_to failed_jobs_path
|
107
|
-
end
|
108
|
-
|
109
|
-
def retry_failed_jobs
|
110
|
-
result = SolidQueueMonitor::FailedJobService.new.retry_all(params[:job_ids])
|
111
|
-
|
112
|
-
if result[:success]
|
113
|
-
set_flash_message(result[:message], 'success')
|
114
|
-
else
|
115
|
-
set_flash_message(result[:message], 'error')
|
116
|
-
end
|
117
|
-
redirect_to failed_jobs_path
|
118
|
-
end
|
119
|
-
|
120
|
-
def discard_failed_jobs
|
121
|
-
result = SolidQueueMonitor::FailedJobService.new.discard_all(params[:job_ids])
|
122
|
-
|
123
|
-
if result[:success]
|
124
|
-
set_flash_message(result[:message], 'success')
|
125
|
-
else
|
126
|
-
set_flash_message(result[:message], 'error')
|
127
|
-
end
|
128
|
-
redirect_to failed_jobs_path
|
129
|
-
end
|
130
|
-
|
131
|
-
private
|
132
|
-
|
133
|
-
def authenticate
|
134
|
-
authenticate_or_request_with_http_basic do |username, password|
|
135
|
-
SolidQueueMonitor::AuthenticationService.authenticate(username, password)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def paginate(relation)
|
140
|
-
PaginationService.new(relation, current_page, per_page).paginate
|
141
|
-
end
|
142
|
-
|
143
|
-
def render_page(title, content)
|
144
|
-
# Get flash message from session
|
145
|
-
message = session[:flash_message]
|
146
|
-
message_type = session[:flash_type]
|
147
|
-
|
148
|
-
# Clear the flash message from session after using it
|
149
|
-
session.delete(:flash_message)
|
150
|
-
session.delete(:flash_type)
|
151
|
-
|
152
|
-
html = SolidQueueMonitor::HtmlGenerator.new(
|
153
|
-
title: title,
|
154
|
-
content: content,
|
155
|
-
message: message,
|
156
|
-
message_type: message_type
|
157
|
-
).generate
|
158
|
-
|
159
|
-
render html: html.html_safe
|
160
|
-
end
|
161
|
-
|
162
|
-
def generate_overview_content
|
163
|
-
SolidQueueMonitor::StatsPresenter.new(@stats).render +
|
164
|
-
SolidQueueMonitor::JobsPresenter.new(@recent_jobs[:records],
|
165
|
-
current_page: @recent_jobs[:current_page],
|
166
|
-
total_pages: @recent_jobs[:total_pages],
|
167
|
-
filters: filter_params
|
168
|
-
).render
|
169
|
-
end
|
170
|
-
|
171
|
-
def current_page
|
172
|
-
(params[:page] || 1).to_i
|
173
|
-
end
|
174
|
-
|
175
|
-
def per_page
|
176
|
-
SolidQueueMonitor.jobs_per_page
|
177
|
-
end
|
178
|
-
|
179
|
-
# Preload job statuses to avoid N+1 queries
|
180
|
-
def preload_job_statuses(jobs)
|
181
|
-
return if jobs.empty?
|
182
|
-
|
183
|
-
# Get all job IDs
|
184
|
-
job_ids = jobs.map(&:id)
|
185
|
-
|
186
|
-
# Find all failed jobs in a single query
|
187
|
-
failed_job_ids = SolidQueue::FailedExecution.where(job_id: job_ids).pluck(:job_id)
|
188
|
-
|
189
|
-
# Find all scheduled jobs in a single query
|
190
|
-
scheduled_job_ids = SolidQueue::ScheduledExecution.where(job_id: job_ids).pluck(:job_id)
|
191
|
-
|
192
|
-
# Attach the status information to each job
|
193
|
-
jobs.each do |job|
|
194
|
-
job.instance_variable_set(:@failed, failed_job_ids.include?(job.id))
|
195
|
-
job.instance_variable_set(:@scheduled, scheduled_job_ids.include?(job.id))
|
196
|
-
end
|
197
|
-
|
198
|
-
# Define the method to check if a job is failed
|
199
|
-
SolidQueue::Job.class_eval do
|
200
|
-
def failed?
|
201
|
-
if instance_variable_defined?(:@failed)
|
202
|
-
@failed
|
203
|
-
else
|
204
|
-
SolidQueue::FailedExecution.exists?(job_id: id)
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
def scheduled?
|
209
|
-
if instance_variable_defined?(:@scheduled)
|
210
|
-
@scheduled
|
211
|
-
else
|
212
|
-
SolidQueue::ScheduledExecution.exists?(job_id: id)
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
def filter_jobs(relation)
|
219
|
-
relation = relation.where("class_name LIKE ?", "%#{params[:class_name]}%") if params[:class_name].present?
|
220
|
-
relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%") if params[:queue_name].present?
|
221
|
-
|
222
|
-
if params[:status].present?
|
223
|
-
case params[:status]
|
224
|
-
when 'completed'
|
225
|
-
relation = relation.where.not(finished_at: nil)
|
226
|
-
when 'failed'
|
227
|
-
failed_job_ids = SolidQueue::FailedExecution.pluck(:job_id)
|
228
|
-
relation = relation.where(id: failed_job_ids)
|
229
|
-
when 'scheduled'
|
230
|
-
scheduled_job_ids = SolidQueue::ScheduledExecution.pluck(:job_id)
|
231
|
-
relation = relation.where(id: scheduled_job_ids)
|
232
|
-
when 'pending'
|
233
|
-
# Pending jobs are those that are not completed, failed, or scheduled
|
234
|
-
failed_job_ids = SolidQueue::FailedExecution.pluck(:job_id)
|
235
|
-
scheduled_job_ids = SolidQueue::ScheduledExecution.pluck(:job_id)
|
236
|
-
relation = relation.where(finished_at: nil)
|
237
|
-
.where.not(id: failed_job_ids + scheduled_job_ids)
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
relation
|
242
|
-
end
|
243
|
-
|
244
|
-
def filter_ready_jobs(relation)
|
245
|
-
return relation unless params[:class_name].present? || params[:queue_name].present?
|
246
|
-
|
247
|
-
if params[:class_name].present?
|
248
|
-
job_ids = SolidQueue::Job.where("class_name LIKE ?", "%#{params[:class_name]}%").pluck(:id)
|
249
|
-
relation = relation.where(job_id: job_ids)
|
250
|
-
end
|
251
|
-
|
252
|
-
if params[:queue_name].present?
|
253
|
-
relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
|
254
|
-
end
|
255
|
-
|
256
|
-
relation
|
257
|
-
end
|
258
|
-
|
259
|
-
def filter_scheduled_jobs(relation)
|
260
|
-
return relation unless params[:class_name].present? || params[:queue_name].present?
|
261
|
-
|
262
|
-
if params[:class_name].present?
|
263
|
-
job_ids = SolidQueue::Job.where("class_name LIKE ?", "%#{params[:class_name]}%").pluck(:id)
|
264
|
-
relation = relation.where(job_id: job_ids)
|
265
|
-
end
|
266
|
-
|
267
|
-
if params[:queue_name].present?
|
268
|
-
relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
|
269
|
-
end
|
270
|
-
|
271
|
-
relation
|
272
|
-
end
|
273
|
-
|
274
|
-
def filter_recurring_jobs(relation)
|
275
|
-
return relation unless params[:class_name].present? || params[:queue_name].present?
|
276
|
-
|
277
|
-
if params[:class_name].present?
|
278
|
-
relation = relation.where("class_name LIKE ?", "%#{params[:class_name]}%")
|
279
|
-
end
|
280
|
-
|
281
|
-
if params[:queue_name].present?
|
282
|
-
relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
|
283
|
-
end
|
284
|
-
|
285
|
-
relation
|
286
|
-
end
|
287
|
-
|
288
|
-
def filter_failed_jobs(relation)
|
289
|
-
return relation unless params[:class_name].present? || params[:queue_name].present?
|
290
|
-
|
291
|
-
if params[:class_name].present?
|
292
|
-
job_ids = SolidQueue::Job.where("class_name LIKE ?", "%#{params[:class_name]}%").pluck(:id)
|
293
|
-
relation = relation.where(job_id: job_ids)
|
294
|
-
end
|
295
|
-
|
296
|
-
if params[:queue_name].present?
|
297
|
-
# Check if FailedExecution has queue_name column
|
298
|
-
if relation.column_names.include?('queue_name')
|
299
|
-
relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
|
300
|
-
else
|
301
|
-
# If not, filter by job's queue_name
|
302
|
-
job_ids = SolidQueue::Job.where("queue_name LIKE ?", "%#{params[:queue_name]}%").pluck(:id)
|
303
|
-
relation = relation.where(job_id: job_ids)
|
304
|
-
end
|
305
|
-
end
|
306
|
-
|
307
|
-
relation
|
308
|
-
end
|
309
|
-
|
310
|
-
def filter_params
|
311
|
-
{
|
312
|
-
class_name: params[:class_name],
|
313
|
-
queue_name: params[:queue_name],
|
314
|
-
status: params[:status]
|
315
|
-
}
|
316
|
-
end
|
317
|
-
end
|
318
|
-
end
|