good_job 2.4.2 → 2.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +79 -0
- data/README.md +12 -4
- data/engine/app/assets/scripts.js +1 -0
- data/engine/app/assets/style.css +5 -0
- data/engine/app/assets/vendor/chartjs/chart.min.js +13 -0
- data/engine/app/assets/vendor/rails_ujs.js +747 -0
- data/engine/app/charts/good_job/scheduled_by_queue_chart.rb +69 -0
- data/engine/app/controllers/good_job/assets_controller.rb +8 -4
- data/engine/app/controllers/good_job/cron_entries_controller.rb +19 -0
- data/engine/app/controllers/good_job/jobs_controller.rb +36 -0
- data/engine/app/filters/good_job/base_filter.rb +18 -56
- data/engine/app/filters/good_job/executions_filter.rb +9 -8
- data/engine/app/filters/good_job/jobs_filter.rb +12 -9
- data/engine/app/views/good_job/cron_entries/index.html.erb +51 -0
- data/engine/app/views/good_job/cron_entries/show.html.erb +4 -0
- data/engine/app/views/good_job/{shared/_executions_table.erb → executions/_table.erb} +1 -1
- data/engine/app/views/good_job/executions/index.html.erb +2 -2
- data/engine/app/views/good_job/{shared/_jobs_table.erb → jobs/_table.erb} +18 -6
- data/engine/app/views/good_job/jobs/index.html.erb +15 -2
- data/engine/app/views/good_job/jobs/show.html.erb +2 -2
- data/engine/app/views/good_job/shared/_chart.erb +19 -46
- data/engine/app/views/good_job/shared/_filter.erb +27 -13
- data/engine/app/views/good_job/shared/icons/_arrow_clockwise.html.erb +5 -0
- data/engine/app/views/good_job/shared/icons/_play.html.erb +4 -0
- data/engine/app/views/good_job/shared/icons/_skip_forward.html.erb +4 -0
- data/engine/app/views/good_job/shared/icons/_stop.html.erb +4 -0
- data/engine/app/views/layouts/good_job/base.html.erb +6 -4
- data/engine/config/routes.rb +17 -4
- data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +2 -0
- data/lib/generators/good_job/templates/update/migrations/02_add_cron_at_to_good_jobs.rb.erb +14 -0
- data/lib/generators/good_job/templates/update/migrations/03_add_cron_key_cron_at_index_to_good_jobs.rb.erb +20 -0
- data/lib/good_job/active_job_job.rb +228 -0
- data/lib/good_job/configuration.rb +1 -1
- data/lib/good_job/cron_entry.rb +78 -5
- data/lib/good_job/cron_manager.rb +4 -6
- data/lib/good_job/current_thread.rb +38 -5
- data/lib/good_job/execution.rb +53 -39
- data/lib/good_job/filterable.rb +42 -0
- data/lib/good_job/notifier.rb +17 -7
- data/lib/good_job/version.rb +1 -1
- metadata +31 -21
- data/engine/app/assets/vendor/chartist/chartist.css +0 -613
- data/engine/app/assets/vendor/chartist/chartist.js +0 -4516
- data/engine/app/controllers/good_job/cron_schedules_controller.rb +0 -9
- data/engine/app/models/good_job/active_job_job.rb +0 -127
- data/engine/app/views/good_job/cron_schedules/index.html.erb +0 -72
data/lib/good_job/execution.rb
CHANGED
@@ -6,10 +6,14 @@ module GoodJob
|
|
6
6
|
# class Execution < ActiveRecord::Base; end
|
7
7
|
class Execution < Object.const_get(GoodJob.active_record_parent_class)
|
8
8
|
include Lockable
|
9
|
+
include Filterable
|
9
10
|
|
10
11
|
# Raised if something attempts to execute a previously completed Execution again.
|
11
12
|
PreviouslyPerformedError = Class.new(StandardError)
|
12
13
|
|
14
|
+
# String separating Error Class from Error Message
|
15
|
+
ERROR_MESSAGE_SEPARATOR = ": "
|
16
|
+
|
13
17
|
# ActiveJob jobs without a +queue_name+ attribute are placed on this queue.
|
14
18
|
DEFAULT_QUEUE_NAME = 'default'
|
15
19
|
# ActiveJob jobs without a +priority+ attribute are given this priority.
|
@@ -50,6 +54,16 @@ module GoodJob
|
|
50
54
|
end
|
51
55
|
end
|
52
56
|
|
57
|
+
def self._migration_pending_warning
|
58
|
+
ActiveSupport::Deprecation.warn(<<~DEPRECATION)
|
59
|
+
GoodJob has pending database migrations. To create the migration files, run:
|
60
|
+
rails generate good_job:update
|
61
|
+
To apply the migration files, run:
|
62
|
+
rails db:migrate
|
63
|
+
DEPRECATION
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
53
67
|
# Get Jobs with given ActiveJob ID
|
54
68
|
# @!method active_job_id
|
55
69
|
# @!scope class
|
@@ -143,24 +157,6 @@ module GoodJob
|
|
143
157
|
end
|
144
158
|
end)
|
145
159
|
|
146
|
-
# Get Jobs in display order with optional keyset pagination.
|
147
|
-
# @!method display_all(after_scheduled_at: nil, after_id: nil)
|
148
|
-
# @!scope class
|
149
|
-
# @param after_scheduled_at [DateTime, String, nil]
|
150
|
-
# Display records scheduled after this time for keyset pagination
|
151
|
-
# @param after_id [Numeric, String, nil]
|
152
|
-
# Display records after this ID for keyset pagination
|
153
|
-
# @return [ActiveRecord::Relation]
|
154
|
-
scope :display_all, (lambda do |after_scheduled_at: nil, after_id: nil|
|
155
|
-
query = order(Arel.sql('COALESCE(scheduled_at, created_at) DESC, id DESC'))
|
156
|
-
if after_scheduled_at.present? && after_id.present?
|
157
|
-
query = query.where(Arel.sql('(COALESCE(scheduled_at, created_at), id) < (:after_scheduled_at, :after_id)'), after_scheduled_at: after_scheduled_at, after_id: after_id)
|
158
|
-
elsif after_scheduled_at.present?
|
159
|
-
query = query.where(Arel.sql('(COALESCE(scheduled_at, created_at)) < (:after_scheduled_at)'), after_scheduled_at: after_scheduled_at)
|
160
|
-
end
|
161
|
-
query
|
162
|
-
end)
|
163
|
-
|
164
160
|
# Finds the next eligible Execution, acquire an advisory lock related to it, and
|
165
161
|
# executes the job.
|
166
162
|
# @return [ExecutionResult, nil]
|
@@ -222,7 +218,15 @@ module GoodJob
|
|
222
218
|
|
223
219
|
if CurrentThread.cron_key
|
224
220
|
execution_args[:cron_key] = CurrentThread.cron_key
|
225
|
-
|
221
|
+
|
222
|
+
@cron_at_index = column_names.include?('cron_at') && connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at) unless instance_variable_defined?(:@cron_at_index)
|
223
|
+
|
224
|
+
if @cron_at_index
|
225
|
+
execution_args[:cron_at] = CurrentThread.cron_at
|
226
|
+
else
|
227
|
+
_migration_pending_warning
|
228
|
+
end
|
229
|
+
elsif CurrentThread.active_job_id && CurrentThread.active_job_id == active_job.job_id
|
226
230
|
execution_args[:cron_key] = CurrentThread.execution.cron_key
|
227
231
|
end
|
228
232
|
|
@@ -233,7 +237,7 @@ module GoodJob
|
|
233
237
|
execution.save!
|
234
238
|
active_job.provider_job_id = execution.id
|
235
239
|
|
236
|
-
CurrentThread.execution.retried_good_job_id = execution.id if CurrentThread.
|
240
|
+
CurrentThread.execution.retried_good_job_id = execution.id if CurrentThread.active_job_id && CurrentThread.active_job_id == active_job.job_id
|
237
241
|
|
238
242
|
execution
|
239
243
|
end
|
@@ -253,7 +257,7 @@ module GoodJob
|
|
253
257
|
result = execute
|
254
258
|
|
255
259
|
job_error = result.handled_error || result.unhandled_error
|
256
|
-
self.error =
|
260
|
+
self.error = [job_error.class, ERROR_MESSAGE_SEPARATOR, job_error.message].join if job_error
|
257
261
|
|
258
262
|
if result.unhandled_error && GoodJob.retry_on_unhandled_error
|
259
263
|
save!
|
@@ -273,29 +277,39 @@ module GoodJob
|
|
273
277
|
self.class.unscoped.unfinished.owns_advisory_locked.exists?(id: id)
|
274
278
|
end
|
275
279
|
|
280
|
+
def active_job
|
281
|
+
ActiveJob::Base.deserialize(active_job_data)
|
282
|
+
end
|
283
|
+
|
276
284
|
private
|
277
285
|
|
286
|
+
def active_job_data
|
287
|
+
serialized_params.deep_dup
|
288
|
+
.tap do |job_data|
|
289
|
+
job_data["provider_job_id"] = id
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
278
293
|
# @return [ExecutionResult]
|
279
294
|
def execute
|
280
|
-
GoodJob::CurrentThread.
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
295
|
+
GoodJob::CurrentThread.within do |current_thread|
|
296
|
+
current_thread.reset
|
297
|
+
current_thread.execution = self
|
298
|
+
|
299
|
+
# DEPRECATION: Remove deprecated `good_job:` parameter in GoodJob v3
|
300
|
+
ActiveSupport::Notifications.instrument("perform_job.good_job", { good_job: self, execution: self, process_id: current_thread.process_id, thread_name: current_thread.thread_name }) do
|
301
|
+
value = ActiveJob::Base.execute(active_job_data)
|
302
|
+
|
303
|
+
if value.is_a?(Exception)
|
304
|
+
handled_error = value
|
305
|
+
value = nil
|
306
|
+
end
|
307
|
+
handled_error ||= current_thread.error_on_retry || current_thread.error_on_discard
|
308
|
+
|
309
|
+
ExecutionResult.new(value: value, handled_error: handled_error)
|
310
|
+
rescue StandardError => e
|
311
|
+
ExecutionResult.new(value: nil, unhandled_error: e)
|
293
312
|
end
|
294
|
-
handled_error ||= GoodJob::CurrentThread.error_on_retry || GoodJob::CurrentThread.error_on_discard
|
295
|
-
|
296
|
-
ExecutionResult.new(value: value, handled_error: handled_error)
|
297
|
-
rescue StandardError => e
|
298
|
-
ExecutionResult.new(value: nil, unhandled_error: e)
|
299
313
|
end
|
300
314
|
end
|
301
315
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GoodJob
|
3
|
+
# Shared methods for filtering Execution/Job records from the +good_jobs+ table.
|
4
|
+
module Filterable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
# Get records in display order with optional keyset pagination.
|
9
|
+
# @!method display_all(after_scheduled_at: nil, after_id: nil)
|
10
|
+
# @!scope class
|
11
|
+
# @param after_scheduled_at [DateTime, String, nil]
|
12
|
+
# Display records scheduled after this time for keyset pagination
|
13
|
+
# @param after_id [Numeric, String, nil]
|
14
|
+
# Display records after this ID for keyset pagination
|
15
|
+
# @return [ActiveRecord::Relation]
|
16
|
+
scope :display_all, (lambda do |after_scheduled_at: nil, after_id: nil|
|
17
|
+
query = order(Arel.sql('COALESCE(scheduled_at, created_at) DESC, id DESC'))
|
18
|
+
if after_scheduled_at.present? && after_id.present?
|
19
|
+
query = query.where(Arel.sql('(COALESCE(scheduled_at, created_at), id) < (:after_scheduled_at, :after_id)'), after_scheduled_at: after_scheduled_at, after_id: after_id)
|
20
|
+
elsif after_scheduled_at.present?
|
21
|
+
query = query.where(Arel.sql('(COALESCE(scheduled_at, created_at)) < (:after_scheduled_at)'), after_scheduled_at: after_scheduled_at)
|
22
|
+
end
|
23
|
+
query
|
24
|
+
end)
|
25
|
+
|
26
|
+
# Search records by text query.
|
27
|
+
# @!method search_text(query)
|
28
|
+
# @!scope class
|
29
|
+
# @param query [String]
|
30
|
+
# Search Query
|
31
|
+
# @return [ActiveRecord::Relation]
|
32
|
+
scope :search_text, (lambda do |query|
|
33
|
+
query = query.to_s.strip
|
34
|
+
next if query.blank?
|
35
|
+
|
36
|
+
tsvector = "(to_tsvector('english', serialized_params) || to_tsvector('english', id::text) || to_tsvector('english', COALESCE(error, '')::text))"
|
37
|
+
where("#{tsvector} @@ to_tsquery(?)", query)
|
38
|
+
.order(sanitize_sql_for_order([Arel.sql("ts_rank(#{tsvector}, to_tsquery(?))"), query]) => 'DESC')
|
39
|
+
end)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/good_job/notifier.rb
CHANGED
@@ -25,10 +25,17 @@ module GoodJob # :nodoc:
|
|
25
25
|
max_queue: 1,
|
26
26
|
fallback_policy: :discard,
|
27
27
|
}.freeze
|
28
|
-
# Seconds to wait if database cannot be connected to
|
29
|
-
RECONNECT_INTERVAL = 5
|
30
28
|
# Seconds to block while LISTENing for a message
|
31
29
|
WAIT_INTERVAL = 1
|
30
|
+
# Seconds to wait if database cannot be connected to
|
31
|
+
RECONNECT_INTERVAL = 5
|
32
|
+
# Connection errors that will wait {RECONNECT_INTERVAL} before reconnecting
|
33
|
+
CONNECTION_ERRORS = %w[
|
34
|
+
ActiveRecord::ConnectionNotEstablished
|
35
|
+
ActiveRecord::StatementInvalid
|
36
|
+
PG::UnableToSend
|
37
|
+
PG::Error
|
38
|
+
].freeze
|
32
39
|
|
33
40
|
# @!attribute [r] instances
|
34
41
|
# @!scope class
|
@@ -115,15 +122,18 @@ module GoodJob # :nodoc:
|
|
115
122
|
if thread_error
|
116
123
|
GoodJob.on_thread_error.call(thread_error) if GoodJob.on_thread_error.respond_to?(:call)
|
117
124
|
ActiveSupport::Notifications.instrument("notifier_notify_error.good_job", { error: thread_error })
|
125
|
+
|
126
|
+
connection_error = CONNECTION_ERRORS.any? do |error_string|
|
127
|
+
error_class = error_string.safe_constantize
|
128
|
+
next unless error_class
|
129
|
+
|
130
|
+
thread_error.is_a? error_class
|
131
|
+
end
|
118
132
|
end
|
119
133
|
|
120
134
|
return if shutdown?
|
121
135
|
|
122
|
-
|
123
|
-
listen(delay: RECONNECT_INTERVAL)
|
124
|
-
else
|
125
|
-
listen
|
126
|
-
end
|
136
|
+
listen(delay: connection_error ? RECONNECT_INTERVAL : 0)
|
127
137
|
end
|
128
138
|
|
129
139
|
private
|
data/lib/good_job/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: good_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Sheldon
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -126,16 +126,16 @@ dependencies:
|
|
126
126
|
name: capybara
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- - "
|
129
|
+
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
131
|
+
version: 3.35.0
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- - "
|
136
|
+
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
138
|
+
version: 3.35.0
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: database_cleaner
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -280,16 +280,16 @@ dependencies:
|
|
280
280
|
name: selenium-webdriver
|
281
281
|
requirement: !ruby/object:Gem::Requirement
|
282
282
|
requirements:
|
283
|
-
- - "
|
283
|
+
- - "~>"
|
284
284
|
- !ruby/object:Gem::Version
|
285
|
-
version: '
|
285
|
+
version: '3.142'
|
286
286
|
type: :development
|
287
287
|
prerelease: false
|
288
288
|
version_requirements: !ruby/object:Gem::Requirement
|
289
289
|
requirements:
|
290
|
-
- - "
|
290
|
+
- - "~>"
|
291
291
|
- !ruby/object:Gem::Version
|
292
|
-
version: '
|
292
|
+
version: '3.142'
|
293
293
|
- !ruby/object:Gem::Dependency
|
294
294
|
name: sigdump
|
295
295
|
requirement: !ruby/object:Gem::Requirement
|
@@ -346,31 +346,37 @@ files:
|
|
346
346
|
- CHANGELOG.md
|
347
347
|
- LICENSE.txt
|
348
348
|
- README.md
|
349
|
+
- engine/app/assets/scripts.js
|
349
350
|
- engine/app/assets/style.css
|
350
351
|
- engine/app/assets/vendor/bootstrap/bootstrap.bundle.min.js
|
351
352
|
- engine/app/assets/vendor/bootstrap/bootstrap.min.css
|
352
|
-
- engine/app/assets/vendor/
|
353
|
-
- engine/app/assets/vendor/
|
353
|
+
- engine/app/assets/vendor/chartjs/chart.min.js
|
354
|
+
- engine/app/assets/vendor/rails_ujs.js
|
355
|
+
- engine/app/charts/good_job/scheduled_by_queue_chart.rb
|
354
356
|
- engine/app/controllers/good_job/assets_controller.rb
|
355
357
|
- engine/app/controllers/good_job/base_controller.rb
|
356
|
-
- engine/app/controllers/good_job/
|
358
|
+
- engine/app/controllers/good_job/cron_entries_controller.rb
|
357
359
|
- engine/app/controllers/good_job/executions_controller.rb
|
358
360
|
- engine/app/controllers/good_job/jobs_controller.rb
|
359
361
|
- engine/app/filters/good_job/base_filter.rb
|
360
362
|
- engine/app/filters/good_job/executions_filter.rb
|
361
363
|
- engine/app/filters/good_job/jobs_filter.rb
|
362
364
|
- engine/app/helpers/good_job/application_helper.rb
|
363
|
-
- engine/app/
|
364
|
-
- engine/app/views/good_job/
|
365
|
+
- engine/app/views/good_job/cron_entries/index.html.erb
|
366
|
+
- engine/app/views/good_job/cron_entries/show.html.erb
|
367
|
+
- engine/app/views/good_job/executions/_table.erb
|
365
368
|
- engine/app/views/good_job/executions/index.html.erb
|
369
|
+
- engine/app/views/good_job/jobs/_table.erb
|
366
370
|
- engine/app/views/good_job/jobs/index.html.erb
|
367
371
|
- engine/app/views/good_job/jobs/show.html.erb
|
368
372
|
- engine/app/views/good_job/shared/_chart.erb
|
369
|
-
- engine/app/views/good_job/shared/_executions_table.erb
|
370
373
|
- engine/app/views/good_job/shared/_filter.erb
|
371
|
-
- engine/app/views/good_job/shared/
|
374
|
+
- engine/app/views/good_job/shared/icons/_arrow_clockwise.html.erb
|
372
375
|
- engine/app/views/good_job/shared/icons/_check.html.erb
|
373
376
|
- engine/app/views/good_job/shared/icons/_exclamation.html.erb
|
377
|
+
- engine/app/views/good_job/shared/icons/_play.html.erb
|
378
|
+
- engine/app/views/good_job/shared/icons/_skip_forward.html.erb
|
379
|
+
- engine/app/views/good_job/shared/icons/_stop.html.erb
|
374
380
|
- engine/app/views/good_job/shared/icons/_trash.html.erb
|
375
381
|
- engine/app/views/layouts/good_job/base.html.erb
|
376
382
|
- engine/config/routes.rb
|
@@ -380,10 +386,13 @@ files:
|
|
380
386
|
- lib/generators/good_job/install_generator.rb
|
381
387
|
- lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb
|
382
388
|
- lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb
|
389
|
+
- lib/generators/good_job/templates/update/migrations/02_add_cron_at_to_good_jobs.rb.erb
|
390
|
+
- lib/generators/good_job/templates/update/migrations/03_add_cron_key_cron_at_index_to_good_jobs.rb.erb
|
383
391
|
- lib/generators/good_job/update_generator.rb
|
384
392
|
- lib/good_job.rb
|
385
393
|
- lib/good_job/active_job_extensions.rb
|
386
394
|
- lib/good_job/active_job_extensions/concurrency.rb
|
395
|
+
- lib/good_job/active_job_job.rb
|
387
396
|
- lib/good_job/adapter.rb
|
388
397
|
- lib/good_job/cli.rb
|
389
398
|
- lib/good_job/configuration.rb
|
@@ -393,6 +402,7 @@ files:
|
|
393
402
|
- lib/good_job/daemon.rb
|
394
403
|
- lib/good_job/execution.rb
|
395
404
|
- lib/good_job/execution_result.rb
|
405
|
+
- lib/good_job/filterable.rb
|
396
406
|
- lib/good_job/job.rb
|
397
407
|
- lib/good_job/job_performer.rb
|
398
408
|
- lib/good_job/lockable.rb
|
@@ -412,7 +422,7 @@ metadata:
|
|
412
422
|
documentation_uri: https://rdoc.info/github/bensheldon/good_job
|
413
423
|
homepage_uri: https://github.com/bensheldon/good_job
|
414
424
|
source_code_uri: https://github.com/bensheldon/good_job
|
415
|
-
post_install_message:
|
425
|
+
post_install_message:
|
416
426
|
rdoc_options:
|
417
427
|
- "--title"
|
418
428
|
- GoodJob - a multithreaded, Postgres-based ActiveJob backend for Ruby on Rails
|
@@ -435,8 +445,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
435
445
|
- !ruby/object:Gem::Version
|
436
446
|
version: '0'
|
437
447
|
requirements: []
|
438
|
-
rubygems_version: 3.
|
439
|
-
signing_key:
|
448
|
+
rubygems_version: 3.2.30
|
449
|
+
signing_key:
|
440
450
|
specification_version: 4
|
441
451
|
summary: A multithreaded, Postgres-based ActiveJob backend for Ruby on Rails
|
442
452
|
test_files: []
|