flight_control 2.0.0.beta.1
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/CHANGELOG.md +12 -0
- data/MIT-LICENSE +20 -0
- data/README.md +265 -0
- data/Rakefile +6 -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 +41 -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 +39 -0
- data/lib/active_job/jobs_relation.rb +321 -0
- data/lib/active_job/querying.rb +45 -0
- data/lib/active_job/queue.rb +63 -0
- data/lib/active_job/queue_adapters/solid_queue_ext/recurring_tasks.rb +53 -0
- data/lib/active_job/queue_adapters/solid_queue_ext/workers.rb +42 -0
- data/lib/active_job/queue_adapters/solid_queue_ext.rb +309 -0
- data/lib/active_job/queues.rb +34 -0
- data/lib/flight_control/adapter.rb +155 -0
- data/lib/flight_control/application.rb +20 -0
- data/lib/flight_control/applications.rb +8 -0
- data/lib/flight_control/arguments_filter.rb +25 -0
- data/lib/flight_control/authentication.rb +68 -0
- data/lib/flight_control/console/connect_to.rb +15 -0
- data/lib/flight_control/console/context.rb +11 -0
- data/lib/flight_control/console/jobs_help.rb +23 -0
- data/lib/flight_control/engine.rb +102 -0
- data/lib/flight_control/errors/incompatible_adapter.rb +2 -0
- data/lib/flight_control/errors/resource_not_found.rb +2 -0
- data/lib/flight_control/errors/unsupported_adapter.rb +2 -0
- data/lib/flight_control/i18n_config.rb +17 -0
- data/lib/flight_control/identified_by_name.rb +18 -0
- data/lib/flight_control/identified_elements.rb +24 -0
- data/lib/flight_control/server/recurring_tasks.rb +15 -0
- data/lib/flight_control/server/serializable.rb +24 -0
- data/lib/flight_control/server/workers.rb +13 -0
- data/lib/flight_control/server.rb +33 -0
- data/lib/flight_control/tasks.rb +6 -0
- data/lib/flight_control/version.rb +3 -0
- data/lib/flight_control/workers_relation.rb +79 -0
- data/lib/flight_control.rb +39 -0
- metadata +377 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# A relation of jobs that can be filtered and acted on.
|
|
2
|
+
#
|
|
3
|
+
# Relations of jobs are normally fetched via +ActiveJob.jobs+
|
|
4
|
+
# or through a given queue (+ActiveJob::Queue#jobs+).
|
|
5
|
+
#
|
|
6
|
+
# This class offers a fluid interface to query a subset of jobs. For
|
|
7
|
+
# example:
|
|
8
|
+
#
|
|
9
|
+
# queue = ActiveJob.queues[:default]
|
|
10
|
+
# queue.jobs.limit(10).where(job_class_name: "DummyJob").last
|
|
11
|
+
#
|
|
12
|
+
# Relations are enumerable, so you can use +Enumerable+ methods on them.
|
|
13
|
+
# Notice however that using these methods will imply loading all the relation
|
|
14
|
+
# in memory, which could introduce performance concerns.
|
|
15
|
+
#
|
|
16
|
+
# Internally, +ActiveJob+ will always use paginated queries to the underlying
|
|
17
|
+
# queue adapter. The page size can be controlled via +config.active_job.default_page_size+
|
|
18
|
+
# (1000 by default).
|
|
19
|
+
#
|
|
20
|
+
# There are additional performance concerns depending on the configured
|
|
21
|
+
# adapter. Please check +ActiveJob::Relation#where+, +ActiveJob::Relation#count+.
|
|
22
|
+
class ActiveJob::JobsRelation
|
|
23
|
+
include Enumerable
|
|
24
|
+
|
|
25
|
+
STATUSES = %i[pending failed in_progress blocked scheduled finished]
|
|
26
|
+
FILTERS = %i[queue_name job_class_name]
|
|
27
|
+
|
|
28
|
+
PROPERTIES = %i[queue_name status offset_value limit_value job_class_name worker_id recurring_task_id finished_at]
|
|
29
|
+
attr_reader(*PROPERTIES, :default_page_size)
|
|
30
|
+
|
|
31
|
+
delegate :last, :[], :reverse, to: :to_a
|
|
32
|
+
delegate :logger, to: FlightControl
|
|
33
|
+
|
|
34
|
+
ALL_JOBS_LIMIT = 100_000_000 # When no limit value it defaults to "all jobs"
|
|
35
|
+
|
|
36
|
+
def initialize(queue_adapter: ActiveJob::Base.queue_adapter, default_page_size: ActiveJob::Base.default_page_size)
|
|
37
|
+
@queue_adapter = queue_adapter
|
|
38
|
+
@default_page_size = default_page_size
|
|
39
|
+
|
|
40
|
+
set_defaults
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Returns a +ActiveJob::JobsRelation+ with the configured filtering options.
|
|
44
|
+
#
|
|
45
|
+
# === Options
|
|
46
|
+
#
|
|
47
|
+
# * <tt>:job_class_name</tt> - To only include the jobs of a given class.
|
|
48
|
+
# Depending on the configured queue adapter, this will perform the
|
|
49
|
+
# filtering in memory, which could introduce performance concerns
|
|
50
|
+
# for large sets of jobs.
|
|
51
|
+
# * <tt>:queue_name</tt> - To only include the jobs in the provided queue.
|
|
52
|
+
# * <tt>:worker_id</tt> - To only include the jobs processed by the provided worker.
|
|
53
|
+
# * <tt>:recurring_task_id</tt> - To only include the jobs corresponding to runs of a recurring task.
|
|
54
|
+
# * <tt>:finished_at</tt> - (Range) To only include the jobs finished between the provided range
|
|
55
|
+
def where(job_class_name: nil, queue_name: nil, worker_id: nil, recurring_task_id: nil, finished_at: nil)
|
|
56
|
+
# Remove nil arguments to avoid overriding parameters when concatenating +where+ clauses
|
|
57
|
+
arguments = {job_class_name: job_class_name,
|
|
58
|
+
queue_name: queue_name&.to_s,
|
|
59
|
+
worker_id: worker_id,
|
|
60
|
+
recurring_task_id: recurring_task_id,
|
|
61
|
+
finished_at: finished_at}.compact
|
|
62
|
+
|
|
63
|
+
clone_with(**arguments)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def with_status(status)
|
|
67
|
+
if status.to_sym.in? STATUSES
|
|
68
|
+
clone_with status: status.to_sym
|
|
69
|
+
else
|
|
70
|
+
self
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
STATUSES.each do |status|
|
|
75
|
+
define_method status do
|
|
76
|
+
with_status(status)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
define_method "#{status}?" do
|
|
80
|
+
self.status == status
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Sets an offset for the jobs-fetching query. The first position is 0.
|
|
85
|
+
def offset(offset)
|
|
86
|
+
clone_with offset_value: offset
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Sets the max number of jobs to fetch in the query.
|
|
90
|
+
def limit(limit)
|
|
91
|
+
clone_with limit_value: limit
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Returns the number of jobs in the relation.
|
|
95
|
+
#
|
|
96
|
+
# When filtering jobs, if the adapter doesn't support the filter(s)
|
|
97
|
+
# directly, this will load all the jobs in memory to filter them.
|
|
98
|
+
def count
|
|
99
|
+
if loaded? || filtering_needed?
|
|
100
|
+
to_a.length
|
|
101
|
+
else
|
|
102
|
+
query_count
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
alias_method :length, :count
|
|
107
|
+
alias_method :size, :count
|
|
108
|
+
|
|
109
|
+
def empty?
|
|
110
|
+
count == 0
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def to_s
|
|
114
|
+
properties_with_values = PROPERTIES.collect do |name|
|
|
115
|
+
value = public_send(name)
|
|
116
|
+
"#{name}: #{value}" unless value.nil?
|
|
117
|
+
end.compact.join(", ")
|
|
118
|
+
"<Jobs with [#{properties_with_values}]> (loaded: #{loaded?})"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
alias_method :inspect, :to_s
|
|
122
|
+
|
|
123
|
+
def each(&block)
|
|
124
|
+
loaded_jobs&.each(&block) || load_jobs(&block)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Retry all the jobs in the queue.
|
|
128
|
+
#
|
|
129
|
+
# This operation is only valid for sets of failed jobs. It will
|
|
130
|
+
# raise an error +ActiveJob::Errors::InvalidOperation+ otherwise.
|
|
131
|
+
def retry_all
|
|
132
|
+
ensure_failed_status
|
|
133
|
+
queue_adapter.retry_all_jobs(self)
|
|
134
|
+
nil
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Retry the provided job.
|
|
138
|
+
#
|
|
139
|
+
# This operation is only valid for sets of failed jobs. It will
|
|
140
|
+
# raise an error +ActiveJob::Errors::InvalidOperation+ otherwise.
|
|
141
|
+
def retry_job(job)
|
|
142
|
+
ensure_failed_status
|
|
143
|
+
queue_adapter.retry_job(job, self)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Discard all the jobs in the relation.
|
|
147
|
+
def discard_all
|
|
148
|
+
queue_adapter.discard_all_jobs(self)
|
|
149
|
+
nil
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Discard the provided job.
|
|
153
|
+
def discard_job(job)
|
|
154
|
+
queue_adapter.discard_job(job, self)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Dispatch the provided job.
|
|
158
|
+
#
|
|
159
|
+
# This operation is only valid for blocked or scheduled jobs. It will
|
|
160
|
+
# raise an error +ActiveJob::Errors::InvalidOperation+ otherwise.
|
|
161
|
+
def dispatch_job(job)
|
|
162
|
+
raise ActiveJob::Errors::InvalidOperation, "This operation can only be performed on blocked or scheduled jobs, but this job is #{job.status}" unless job.blocked? || job.scheduled?
|
|
163
|
+
|
|
164
|
+
queue_adapter.dispatch_job(job, self)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Find a job by id.
|
|
168
|
+
#
|
|
169
|
+
# Returns nil when not found.
|
|
170
|
+
def find_by_id(job_id)
|
|
171
|
+
queue_adapter.find_job(job_id, self)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Find a job by id.
|
|
175
|
+
#
|
|
176
|
+
# Raises +ActiveJob::Errors::JobNotFoundError+ when not found.
|
|
177
|
+
def find_by_id!(job_id)
|
|
178
|
+
queue_adapter.find_job(job_id, self) or raise ActiveJob::Errors::JobNotFoundError.new(job_id, self)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Returns an array of jobs class names in the first +from_first+ jobs.
|
|
182
|
+
def job_class_names(from_first: 500)
|
|
183
|
+
first(from_first).collect(&:job_class_name).uniq
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def reload
|
|
187
|
+
@count = nil
|
|
188
|
+
@loaded_jobs = nil
|
|
189
|
+
@filters = nil
|
|
190
|
+
|
|
191
|
+
self
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def in_batches(of: default_page_size, order: :asc, &block)
|
|
195
|
+
validate_looping_in_batches_is_possible
|
|
196
|
+
|
|
197
|
+
case order
|
|
198
|
+
when :asc
|
|
199
|
+
in_ascending_batches(of: of, &block)
|
|
200
|
+
when :desc
|
|
201
|
+
in_descending_batches(of: of, &block)
|
|
202
|
+
else
|
|
203
|
+
raise "Unsupported order: #{order}. Valid values: :asc, :desc."
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def paginated?
|
|
208
|
+
offset_value > 0 || limit_value_provided?
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def limit_value_provided?
|
|
212
|
+
limit_value.present? && limit_value != ActiveJob::JobsRelation::ALL_JOBS_LIMIT
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def filtering_needed?
|
|
216
|
+
filters.any?
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
private
|
|
220
|
+
|
|
221
|
+
attr_reader :queue_adapter, :loaded_jobs
|
|
222
|
+
attr_writer(*PROPERTIES)
|
|
223
|
+
|
|
224
|
+
def set_defaults
|
|
225
|
+
self.offset_value = 0
|
|
226
|
+
self.limit_value = ALL_JOBS_LIMIT
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def clone_with(**properties)
|
|
230
|
+
dup.reload.tap do |relation|
|
|
231
|
+
properties.each do |key, value|
|
|
232
|
+
relation.send("#{key}=", value)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def query_count
|
|
238
|
+
@count ||= queue_adapter.jobs_count(self)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def load_jobs
|
|
242
|
+
@loaded_jobs = []
|
|
243
|
+
perform_each do |job|
|
|
244
|
+
@loaded_jobs << job
|
|
245
|
+
yield job
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def perform_each
|
|
250
|
+
current_offset = offset_value
|
|
251
|
+
pending_count = limit_value || Float::INFINITY
|
|
252
|
+
|
|
253
|
+
loop do
|
|
254
|
+
limit = [pending_count, default_page_size].min
|
|
255
|
+
page = offset(current_offset).limit(limit)
|
|
256
|
+
jobs = queue_adapter.fetch_jobs(page)
|
|
257
|
+
finished = jobs.empty?
|
|
258
|
+
jobs = filter(jobs) if filtering_needed?
|
|
259
|
+
Array(jobs).each { |job| yield job }
|
|
260
|
+
current_offset += limit
|
|
261
|
+
pending_count -= jobs.length
|
|
262
|
+
break if finished || pending_count.zero?
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def loaded?
|
|
267
|
+
!@loaded_jobs.nil?
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Filtering for not natively supported filters is performed in memory
|
|
271
|
+
def filter(jobs)
|
|
272
|
+
jobs.filter { |job| satisfy_filter?(job) }
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def satisfy_filter?(job)
|
|
276
|
+
filters.all? { |property| public_send(property) == job.public_send(property) }
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def filters
|
|
280
|
+
@filters ||= FILTERS.select { |property| public_send(property).present? && !queue_adapter.supports_job_filter?(self, property) }
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def ensure_failed_status
|
|
284
|
+
raise ActiveJob::Errors::InvalidOperation, "This operation can only be performed on failed jobs, but these jobs are #{status}" unless failed?
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def validate_looping_in_batches_is_possible
|
|
288
|
+
raise ActiveJob::Errors::InvalidOperation, "Looping in batches is not compatible with providing offset or limit" if paginated?
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def in_ascending_batches(of:)
|
|
292
|
+
current_offset = 0
|
|
293
|
+
max = count
|
|
294
|
+
loop do
|
|
295
|
+
page = offset(current_offset).limit(of)
|
|
296
|
+
current_offset += of
|
|
297
|
+
logger.info page
|
|
298
|
+
yield page
|
|
299
|
+
wait_batch_delay
|
|
300
|
+
break if current_offset >= max
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def in_descending_batches(of:)
|
|
305
|
+
current_offset = count - of
|
|
306
|
+
|
|
307
|
+
loop do
|
|
308
|
+
limit = (current_offset < 0) ? of + current_offset : of
|
|
309
|
+
page = offset([current_offset, 0].max).limit(limit)
|
|
310
|
+
current_offset -= of
|
|
311
|
+
logger.info page
|
|
312
|
+
yield page
|
|
313
|
+
wait_batch_delay
|
|
314
|
+
break if current_offset + of <= 0
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def wait_batch_delay
|
|
319
|
+
sleep FlightControl.delay_between_bulk_operation_batches if FlightControl.delay_between_bulk_operation_batches.to_i > 0
|
|
320
|
+
end
|
|
321
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module ActiveJob::Querying
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
# ActiveJob will use pagination internally when fetching relations of jobs. This
|
|
6
|
+
# parameter sets the max amount of jobs to fetch in each data store query.
|
|
7
|
+
class_attribute :default_page_size, default: 1000
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class_methods do
|
|
11
|
+
# Returns the list of queues.
|
|
12
|
+
#
|
|
13
|
+
# See +ActiveJob::Queues+
|
|
14
|
+
def queues
|
|
15
|
+
ActiveJob::Queues.new(fetch_queues)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def jobs
|
|
19
|
+
ActiveJob::JobsRelation.new(queue_adapter: queue_adapter, default_page_size: default_page_size)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def fetch_queues
|
|
25
|
+
queue_adapter.queues.collect do |queue|
|
|
26
|
+
ActiveJob::Queue.new(queue[:name], size: queue[:size], active: queue[:active], queue_adapter: queue_adapter)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def queue
|
|
32
|
+
self.class.queues[queue_name]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Top-level query methods added to `ActiveJob`
|
|
36
|
+
module Root
|
|
37
|
+
def queues
|
|
38
|
+
ActiveJob::Base.queues
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def jobs
|
|
42
|
+
ActiveJob::Base.jobs
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# A queue of jobs
|
|
2
|
+
class ActiveJob::Queue
|
|
3
|
+
attr_reader :name
|
|
4
|
+
|
|
5
|
+
def initialize(name, size: nil, active: nil, queue_adapter: ActiveJob::Base.queue_adapter)
|
|
6
|
+
@name = name
|
|
7
|
+
@queue_adapter = queue_adapter
|
|
8
|
+
|
|
9
|
+
@size = size
|
|
10
|
+
@active = active
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def size
|
|
14
|
+
@size ||= queue_adapter.queue_size(name)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
alias_method :length, :size
|
|
18
|
+
|
|
19
|
+
def clear
|
|
20
|
+
queue_adapter.clear_queue(name)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def empty?
|
|
24
|
+
size == 0
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def pause
|
|
28
|
+
queue_adapter.pause_queue(name)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def resume
|
|
32
|
+
queue_adapter.resume_queue(name)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def paused?
|
|
36
|
+
!active?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def active?
|
|
40
|
+
return @active unless @active.nil?
|
|
41
|
+
@active = !queue_adapter.queue_paused?(name)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Return an +ActiveJob::JobsRelation+ with the pending jobs in the queue.
|
|
45
|
+
def jobs
|
|
46
|
+
ActiveJob::JobsRelation.new(queue_adapter: queue_adapter).pending.where(queue_name: name)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def reload
|
|
50
|
+
@active = @size = nil
|
|
51
|
+
self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def id
|
|
55
|
+
name.parameterize
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
alias_method :to_param, :id
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
attr_reader :queue_adapter
|
|
63
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
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
|
+
|
|
37
|
+
def recurring_task_attributes_from_solid_queue_recurring_task(task)
|
|
38
|
+
{
|
|
39
|
+
id: task.key,
|
|
40
|
+
job_class_name: task.class_name,
|
|
41
|
+
command: task.command,
|
|
42
|
+
arguments: task.arguments,
|
|
43
|
+
schedule: task.schedule,
|
|
44
|
+
next_time: task.next_time,
|
|
45
|
+
queue_name: task.queue_name,
|
|
46
|
+
priority: task.priority
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def recurring_task_last_enqueued_at(task_keys)
|
|
51
|
+
SolidQueue::RecurringExecution.where(task_key: task_keys).group(:task_key).maximum(:run_at)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
|
|
24
|
+
def solid_queue_processes_from_workers_relation(relation)
|
|
25
|
+
SolidQueue::Process.where(kind: "Worker").offset(relation.offset_value).limit(relation.limit_value)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def worker_from_solid_queue_process(process)
|
|
29
|
+
FlightControl::Worker.new(queue_adapter: self, **worker_attributes_from_solid_queue_process(process))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def worker_attributes_from_solid_queue_process(process)
|
|
33
|
+
{
|
|
34
|
+
id: process.id,
|
|
35
|
+
name: "PID: #{process.pid}",
|
|
36
|
+
hostname: process.hostname,
|
|
37
|
+
last_heartbeat_at: process.last_heartbeat_at,
|
|
38
|
+
configuration: process.metadata,
|
|
39
|
+
raw_data: process.as_json
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
end
|