solid_job_monitor 1.0.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +106 -0
- data/Rakefile +8 -0
- data/app/assets/config/mission_control_jobs_manifest.js +4 -0
- data/app/assets/stylesheets/mission_control/jobs/application.css +16 -0
- data/app/assets/stylesheets/mission_control/jobs/bulma.min.css +21013 -0
- data/app/assets/stylesheets/mission_control/jobs/forms.css +8 -0
- data/app/assets/stylesheets/mission_control/jobs/jobs.css +68 -0
- data/app/controllers/concerns/mission_control/jobs/adapter_features.rb +24 -0
- data/app/controllers/concerns/mission_control/jobs/application_scoped.rb +38 -0
- data/app/controllers/concerns/mission_control/jobs/basic_authentication.rb +33 -0
- data/app/controllers/concerns/mission_control/jobs/failed_jobs_bulk_operations.rb +17 -0
- data/app/controllers/concerns/mission_control/jobs/job_filters.rb +41 -0
- data/app/controllers/concerns/mission_control/jobs/job_scoped.rb +16 -0
- data/app/controllers/concerns/mission_control/jobs/not_found_redirections.rb +36 -0
- data/app/controllers/concerns/mission_control/jobs/queue_scoped.rb +12 -0
- data/app/controllers/mission_control/jobs/application_controller.rb +32 -0
- data/app/controllers/mission_control/jobs/bulk_discards_controller.rb +20 -0
- data/app/controllers/mission_control/jobs/bulk_retries_controller.rb +10 -0
- data/app/controllers/mission_control/jobs/discards_controller.rb +18 -0
- data/app/controllers/mission_control/jobs/dispatches_controller.rb +18 -0
- data/app/controllers/mission_control/jobs/jobs_controller.rb +54 -0
- data/app/controllers/mission_control/jobs/progresses_controller.rb +27 -0
- data/app/controllers/mission_control/jobs/queues/pauses_controller.rb +15 -0
- data/app/controllers/mission_control/jobs/queues_controller.rb +16 -0
- data/app/controllers/mission_control/jobs/recurring_tasks_controller.rb +38 -0
- data/app/controllers/mission_control/jobs/retries_controller.rb +13 -0
- data/app/controllers/mission_control/jobs/stops_controller.rb +22 -0
- data/app/controllers/mission_control/jobs/workers_controller.rb +23 -0
- data/app/helpers/mission_control/jobs/application_helper.rb +9 -0
- data/app/helpers/mission_control/jobs/dates_helper.rb +19 -0
- data/app/helpers/mission_control/jobs/interface_helper.rb +23 -0
- data/app/helpers/mission_control/jobs/jobs_helper.rb +94 -0
- data/app/helpers/mission_control/jobs/navigation_helper.rb +60 -0
- data/app/javascript/mission_control/jobs/application.js +4 -0
- data/app/javascript/mission_control/jobs/controllers/application.js +9 -0
- data/app/javascript/mission_control/jobs/controllers/form_controller.js +21 -0
- data/app/javascript/mission_control/jobs/controllers/index.js +11 -0
- data/app/javascript/mission_control/jobs/controllers/progress_controller.js +55 -0
- data/app/javascript/mission_control/jobs/helpers/debounce_helpers.js +9 -0
- data/app/javascript/mission_control/jobs/helpers/index.js +1 -0
- data/app/models/mission_control/jobs/application_record.rb +7 -0
- data/app/models/mission_control/jobs/current.rb +3 -0
- data/app/models/mission_control/jobs/job_progress.rb +7 -0
- data/app/models/mission_control/jobs/page.rb +48 -0
- data/app/models/mission_control/jobs/recurring_task.rb +25 -0
- data/app/models/mission_control/jobs/worker.rb +17 -0
- data/app/views/layouts/mission_control/jobs/_application_selection.html.erb +14 -0
- data/app/views/layouts/mission_control/jobs/_flash.html.erb +9 -0
- data/app/views/layouts/mission_control/jobs/_navigation.html.erb +9 -0
- data/app/views/layouts/mission_control/jobs/application.html.erb +27 -0
- data/app/views/layouts/mission_control/jobs/application_selection/_applications.html.erb +13 -0
- data/app/views/layouts/mission_control/jobs/application_selection/_servers.html.erb +15 -0
- data/app/views/mission_control/jobs/jobs/_error_information.html.erb +22 -0
- data/app/views/mission_control/jobs/jobs/_filters.html.erb +55 -0
- data/app/views/mission_control/jobs/jobs/_general_information.html.erb +85 -0
- data/app/views/mission_control/jobs/jobs/_job.html.erb +13 -0
- data/app/views/mission_control/jobs/jobs/_jobs_page.html.erb +13 -0
- data/app/views/mission_control/jobs/jobs/_progress_bar.html.erb +16 -0
- data/app/views/mission_control/jobs/jobs/_raw_data.html.erb +4 -0
- data/app/views/mission_control/jobs/jobs/_title.html.erb +22 -0
- data/app/views/mission_control/jobs/jobs/_toolbar.html.erb +18 -0
- data/app/views/mission_control/jobs/jobs/blocked/_actions.html.erb +3 -0
- data/app/views/mission_control/jobs/jobs/blocked/_job.html.erb +8 -0
- data/app/views/mission_control/jobs/jobs/failed/_actions.html.erb +5 -0
- data/app/views/mission_control/jobs/jobs/failed/_backtrace_toggle.html.erb +14 -0
- data/app/views/mission_control/jobs/jobs/failed/_job.html.erb +7 -0
- data/app/views/mission_control/jobs/jobs/finished/_job.html.erb +2 -0
- data/app/views/mission_control/jobs/jobs/in_progress/_actions.html.erb +6 -0
- data/app/views/mission_control/jobs/jobs/in_progress/_job.html.erb +13 -0
- data/app/views/mission_control/jobs/jobs/index.html.erb +19 -0
- data/app/views/mission_control/jobs/jobs/scheduled/_actions.html.erb +5 -0
- data/app/views/mission_control/jobs/jobs/scheduled/_job.html.erb +13 -0
- data/app/views/mission_control/jobs/jobs/show.html.erb +6 -0
- data/app/views/mission_control/jobs/queues/_actions.html.erb +7 -0
- data/app/views/mission_control/jobs/queues/_job.html.erb +15 -0
- data/app/views/mission_control/jobs/queues/_queue.html.erb +16 -0
- data/app/views/mission_control/jobs/queues/_queue_title.html.erb +17 -0
- data/app/views/mission_control/jobs/queues/index.html.erb +16 -0
- data/app/views/mission_control/jobs/queues/show.html.erb +25 -0
- data/app/views/mission_control/jobs/recurring_tasks/_actions.html.erb +5 -0
- data/app/views/mission_control/jobs/recurring_tasks/_general_information.html.erb +37 -0
- data/app/views/mission_control/jobs/recurring_tasks/_recurring_task.html.erb +22 -0
- data/app/views/mission_control/jobs/recurring_tasks/_title.html.erb +10 -0
- data/app/views/mission_control/jobs/recurring_tasks/index.html.erb +18 -0
- data/app/views/mission_control/jobs/recurring_tasks/show.html.erb +14 -0
- data/app/views/mission_control/jobs/shared/_job.html.erb +27 -0
- data/app/views/mission_control/jobs/shared/_jobs.html.erb +14 -0
- data/app/views/mission_control/jobs/shared/_pagination_toolbar.html.erb +7 -0
- data/app/views/mission_control/jobs/workers/_configuration.html.erb +6 -0
- data/app/views/mission_control/jobs/workers/_raw_data.html.erb +6 -0
- data/app/views/mission_control/jobs/workers/_title.html.erb +11 -0
- data/app/views/mission_control/jobs/workers/_worker.html.erb +21 -0
- data/app/views/mission_control/jobs/workers/_workers_page.html.erb +15 -0
- data/app/views/mission_control/jobs/workers/index.html.erb +6 -0
- data/app/views/mission_control/jobs/workers/show.html.erb +14 -0
- data/config/importmap.rb +6 -0
- data/config/routes.rb +35 -0
- data/db/migrate/20260828105144_create_mission_control_job_progresses.rb +16 -0
- data/lib/active_job/errors/invalid_operation.rb +5 -0
- data/lib/active_job/errors/job_not_found_error.rb +14 -0
- data/lib/active_job/errors/query_error.rb +5 -0
- data/lib/active_job/executing.rb +40 -0
- data/lib/active_job/execution_error.rb +8 -0
- data/lib/active_job/failed.rb +7 -0
- data/lib/active_job/job_proxy.rb +38 -0
- data/lib/active_job/jobs_relation.rb +318 -0
- data/lib/active_job/progress_reporting.rb +21 -0
- data/lib/active_job/querying.rb +44 -0
- data/lib/active_job/queue.rb +62 -0
- data/lib/active_job/queue_adapters/async_ext.rb +49 -0
- data/lib/active_job/queue_adapters/resque_ext.rb +317 -0
- data/lib/active_job/queue_adapters/solid_queue_ext/recurring_tasks.rb +52 -0
- data/lib/active_job/queue_adapters/solid_queue_ext/workers.rb +41 -0
- data/lib/active_job/queue_adapters/solid_queue_ext.rb +313 -0
- data/lib/active_job/queues.rb +33 -0
- data/lib/mission_control/jobs/adapter.rb +156 -0
- data/lib/mission_control/jobs/application.rb +20 -0
- data/lib/mission_control/jobs/applications.rb +8 -0
- data/lib/mission_control/jobs/arguments_filter.rb +24 -0
- data/lib/mission_control/jobs/authentication.rb +67 -0
- data/lib/mission_control/jobs/console/connect_to.rb +15 -0
- data/lib/mission_control/jobs/console/context.rb +11 -0
- data/lib/mission_control/jobs/console/jobs_help.rb +23 -0
- data/lib/mission_control/jobs/engine.rb +118 -0
- data/lib/mission_control/jobs/errors/incompatible_adapter.rb +2 -0
- data/lib/mission_control/jobs/errors/resource_not_found.rb +2 -0
- data/lib/mission_control/jobs/i18n_config.rb +17 -0
- data/lib/mission_control/jobs/identified_by_name.rb +18 -0
- data/lib/mission_control/jobs/identified_elements.rb +23 -0
- data/lib/mission_control/jobs/job_stopped_error.rb +5 -0
- data/lib/mission_control/jobs/progress/null_store.rb +25 -0
- data/lib/mission_control/jobs/progress/reporter.rb +63 -0
- data/lib/mission_control/jobs/progress/store.rb +72 -0
- data/lib/mission_control/jobs/progress.rb +17 -0
- data/lib/mission_control/jobs/server/recurring_tasks.rb +15 -0
- data/lib/mission_control/jobs/server/serializable.rb +24 -0
- data/lib/mission_control/jobs/server/workers.rb +13 -0
- data/lib/mission_control/jobs/server.rb +27 -0
- data/lib/mission_control/jobs/tasks.rb +8 -0
- data/lib/mission_control/jobs/version.rb +5 -0
- data/lib/mission_control/jobs/workers_relation.rb +78 -0
- data/lib/mission_control/jobs.rb +43 -0
- data/lib/resque/thread_safe_redis.rb +34 -0
- data/lib/solid_job_monitor.rb +6 -0
- metadata +527 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
module ActiveJob::QueueAdapters::ResqueExt
|
|
2
|
+
include MissionControl::Jobs::Adapter
|
|
3
|
+
|
|
4
|
+
def initialize(redis = Resque.redis)
|
|
5
|
+
super()
|
|
6
|
+
@redis = redis
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def activating(&block)
|
|
10
|
+
Resque.with_per_thread_redis_override(redis, &block)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def queues
|
|
14
|
+
queues = queue_names
|
|
15
|
+
active_statuses = []
|
|
16
|
+
counts = []
|
|
17
|
+
|
|
18
|
+
redis.multi do |multi|
|
|
19
|
+
queues.each do |queue_name|
|
|
20
|
+
active_statuses << multi.mget("pause:queue:#{queue_name}", "pause:all")
|
|
21
|
+
counts << multi.llen("queue:#{queue_name}")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
queues.collect.with_index do |queue_name, index|
|
|
26
|
+
{ name: queue_name, active: active_statuses[index].value.compact.empty?, size: counts[index].value }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def queue_size(queue_name)
|
|
31
|
+
Resque.size queue_name
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def clear_queue(queue_name)
|
|
35
|
+
Resque.remove_queue(queue_name)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def pause_queue(queue_name)
|
|
39
|
+
ResquePauseHelper.pause(queue_name)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def resume_queue(queue_name)
|
|
43
|
+
ResquePauseHelper.unpause(queue_name)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def queue_paused?(queue_name)
|
|
47
|
+
ResquePauseHelper.paused?(queue_name)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def supported_job_filters(jobs_relation)
|
|
51
|
+
if jobs_relation.pending? then [ :queue_name ]
|
|
52
|
+
else []
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def jobs_count(jobs_relation)
|
|
57
|
+
resque_jobs_for(jobs_relation).count
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def fetch_jobs(jobs_relation)
|
|
61
|
+
resque_jobs_for(jobs_relation).all
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def retry_all_jobs(jobs_relation)
|
|
65
|
+
resque_jobs_for(jobs_relation).retry_all
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def retry_job(job, jobs_relation)
|
|
69
|
+
resque_jobs_for(jobs_relation).retry_job(job)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def discard_all_jobs(jobs_relation)
|
|
73
|
+
resque_jobs_for(jobs_relation).discard_all
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def discard_job(job, jobs_relation)
|
|
77
|
+
resque_jobs_for(jobs_relation).discard(job)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def find_job(job_id, jobs_relation)
|
|
81
|
+
resque_jobs_for(jobs_relation).find_job(job_id)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def supports_queue_pausing?
|
|
85
|
+
defined?(ResquePauseHelper)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
attr_reader :redis
|
|
90
|
+
|
|
91
|
+
def queue_names
|
|
92
|
+
Resque.queues
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def resque_jobs_for(jobs_relation)
|
|
96
|
+
ResqueJobs.new(jobs_relation, redis: redis)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class ResqueJobs
|
|
100
|
+
attr_reader :jobs_relation
|
|
101
|
+
|
|
102
|
+
delegate :default_page_size, :paginated?, :limit_value_provided?, to: :jobs_relation
|
|
103
|
+
|
|
104
|
+
def initialize(jobs_relation, redis:)
|
|
105
|
+
@jobs_relation = jobs_relation
|
|
106
|
+
@redis = redis
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def count
|
|
110
|
+
if paginated?
|
|
111
|
+
count_fetched_jobs # no direct way of counting jobs
|
|
112
|
+
else
|
|
113
|
+
direct_jobs_count
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def all
|
|
118
|
+
@all ||= fetch_resque_jobs.collect.with_index { |resque_job, index| deserialize_resque_job(resque_job, index) if resque_job.is_a?(Hash) }.compact
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def retry_all
|
|
122
|
+
if use_batches?
|
|
123
|
+
retry_all_in_batches
|
|
124
|
+
else
|
|
125
|
+
retry_jobs(jobs_relation.to_a.reverse)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def retry_job(job)
|
|
130
|
+
# Not named just +retry+ because it collides with reserved Ruby keyword.
|
|
131
|
+
resque_requeue_and_discard(job)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def discard_all
|
|
135
|
+
if jobs_relation.failed? && targeting_all_jobs?
|
|
136
|
+
clear_failed_queue
|
|
137
|
+
else
|
|
138
|
+
discard_all_one_by_one
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def discard(job)
|
|
143
|
+
redis.multi do |multi|
|
|
144
|
+
multi.lset(queue_redis_key, job.position, SENTINEL)
|
|
145
|
+
multi.lrem(queue_redis_key, 1, SENTINEL)
|
|
146
|
+
end
|
|
147
|
+
rescue Redis::CommandError => error
|
|
148
|
+
handle_resque_job_error(job, error)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def find_job(job_id)
|
|
152
|
+
jobs_by_id[job_id]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
private
|
|
156
|
+
attr_reader :redis
|
|
157
|
+
|
|
158
|
+
SENTINEL = "" # See +Resque::Datastore#remove_from_failed_queue+
|
|
159
|
+
|
|
160
|
+
# Redis transactions severely speed up operations, specially when the network latency is high.
|
|
161
|
+
# We limit the transaction size because large batches can result in redis timeout errors.
|
|
162
|
+
MAX_REDIS_TRANSACTION_SIZE = 100
|
|
163
|
+
|
|
164
|
+
def targeting_all_jobs?
|
|
165
|
+
!paginated? && !jobs_relation.filtering_needed?
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def fetch_resque_jobs
|
|
169
|
+
if jobs_relation.failed? || jobs_relation.queue_name.blank?
|
|
170
|
+
fetch_failed_resque_jobs
|
|
171
|
+
else
|
|
172
|
+
fetch_queue_resque_jobs
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def fetch_failed_resque_jobs
|
|
177
|
+
Array.wrap(Resque::Failure.all(jobs_relation.offset_value, jobs_relation.limit_value))
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def fetch_queue_resque_jobs
|
|
181
|
+
unless jobs_relation.queue_name.present?
|
|
182
|
+
raise ActiveJob::Errors::QueryError, "This adapter requires a queue name unless fetching failed jobs"
|
|
183
|
+
end
|
|
184
|
+
Array.wrap(Resque.peek(jobs_relation.queue_name, jobs_relation.offset_value, jobs_relation.limit_value))
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def deserialize_resque_job(resque_job_hash, index)
|
|
188
|
+
args_hash = extract_args_hash(resque_job_hash)
|
|
189
|
+
ActiveJob::JobProxy.new(args_hash).tap do |job|
|
|
190
|
+
job.last_execution_error = execution_error_from_resque_job(resque_job_hash)
|
|
191
|
+
job.raw_data = resque_job_hash
|
|
192
|
+
job.filtered_raw_data = filter_raw_data_arguments(resque_job_hash)
|
|
193
|
+
job.position = jobs_relation.offset_value + index
|
|
194
|
+
job.failed_at = resque_job_hash["failed_at"]&.to_datetime&.utc
|
|
195
|
+
job.status = job.failed_at.present? ? :failed : :pending
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def filter_raw_data_arguments(raw_data)
|
|
200
|
+
raw_data.deep_dup.tap do |filtered_data|
|
|
201
|
+
if args_hash = extract_args_hash(filtered_data)
|
|
202
|
+
args_hash["arguments"] = MissionControl::Jobs.job_arguments_filter.apply_to(args_hash["arguments"])
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def extract_args_hash(raw_data)
|
|
208
|
+
(raw_data.dig("payload", "args") || raw_data.dig("args"))&.first
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def execution_error_from_resque_job(resque_job_hash)
|
|
212
|
+
if resque_job_hash["exception"].present?
|
|
213
|
+
ActiveJob::ExecutionError.new \
|
|
214
|
+
error_class: resque_job_hash["exception"],
|
|
215
|
+
message: resque_job_hash["error"],
|
|
216
|
+
backtrace: resque_job_hash["backtrace"]
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def direct_jobs_count
|
|
221
|
+
jobs_relation.failed? ? failed_jobs_count : pending_jobs_count
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def pending_jobs_count
|
|
225
|
+
Resque.queue_sizes.inject(0) do |sum, (queue_name, queue_size)|
|
|
226
|
+
if jobs_relation.queue_name.blank? || jobs_relation.queue_name == queue_name
|
|
227
|
+
sum + queue_size
|
|
228
|
+
else
|
|
229
|
+
sum
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def failed_jobs_count
|
|
235
|
+
Resque.data_store.num_failed
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def count_fetched_jobs
|
|
239
|
+
all.size
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def queue_redis_key
|
|
243
|
+
jobs_relation.failed? ? "failed" : "queue:#{jobs_relation.queue_name}"
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def clear_failed_queue
|
|
247
|
+
Resque::Failure.clear("failed")
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def retry_jobs(jobs)
|
|
251
|
+
in_transactional_jobs_batches(jobs) do |jobs_batch|
|
|
252
|
+
jobs_batch.each { |job| retry_job(job) }
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def in_transactional_jobs_batches(jobs)
|
|
257
|
+
jobs.each_slice(MAX_REDIS_TRANSACTION_SIZE) do |jobs_batch|
|
|
258
|
+
redis.multi do |multi|
|
|
259
|
+
yield jobs_batch
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def use_batches?
|
|
265
|
+
!jobs_relation.limit_value_provided? && jobs_relation.count > default_page_size
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def retry_all_in_batches
|
|
269
|
+
jobs_relation.in_batches(order: :desc, &:retry_all)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def resque_requeue_and_discard(job)
|
|
273
|
+
requeue(job)
|
|
274
|
+
discard(job)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def requeue(job)
|
|
278
|
+
resque_job = job.raw_data
|
|
279
|
+
resque_job["retried_at"] = Time.now.utc.strftime("%Y/%m/%d %H:%M:%S")
|
|
280
|
+
|
|
281
|
+
redis.lset(queue_redis_key, job.position, Resque.encode(resque_job))
|
|
282
|
+
Resque::Job.create(resque_job["queue"], resque_job["payload"]["class"], *resque_job["payload"]["args"])
|
|
283
|
+
rescue Redis::CommandError => error
|
|
284
|
+
handle_resque_job_error(job, error)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def discard_all_one_by_one
|
|
288
|
+
if use_batches?
|
|
289
|
+
discard_all_in_batches
|
|
290
|
+
else
|
|
291
|
+
discard_jobs(jobs_relation.to_a.reverse)
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def discard_jobs(jobs)
|
|
296
|
+
in_transactional_jobs_batches(jobs) do |jobs_batch|
|
|
297
|
+
jobs_batch.each { |job| discard(job) }
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def discard_all_in_batches
|
|
302
|
+
jobs_relation.in_batches(order: :desc, &:discard_all)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def jobs_by_id
|
|
306
|
+
@jobs_by_id ||= all.index_by(&:job_id)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def handle_resque_job_error(job, error)
|
|
310
|
+
if error.message =~/no such key/i
|
|
311
|
+
raise ActiveJob::Errors::JobNotFoundError.new(job, jobs_relation)
|
|
312
|
+
else
|
|
313
|
+
raise error
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module ActiveJob::QueueAdapters::SolidQueueExt::RecurringTasks
|
|
2
|
+
def supports_recurring_tasks?
|
|
3
|
+
true
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def recurring_tasks
|
|
7
|
+
tasks = SolidQueue::RecurringTask.all
|
|
8
|
+
last_enqueued_at_times = recurring_task_last_enqueued_at(tasks.map(&:key))
|
|
9
|
+
|
|
10
|
+
tasks.collect do |task|
|
|
11
|
+
recurring_task_attributes_from_solid_queue_recurring_task(task).merge \
|
|
12
|
+
last_enqueued_at: last_enqueued_at_times[task.key]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def find_recurring_task(task_id)
|
|
17
|
+
if task = SolidQueue::RecurringTask.find_by(key: task_id)
|
|
18
|
+
recurring_task_attributes_from_solid_queue_recurring_task(task).merge \
|
|
19
|
+
last_enqueued_at: recurring_task_last_enqueued_at(task.key).values&.first
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def enqueue_recurring_task(task_id)
|
|
24
|
+
if task = SolidQueue::RecurringTask.find_by(key: task_id)
|
|
25
|
+
task.enqueue(at: Time.now)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def can_enqueue_recurring_task?(task_id)
|
|
30
|
+
if task = SolidQueue::RecurringTask.find_by(key: task_id)
|
|
31
|
+
task.valid?
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
def recurring_task_attributes_from_solid_queue_recurring_task(task)
|
|
37
|
+
{
|
|
38
|
+
id: task.key,
|
|
39
|
+
job_class_name: task.class_name,
|
|
40
|
+
command: task.command,
|
|
41
|
+
arguments: task.arguments,
|
|
42
|
+
schedule: task.schedule,
|
|
43
|
+
next_time: task.next_time,
|
|
44
|
+
queue_name: task.queue_name,
|
|
45
|
+
priority: task.priority
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def recurring_task_last_enqueued_at(task_keys)
|
|
50
|
+
SolidQueue::RecurringExecution.where(task_key: task_keys).group(:task_key).maximum(:run_at)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module ActiveJob::QueueAdapters::SolidQueueExt::Workers
|
|
2
|
+
def exposes_workers?
|
|
3
|
+
true
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def fetch_workers(workers_relation)
|
|
7
|
+
solid_queue_processes_from_workers_relation(workers_relation).collect do |process|
|
|
8
|
+
worker_from_solid_queue_process(process)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def count_workers(workers_relation)
|
|
13
|
+
solid_queue_processes_from_workers_relation(workers_relation).count
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def find_worker(worker_id)
|
|
17
|
+
if process = SolidQueue::Process.find_by(id: worker_id)
|
|
18
|
+
worker_attributes_from_solid_queue_process(process)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
def solid_queue_processes_from_workers_relation(relation)
|
|
24
|
+
SolidQueue::Process.where(kind: "Worker").offset(relation.offset_value).limit(relation.limit_value)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def worker_from_solid_queue_process(process)
|
|
28
|
+
MissionControl::Jobs::Worker.new(queue_adapter: self, **worker_attributes_from_solid_queue_process(process))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def worker_attributes_from_solid_queue_process(process)
|
|
32
|
+
{
|
|
33
|
+
id: process.id,
|
|
34
|
+
name: "PID: #{process.pid}",
|
|
35
|
+
hostname: process.hostname,
|
|
36
|
+
last_heartbeat_at: process.last_heartbeat_at,
|
|
37
|
+
configuration: process.metadata,
|
|
38
|
+
raw_data: process.as_json
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
end
|