solid_queue_monitor 0.1.1 → 0.2.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 +30 -4
- data/app/controllers/solid_queue_monitor/base_controller.rb +194 -0
- data/app/controllers/solid_queue_monitor/failed_jobs_controller.rb +62 -0
- data/app/controllers/solid_queue_monitor/in_progress_jobs_controller.rb +27 -0
- data/app/controllers/solid_queue_monitor/monitor_controller.rb +75 -7
- data/app/controllers/solid_queue_monitor/overview_controller.rb +25 -0
- data/app/controllers/solid_queue_monitor/queues_controller.rb +11 -0
- data/app/controllers/solid_queue_monitor/ready_jobs_controller.rb +14 -0
- data/app/controllers/solid_queue_monitor/recurring_jobs_controller.rb +14 -0
- data/app/controllers/solid_queue_monitor/scheduled_jobs_controller.rb +24 -0
- data/app/presenters/solid_queue_monitor/base_presenter.rb +57 -48
- data/app/presenters/solid_queue_monitor/failed_jobs_presenter.rb +257 -21
- data/app/presenters/solid_queue_monitor/in_progress_jobs_presenter.rb +71 -0
- data/app/presenters/solid_queue_monitor/jobs_presenter.rb +36 -2
- data/app/presenters/solid_queue_monitor/scheduled_jobs_presenter.rb +33 -10
- data/app/presenters/solid_queue_monitor/stats_presenter.rb +2 -2
- data/app/services/solid_queue_monitor/failed_job_service.rb +97 -0
- data/app/services/solid_queue_monitor/html_generator.rb +28 -2
- data/app/services/solid_queue_monitor/stats_calculator.rb +1 -0
- data/app/services/solid_queue_monitor/stylesheet_generator.rb +150 -1
- data/config/routes.rb +13 -7
- data/lib/solid_queue_monitor/version.rb +2 -2
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 264dd6a6542855e5209702c11fbb46c1c267b5c8d48d530887030f924bdb929f
|
4
|
+
data.tar.gz: 4d5a33c4152f29d0c3ba4c9fd6c59582e7af9694e85469a78cde9a9d173a4433
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 704927588ba7329d988a7533ab70e6b444254abc64a15684fbeccce47221c929c39701abd8fbb02dbebf444c0c3a9521cbcc84260d06d3c6fdafca6e01ad2be5
|
7
|
+
data.tar.gz: 8b0a608d0ae89c54719dd5286e9de77cb4145d1f1c3dc7c3f8c1b46a521ae1b2e0ad37d35e7c2796a187122fc38f401b423a46109cc81328cff6805274338357
|
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# SolidQueueMonitor
|
2
2
|
|
3
|
-
|
3
|
+
[](https://badge.fury.io/rb/solid_queue_monitor)
|
4
|
+
[](LICENSE)
|
5
|
+
|
6
|
+
A lightweight, zero-dependency web interface for monitoring Solid Queue background jobs in Ruby on Rails applications. Perfect for Rails API-only applications and traditional Rails apps.
|
4
7
|
|
5
8
|
## Key Advantages
|
6
9
|
|
@@ -8,6 +11,7 @@ A lightweight, zero-dependency web interface for monitoring Solid Queue jobs in
|
|
8
11
|
- **No External Dependencies**: No JavaScript frameworks, no CSS libraries, no additional gems required - just pure Rails.
|
9
12
|
- **Self-contained UI**: All HTML, CSS, and JavaScript are generated server-side, making deployment simple and reliable.
|
10
13
|
- **Minimal Footprint**: Adds minimal overhead to your application while providing powerful monitoring capabilities.
|
14
|
+
- **Rails 7 Compatible**: Fully compatible with Rails 7.1+ and the latest Solid Queue versions.
|
11
15
|
|
12
16
|
## Features
|
13
17
|
|
@@ -16,6 +20,7 @@ A lightweight, zero-dependency web interface for monitoring Solid Queue jobs in
|
|
16
20
|
- **Job Management**: Execute scheduled jobs on demand
|
17
21
|
- **Failed Job Inspection**: View detailed error information for failed jobs
|
18
22
|
- **Queue Monitoring**: Track job distribution across different queues
|
23
|
+
- **Recurring Jobs**: Monitor and manage recurring background tasks
|
19
24
|
- **Pagination**: Navigate through large job lists with ease
|
20
25
|
- **Optional Authentication**: Secure your dashboard with HTTP Basic Authentication
|
21
26
|
- **Responsive Design**: Works on desktop and mobile devices
|
@@ -27,16 +32,16 @@ A lightweight, zero-dependency web interface for monitoring Solid Queue jobs in
|
|
27
32
|
|
28
33
|

|
29
34
|
|
30
|
-
###
|
35
|
+
### Failed Jobs
|
31
36
|
|
32
|
-

|
33
38
|
|
34
39
|
## Installation
|
35
40
|
|
36
41
|
Add this line to your application's Gemfile:
|
37
42
|
|
38
43
|
```ruby
|
39
|
-
gem 'solid_queue_monitor'
|
44
|
+
gem 'solid_queue_monitor', '~> 0.2.0'
|
40
45
|
```
|
41
46
|
|
42
47
|
Then execute:
|
@@ -96,6 +101,7 @@ The dashboard provides several views:
|
|
96
101
|
- **Overview**: Shows statistics and recent jobs
|
97
102
|
- **Ready Jobs**: Jobs that are ready to be executed
|
98
103
|
- **Scheduled Jobs**: Jobs scheduled for future execution
|
104
|
+
- **Recurring Jobs**: Jobs that run on a recurring schedule
|
99
105
|
- **Failed Jobs**: Jobs that have failed with error details
|
100
106
|
- **Queues**: Distribution of jobs across different queues
|
101
107
|
|
@@ -103,6 +109,20 @@ The dashboard provides several views:
|
|
103
109
|
|
104
110
|
For API-only Rails applications, SolidQueueMonitor works out of the box without requiring you to enable the asset pipeline or webpacker. This makes it an ideal choice for monitoring background jobs in modern API-based architectures.
|
105
111
|
|
112
|
+
## Use Cases
|
113
|
+
|
114
|
+
- **Production Monitoring**: Keep an eye on your background job processing in production environments
|
115
|
+
- **Debugging**: Quickly identify and troubleshoot failed jobs
|
116
|
+
- **Job Management**: Execute scheduled jobs on demand when needed
|
117
|
+
- **Performance Analysis**: Track job distribution and identify bottlenecks
|
118
|
+
- **DevOps Integration**: Easily integrate with your monitoring stack
|
119
|
+
|
120
|
+
## Compatibility
|
121
|
+
|
122
|
+
- **Ruby**: 3.1.6 or higher
|
123
|
+
- **Rails**: 7.1 or higher
|
124
|
+
- **Solid Queue**: 0.1.0 or higher
|
125
|
+
|
106
126
|
## Contributing
|
107
127
|
|
108
128
|
Contributions are welcome! Here's how you can contribute:
|
@@ -126,3 +146,9 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
126
146
|
## Code of Conduct
|
127
147
|
|
128
148
|
Everyone interacting in the SolidQueueMonitor project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/yourusername/solid_queue_monitor/blob/main/CODE_OF_CONDUCT.md).
|
149
|
+
|
150
|
+
## Related Projects
|
151
|
+
|
152
|
+
- [Solid Queue](https://github.com/rails/solid_queue) - The official Rails background job framework
|
153
|
+
- [Rails](https://github.com/rails/rails) - The web application framework
|
154
|
+
- [ActiveJob](https://github.com/rails/rails/tree/main/activejob) - Rails job frameworkk
|
@@ -0,0 +1,194 @@
|
|
1
|
+
module SolidQueueMonitor
|
2
|
+
class BaseController < 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
|
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
|
+
private
|
17
|
+
|
18
|
+
def authenticate
|
19
|
+
authenticate_or_request_with_http_basic do |username, password|
|
20
|
+
SolidQueueMonitor::AuthenticationService.authenticate(username, password)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def paginate(relation)
|
25
|
+
PaginationService.new(relation, current_page, per_page).paginate
|
26
|
+
end
|
27
|
+
|
28
|
+
def render_page(title, content)
|
29
|
+
# Get flash message from session
|
30
|
+
message = session[:flash_message]
|
31
|
+
message_type = session[:flash_type]
|
32
|
+
|
33
|
+
# Clear the flash message from session after using it
|
34
|
+
session.delete(:flash_message)
|
35
|
+
session.delete(:flash_type)
|
36
|
+
|
37
|
+
html = SolidQueueMonitor::HtmlGenerator.new(
|
38
|
+
title: title,
|
39
|
+
content: content,
|
40
|
+
message: message,
|
41
|
+
message_type: message_type
|
42
|
+
).generate
|
43
|
+
|
44
|
+
render html: html.html_safe
|
45
|
+
end
|
46
|
+
|
47
|
+
def current_page
|
48
|
+
(params[:page] || 1).to_i
|
49
|
+
end
|
50
|
+
|
51
|
+
def per_page
|
52
|
+
SolidQueueMonitor.jobs_per_page
|
53
|
+
end
|
54
|
+
|
55
|
+
# Preload job statuses to avoid N+1 queries
|
56
|
+
def preload_job_statuses(jobs)
|
57
|
+
return if jobs.empty?
|
58
|
+
|
59
|
+
# Get all job IDs
|
60
|
+
job_ids = jobs.map(&:id)
|
61
|
+
|
62
|
+
# Find all failed jobs in a single query
|
63
|
+
failed_job_ids = SolidQueue::FailedExecution.where(job_id: job_ids).pluck(:job_id)
|
64
|
+
|
65
|
+
# Find all scheduled jobs in a single query
|
66
|
+
scheduled_job_ids = SolidQueue::ScheduledExecution.where(job_id: job_ids).pluck(:job_id)
|
67
|
+
|
68
|
+
# Attach the status information to each job
|
69
|
+
jobs.each do |job|
|
70
|
+
job.instance_variable_set(:@failed, failed_job_ids.include?(job.id))
|
71
|
+
job.instance_variable_set(:@scheduled, scheduled_job_ids.include?(job.id))
|
72
|
+
end
|
73
|
+
|
74
|
+
# Define the method to check if a job is failed
|
75
|
+
SolidQueue::Job.class_eval do
|
76
|
+
def failed?
|
77
|
+
if instance_variable_defined?(:@failed)
|
78
|
+
@failed
|
79
|
+
else
|
80
|
+
SolidQueue::FailedExecution.exists?(job_id: id)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def scheduled?
|
85
|
+
if instance_variable_defined?(:@scheduled)
|
86
|
+
@scheduled
|
87
|
+
else
|
88
|
+
SolidQueue::ScheduledExecution.exists?(job_id: id)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def filter_jobs(relation)
|
95
|
+
relation = relation.where("class_name LIKE ?", "%#{params[:class_name]}%") if params[:class_name].present?
|
96
|
+
relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%") if params[:queue_name].present?
|
97
|
+
|
98
|
+
if params[:status].present?
|
99
|
+
case params[:status]
|
100
|
+
when 'completed'
|
101
|
+
relation = relation.where.not(finished_at: nil)
|
102
|
+
when 'failed'
|
103
|
+
failed_job_ids = SolidQueue::FailedExecution.pluck(:job_id)
|
104
|
+
relation = relation.where(id: failed_job_ids)
|
105
|
+
when 'scheduled'
|
106
|
+
scheduled_job_ids = SolidQueue::ScheduledExecution.pluck(:job_id)
|
107
|
+
relation = relation.where(id: scheduled_job_ids)
|
108
|
+
when 'pending'
|
109
|
+
# Pending jobs are those that are not completed, failed, or scheduled
|
110
|
+
failed_job_ids = SolidQueue::FailedExecution.pluck(:job_id)
|
111
|
+
scheduled_job_ids = SolidQueue::ScheduledExecution.pluck(:job_id)
|
112
|
+
relation = relation.where(finished_at: nil)
|
113
|
+
.where.not(id: failed_job_ids + scheduled_job_ids)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
relation
|
118
|
+
end
|
119
|
+
|
120
|
+
def filter_ready_jobs(relation)
|
121
|
+
return relation unless params[:class_name].present? || params[:queue_name].present?
|
122
|
+
|
123
|
+
if params[:class_name].present?
|
124
|
+
job_ids = SolidQueue::Job.where("class_name LIKE ?", "%#{params[:class_name]}%").pluck(:id)
|
125
|
+
relation = relation.where(job_id: job_ids)
|
126
|
+
end
|
127
|
+
|
128
|
+
if params[:queue_name].present?
|
129
|
+
relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
|
130
|
+
end
|
131
|
+
|
132
|
+
relation
|
133
|
+
end
|
134
|
+
|
135
|
+
def filter_scheduled_jobs(relation)
|
136
|
+
return relation unless params[:class_name].present? || params[:queue_name].present?
|
137
|
+
|
138
|
+
if params[:class_name].present?
|
139
|
+
job_ids = SolidQueue::Job.where("class_name LIKE ?", "%#{params[:class_name]}%").pluck(:id)
|
140
|
+
relation = relation.where(job_id: job_ids)
|
141
|
+
end
|
142
|
+
|
143
|
+
if params[:queue_name].present?
|
144
|
+
relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
|
145
|
+
end
|
146
|
+
|
147
|
+
relation
|
148
|
+
end
|
149
|
+
|
150
|
+
def filter_recurring_jobs(relation)
|
151
|
+
return relation unless params[:class_name].present? || params[:queue_name].present?
|
152
|
+
|
153
|
+
if params[:class_name].present?
|
154
|
+
relation = relation.where("class_name LIKE ?", "%#{params[:class_name]}%")
|
155
|
+
end
|
156
|
+
|
157
|
+
if params[:queue_name].present?
|
158
|
+
relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
|
159
|
+
end
|
160
|
+
|
161
|
+
relation
|
162
|
+
end
|
163
|
+
|
164
|
+
def filter_failed_jobs(relation)
|
165
|
+
return relation unless params[:class_name].present? || params[:queue_name].present?
|
166
|
+
|
167
|
+
if params[:class_name].present?
|
168
|
+
job_ids = SolidQueue::Job.where("class_name LIKE ?", "%#{params[:class_name]}%").pluck(:id)
|
169
|
+
relation = relation.where(job_id: job_ids)
|
170
|
+
end
|
171
|
+
|
172
|
+
if params[:queue_name].present?
|
173
|
+
# Check if FailedExecution has queue_name column
|
174
|
+
if relation.column_names.include?('queue_name')
|
175
|
+
relation = relation.where("queue_name LIKE ?", "%#{params[:queue_name]}%")
|
176
|
+
else
|
177
|
+
# If not, filter by job's queue_name
|
178
|
+
job_ids = SolidQueue::Job.where("queue_name LIKE ?", "%#{params[:queue_name]}%").pluck(:id)
|
179
|
+
relation = relation.where(job_id: job_ids)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
relation
|
184
|
+
end
|
185
|
+
|
186
|
+
def filter_params
|
187
|
+
{
|
188
|
+
class_name: params[:class_name],
|
189
|
+
queue_name: params[:queue_name],
|
190
|
+
status: params[:status]
|
191
|
+
}
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module SolidQueueMonitor
|
2
|
+
class FailedJobsController < BaseController
|
3
|
+
def index
|
4
|
+
base_query = SolidQueue::FailedExecution.includes(:job).order(created_at: :desc)
|
5
|
+
@failed_jobs = paginate(filter_failed_jobs(base_query))
|
6
|
+
|
7
|
+
render_page('Failed Jobs', SolidQueueMonitor::FailedJobsPresenter.new(@failed_jobs[:records],
|
8
|
+
current_page: @failed_jobs[:current_page],
|
9
|
+
total_pages: @failed_jobs[:total_pages],
|
10
|
+
filters: filter_params
|
11
|
+
).render)
|
12
|
+
end
|
13
|
+
|
14
|
+
def retry
|
15
|
+
id = params[:id]
|
16
|
+
service = SolidQueueMonitor::FailedJobService.new
|
17
|
+
|
18
|
+
if service.retry_job(id)
|
19
|
+
set_flash_message("Job #{id} has been queued for retry.", 'success')
|
20
|
+
else
|
21
|
+
set_flash_message("Failed to retry job #{id}.", 'error')
|
22
|
+
end
|
23
|
+
|
24
|
+
redirect_to params[:redirect_to].present? ? params[:redirect_to] : failed_jobs_path
|
25
|
+
end
|
26
|
+
|
27
|
+
def discard
|
28
|
+
id = params[:id]
|
29
|
+
service = SolidQueueMonitor::FailedJobService.new
|
30
|
+
|
31
|
+
if service.discard_job(id)
|
32
|
+
set_flash_message("Job #{id} has been discarded.", 'success')
|
33
|
+
else
|
34
|
+
set_flash_message("Failed to discard job #{id}.", 'error')
|
35
|
+
end
|
36
|
+
|
37
|
+
redirect_to params[:redirect_to].present? ? params[:redirect_to] : failed_jobs_path
|
38
|
+
end
|
39
|
+
|
40
|
+
def retry_all
|
41
|
+
result = SolidQueueMonitor::FailedJobService.new.retry_all(params[:job_ids])
|
42
|
+
|
43
|
+
if result[:success]
|
44
|
+
set_flash_message(result[:message], 'success')
|
45
|
+
else
|
46
|
+
set_flash_message(result[:message], 'error')
|
47
|
+
end
|
48
|
+
redirect_to failed_jobs_path
|
49
|
+
end
|
50
|
+
|
51
|
+
def discard_all
|
52
|
+
result = SolidQueueMonitor::FailedJobService.new.discard_all(params[:job_ids])
|
53
|
+
|
54
|
+
if result[:success]
|
55
|
+
set_flash_message(result[:message], 'success')
|
56
|
+
else
|
57
|
+
set_flash_message(result[:message], 'error')
|
58
|
+
end
|
59
|
+
redirect_to failed_jobs_path
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module SolidQueueMonitor
|
2
|
+
class InProgressJobsController < BaseController
|
3
|
+
def index
|
4
|
+
base_query = SolidQueue::ClaimedExecution.includes(:job).order(created_at: :desc)
|
5
|
+
@in_progress_jobs = paginate(filter_in_progress_jobs(base_query))
|
6
|
+
|
7
|
+
render_page('In Progress Jobs', SolidQueueMonitor::InProgressJobsPresenter.new(@in_progress_jobs[:records],
|
8
|
+
current_page: @in_progress_jobs[:current_page],
|
9
|
+
total_pages: @in_progress_jobs[:total_pages],
|
10
|
+
filters: filter_params
|
11
|
+
).render)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def filter_in_progress_jobs(relation)
|
17
|
+
return relation unless params[:class_name].present?
|
18
|
+
|
19
|
+
if params[:class_name].present?
|
20
|
+
job_ids = SolidQueue::Job.where("class_name LIKE ?", "%#{params[:class_name]}%").pluck(:id)
|
21
|
+
relation = relation.where(job_id: job_ids)
|
22
|
+
end
|
23
|
+
|
24
|
+
relation
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,10 +1,17 @@
|
|
1
1
|
module SolidQueueMonitor
|
2
2
|
class MonitorController < ActionController::Base
|
3
3
|
include ActionController::HttpAuthentication::Basic::ControllerMethods
|
4
|
+
include ActionController::Flash
|
4
5
|
|
5
6
|
before_action :authenticate, if: -> { SolidQueueMonitor::AuthenticationService.authentication_required? }
|
6
7
|
layout false
|
7
|
-
skip_before_action :verify_authenticity_token, only: [:execute_jobs]
|
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
|
8
15
|
|
9
16
|
def index
|
10
17
|
@stats = SolidQueueMonitor::StatsCalculator.calculate
|
@@ -68,11 +75,57 @@ module SolidQueueMonitor
|
|
68
75
|
def execute_jobs
|
69
76
|
if params[:job_ids].present?
|
70
77
|
SolidQueueMonitor::ExecuteJobService.new.execute_many(params[:job_ids])
|
71
|
-
|
78
|
+
set_flash_message('Selected jobs moved to ready queue', 'success')
|
72
79
|
else
|
73
|
-
|
80
|
+
set_flash_message('No jobs selected', 'error')
|
74
81
|
end
|
75
|
-
redirect_to
|
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
|
76
129
|
end
|
77
130
|
|
78
131
|
private
|
@@ -88,11 +141,19 @@ module SolidQueueMonitor
|
|
88
141
|
end
|
89
142
|
|
90
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
|
+
|
91
152
|
html = SolidQueueMonitor::HtmlGenerator.new(
|
92
153
|
title: title,
|
93
154
|
content: content,
|
94
|
-
message:
|
95
|
-
message_type:
|
155
|
+
message: message,
|
156
|
+
message_type: message_type
|
96
157
|
).generate
|
97
158
|
|
98
159
|
render html: html.html_safe
|
@@ -233,7 +294,14 @@ module SolidQueueMonitor
|
|
233
294
|
end
|
234
295
|
|
235
296
|
if params[:queue_name].present?
|
236
|
-
|
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
|
237
305
|
end
|
238
306
|
|
239
307
|
relation
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module SolidQueueMonitor
|
2
|
+
class OverviewController < BaseController
|
3
|
+
def index
|
4
|
+
@stats = SolidQueueMonitor::StatsCalculator.calculate
|
5
|
+
|
6
|
+
recent_jobs_query = SolidQueue::Job.order(created_at: :desc).limit(100)
|
7
|
+
@recent_jobs = paginate(filter_jobs(recent_jobs_query))
|
8
|
+
|
9
|
+
preload_job_statuses(@recent_jobs[:records])
|
10
|
+
|
11
|
+
render_page('Overview', generate_overview_content)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def generate_overview_content
|
17
|
+
SolidQueueMonitor::StatsPresenter.new(@stats).render +
|
18
|
+
SolidQueueMonitor::JobsPresenter.new(@recent_jobs[:records],
|
19
|
+
current_page: @recent_jobs[:current_page],
|
20
|
+
total_pages: @recent_jobs[:total_pages],
|
21
|
+
filters: filter_params
|
22
|
+
).render
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module SolidQueueMonitor
|
2
|
+
class QueuesController < BaseController
|
3
|
+
def index
|
4
|
+
@queues = SolidQueue::Job.group(:queue_name)
|
5
|
+
.select('queue_name, COUNT(*) as job_count')
|
6
|
+
.order('job_count DESC')
|
7
|
+
|
8
|
+
render_page('Queues', SolidQueueMonitor::QueuesPresenter.new(@queues).render)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SolidQueueMonitor
|
2
|
+
class ReadyJobsController < BaseController
|
3
|
+
def index
|
4
|
+
base_query = SolidQueue::ReadyExecution.includes(:job).order(created_at: :desc)
|
5
|
+
@ready_jobs = paginate(filter_ready_jobs(base_query))
|
6
|
+
|
7
|
+
render_page('Ready Jobs', SolidQueueMonitor::ReadyJobsPresenter.new(@ready_jobs[:records],
|
8
|
+
current_page: @ready_jobs[:current_page],
|
9
|
+
total_pages: @ready_jobs[:total_pages],
|
10
|
+
filters: filter_params
|
11
|
+
).render)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SolidQueueMonitor
|
2
|
+
class RecurringJobsController < BaseController
|
3
|
+
def index
|
4
|
+
base_query = filter_recurring_jobs(SolidQueue::RecurringTask.order(:key))
|
5
|
+
@recurring_jobs = paginate(base_query)
|
6
|
+
|
7
|
+
render_page('Recurring Jobs', SolidQueueMonitor::RecurringJobsPresenter.new(@recurring_jobs[:records],
|
8
|
+
current_page: @recurring_jobs[:current_page],
|
9
|
+
total_pages: @recurring_jobs[:total_pages],
|
10
|
+
filters: filter_params
|
11
|
+
).render)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SolidQueueMonitor
|
2
|
+
class ScheduledJobsController < BaseController
|
3
|
+
def index
|
4
|
+
base_query = SolidQueue::ScheduledExecution.includes(:job).order(scheduled_at: :asc)
|
5
|
+
@scheduled_jobs = paginate(filter_scheduled_jobs(base_query))
|
6
|
+
|
7
|
+
render_page('Scheduled Jobs', SolidQueueMonitor::ScheduledJobsPresenter.new(@scheduled_jobs[:records],
|
8
|
+
current_page: @scheduled_jobs[:current_page],
|
9
|
+
total_pages: @scheduled_jobs[:total_pages],
|
10
|
+
filters: filter_params
|
11
|
+
).render)
|
12
|
+
end
|
13
|
+
|
14
|
+
def create
|
15
|
+
if params[:job_ids].present?
|
16
|
+
SolidQueueMonitor::ExecuteJobService.new.execute_many(params[:job_ids])
|
17
|
+
set_flash_message('Selected jobs moved to ready queue', 'success')
|
18
|
+
else
|
19
|
+
set_flash_message('No jobs selected', 'error')
|
20
|
+
end
|
21
|
+
redirect_to scheduled_jobs_path
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|