good_job 3.30.1 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +22 -0
- data/app/charts/good_job/scheduled_by_queue_chart.rb +2 -2
- data/app/controllers/good_job/cron_entries_controller.rb +0 -8
- data/app/controllers/good_job/metrics_controller.rb +1 -1
- data/app/controllers/good_job/performances_controller.rb +5 -9
- data/app/models/concerns/good_job/filterable.rb +1 -1
- data/app/models/good_job/base_execution.rb +104 -202
- data/app/models/good_job/cron_entry.rb +0 -2
- data/app/models/good_job/discrete_execution.rb +1 -31
- data/app/models/good_job/execution.rb +3 -7
- data/app/models/good_job/job.rb +37 -116
- data/app/models/good_job/process.rb +7 -20
- data/app/views/good_job/batches/index.html.erb +11 -15
- data/app/views/good_job/jobs/_executions.erb +1 -1
- data/app/views/good_job/jobs/_table.erb +1 -1
- data/app/views/good_job/jobs/show.html.erb +1 -8
- data/app/views/good_job/performances/show.html.erb +33 -40
- data/config/locales/de.yml +1 -4
- data/config/locales/en.yml +1 -4
- data/config/locales/es.yml +1 -4
- data/config/locales/fr.yml +1 -4
- data/config/locales/it.yml +1 -4
- data/config/locales/ja.yml +1 -4
- data/config/locales/ko.yml +1 -4
- data/config/locales/nl.yml +1 -4
- data/config/locales/pt-BR.yml +1 -4
- data/config/locales/ru.yml +1 -4
- data/config/locales/tr.yml +1 -4
- data/config/locales/uk.yml +1 -4
- data/lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb +65 -3
- data/lib/good_job/active_job_extensions/batches.rb +1 -1
- data/lib/good_job/active_job_extensions/concurrency.rb +10 -10
- data/lib/good_job/adapter.rb +13 -24
- data/lib/good_job/current_thread.rb +6 -6
- data/lib/good_job/job_performer.rb +2 -2
- data/lib/good_job/log_subscriber.rb +2 -10
- data/lib/good_job/notifier.rb +3 -3
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +22 -21
- metadata +16 -30
- data/lib/generators/good_job/templates/update/migrations/02_create_good_job_settings.rb.erb +0 -20
- data/lib/generators/good_job/templates/update/migrations/03_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb.erb +0 -19
- data/lib/generators/good_job/templates/update/migrations/04_create_good_job_batches.rb.erb +0 -35
- data/lib/generators/good_job/templates/update/migrations/05_create_good_job_executions.rb.erb +0 -33
- data/lib/generators/good_job/templates/update/migrations/06_create_good_jobs_error_event.rb.erb +0 -16
- data/lib/generators/good_job/templates/update/migrations/07_recreate_good_job_cron_indexes_with_conditional.rb.erb +0 -45
- data/lib/generators/good_job/templates/update/migrations/08_create_good_job_labels.rb.erb +0 -15
- data/lib/generators/good_job/templates/update/migrations/09_create_good_job_labels_index.rb.erb +0 -22
- data/lib/generators/good_job/templates/update/migrations/10_remove_good_job_active_id_index.rb.erb +0 -21
- data/lib/generators/good_job/templates/update/migrations/11_create_index_good_job_jobs_for_candidate_lookup.rb.erb +0 -19
- data/lib/generators/good_job/templates/update/migrations/12_create_good_job_execution_error_backtrace.rb.erb +0 -15
- data/lib/generators/good_job/templates/update/migrations/13_create_good_job_process_lock_ids.rb.erb +0 -18
- data/lib/generators/good_job/templates/update/migrations/14_create_good_job_process_lock_indexes.rb.erb +0 -38
- data/lib/generators/good_job/templates/update/migrations/15_create_good_job_execution_duration.rb.erb +0 -15
@@ -14,31 +14,6 @@ module GoodJob # :nodoc:
|
|
14
14
|
|
15
15
|
alias_attribute :performed_at, :created_at
|
16
16
|
|
17
|
-
def self.error_event_migrated?
|
18
|
-
return true if columns_hash["error_event"].present?
|
19
|
-
|
20
|
-
migration_pending_warning!
|
21
|
-
false
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.backtrace_migrated?
|
25
|
-
return true if columns_hash["error_backtrace"].present?
|
26
|
-
|
27
|
-
migration_pending_warning!
|
28
|
-
false
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.duration_interval_migrated?
|
32
|
-
return true if columns_hash["duration"].present?
|
33
|
-
|
34
|
-
migration_pending_warning!
|
35
|
-
false
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.duration_interval_usable?
|
39
|
-
duration_interval_migrated? && Gem::Version.new(Rails.version) >= Gem::Version.new('6.1.0.a')
|
40
|
-
end
|
41
|
-
|
42
17
|
def number
|
43
18
|
serialized_params.fetch('executions', 0) + 1
|
44
19
|
end
|
@@ -50,12 +25,7 @@ module GoodJob # :nodoc:
|
|
50
25
|
|
51
26
|
# Monotonic time between when this job started and finished
|
52
27
|
def runtime_latency
|
53
|
-
|
54
|
-
if self.class.duration_interval_usable?
|
55
|
-
duration
|
56
|
-
elsif performed_at
|
57
|
-
(finished_at || Time.current) - performed_at
|
58
|
-
end
|
28
|
+
duration
|
59
29
|
end
|
60
30
|
|
61
31
|
def last_status_at
|
@@ -1,12 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module GoodJob
|
4
|
-
#
|
5
|
-
#
|
6
|
-
|
7
|
-
class Execution < BaseExecution
|
8
|
-
self.table_name = 'good_jobs'
|
9
|
-
|
10
|
-
belongs_to :job, class_name: 'GoodJob::Job', foreign_key: 'active_job_id', primary_key: 'active_job_id', optional: true, inverse_of: :executions
|
4
|
+
# Created at the time a Job begins executing.
|
5
|
+
# Behavior from +DiscreteExecution+ will be merged into this class.
|
6
|
+
class Execution < DiscreteExecution
|
11
7
|
end
|
12
8
|
end
|
data/app/models/good_job/job.rb
CHANGED
@@ -2,10 +2,6 @@
|
|
2
2
|
|
3
3
|
module GoodJob
|
4
4
|
# Active Record model that represents an +ActiveJob+ job.
|
5
|
-
# There is not a table in the database whose discrete rows represents "Jobs".
|
6
|
-
# The +good_jobs+ table is a table of individual {GoodJob::Execution}s that share the same +active_job_id+.
|
7
|
-
# A single row from the +good_jobs+ table of executions is fetched to represent a Job.
|
8
|
-
#
|
9
5
|
class Job < BaseExecution
|
10
6
|
# Raised when an inappropriate action is applied to a Job based on its state.
|
11
7
|
ActionForStateMismatchError = Class.new(StandardError)
|
@@ -16,29 +12,17 @@ module GoodJob
|
|
16
12
|
# Raised when Active Job data cannot be deserialized
|
17
13
|
ActiveJobDeserializationError = Class.new(StandardError)
|
18
14
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def table_name=(_value)
|
23
|
-
raise NotImplementedError, 'Assign GoodJob::Execution.table_name directly'
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
self.primary_key = 'active_job_id'
|
28
|
-
self.advisory_lockable_column = 'active_job_id'
|
15
|
+
self.table_name = 'good_jobs'
|
16
|
+
self.advisory_lockable_column = 'id'
|
29
17
|
self.implicit_order_column = 'created_at'
|
30
18
|
|
31
19
|
belongs_to :batch, class_name: 'GoodJob::BatchRecord', inverse_of: :jobs, optional: true
|
32
20
|
belongs_to :locked_by_process, class_name: "GoodJob::Process", foreign_key: :locked_by_id, inverse_of: :locked_jobs, optional: true
|
33
|
-
has_many :executions, -> { order(created_at: :asc) }, class_name: 'GoodJob::Execution', foreign_key: 'active_job_id', inverse_of: :job
|
34
|
-
|
21
|
+
has_many :executions, -> { order(created_at: :asc) }, class_name: 'GoodJob::Execution', foreign_key: 'active_job_id', primary_key: :active_job_id, inverse_of: :job, dependent: :delete_all
|
22
|
+
# TODO: rename callers of discrete_execution to executions, but after v4 has some time to bake for cleaner diffs/patches
|
23
|
+
has_many :discrete_executions, -> { order(created_at: :asc) }, class_name: 'GoodJob::Execution', foreign_key: 'active_job_id', primary_key: :active_job_id, inverse_of: :job, dependent: :delete_all
|
35
24
|
|
36
|
-
|
37
|
-
GoodJob::DiscreteExecution.where(active_job_id: active_job_id).delete_all if discrete? # TODO: move into association `dependent: :delete_all` after v4
|
38
|
-
}
|
39
|
-
|
40
|
-
# Only the most-recent unretried execution represents a "Job"
|
41
|
-
default_scope { where(retried_good_job_id: nil) }
|
25
|
+
before_create -> { self.id = active_job_id }, if: -> { active_job_id.present? }
|
42
26
|
|
43
27
|
# Get Jobs finished before the given timestamp.
|
44
28
|
# @!method finished_before(timestamp)
|
@@ -64,65 +48,11 @@ module GoodJob
|
|
64
48
|
|
65
49
|
scope :unfinished_undiscrete, -> { where(finished_at: nil, retried_good_job_id: nil, is_discrete: [nil, false]) }
|
66
50
|
|
67
|
-
#
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
# Override #reload to add a custom scope to ensure the reloaded record is the head execution
|
74
|
-
# @return [Job]
|
75
|
-
def reload(options = nil)
|
76
|
-
self.class.connection.clear_query_cache
|
77
|
-
|
78
|
-
# override with the `where(retried_good_job_id: nil)` scope
|
79
|
-
override_query = self.class.where(retried_good_job_id: nil)
|
80
|
-
fresh_object =
|
81
|
-
if options && options[:lock]
|
82
|
-
self.class.unscoped { override_query.lock(options[:lock]).find(id) }
|
83
|
-
else
|
84
|
-
self.class.unscoped { override_query.find(id) }
|
85
|
-
end
|
86
|
-
|
87
|
-
@attributes = fresh_object.instance_variable_get(:@attributes)
|
88
|
-
@new_record = false
|
89
|
-
@previously_new_record = false
|
90
|
-
self
|
91
|
-
end
|
92
|
-
|
93
|
-
# This job's most recent {Execution}
|
94
|
-
# @param reload [Booelan] whether to reload executions
|
95
|
-
# @return [Execution]
|
96
|
-
def head_execution(reload: false)
|
97
|
-
executions.reload if reload
|
98
|
-
executions.load # memoize the results
|
99
|
-
executions.last
|
100
|
-
end
|
101
|
-
|
102
|
-
# This job's initial/oldest {Execution}
|
103
|
-
# @return [Execution]
|
104
|
-
def tail_execution
|
105
|
-
executions.first
|
106
|
-
end
|
107
|
-
|
108
|
-
# The number of times this job has been executed, according to Active Job's serialized state.
|
109
|
-
# @return [Numeric]
|
110
|
-
def executions_count
|
111
|
-
aj_count = serialized_params.fetch('executions', 0)
|
112
|
-
# The execution count within serialized_params is not updated
|
113
|
-
# once the underlying execution has been executed.
|
114
|
-
if status.in? [:discarded, :succeeded, :running]
|
115
|
-
aj_count + 1
|
116
|
-
else
|
117
|
-
aj_count
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
# The number of times this job has been executed, according to the number of GoodJob {Execution} records.
|
122
|
-
# @return [Numeric]
|
123
|
-
def preserved_executions_count
|
124
|
-
executions.size
|
125
|
-
end
|
51
|
+
# TODO: it would be nice to enforce these values at the model
|
52
|
+
# validates :active_job_id, presence: true
|
53
|
+
# validates :scheduled_at, presence: true
|
54
|
+
# validates :job_class, presence: true
|
55
|
+
# validates :error_event, presence: true, if: -> { error.present? }
|
126
56
|
|
127
57
|
# The most recent error message.
|
128
58
|
# If the job has been retried, the error will be fetched from the previous {Execution} record.
|
@@ -155,6 +85,10 @@ module GoodJob
|
|
155
85
|
job_class
|
156
86
|
end
|
157
87
|
|
88
|
+
def executions_count
|
89
|
+
super || 0
|
90
|
+
end
|
91
|
+
|
158
92
|
# Tests whether the job is being executed right now.
|
159
93
|
# @return [Boolean]
|
160
94
|
def running?
|
@@ -189,33 +123,33 @@ module GoodJob
|
|
189
123
|
# @return [ActiveJob::Base]
|
190
124
|
def retry_job
|
191
125
|
with_advisory_lock do
|
192
|
-
|
193
|
-
active_job =
|
126
|
+
reload
|
127
|
+
active_job = self.active_job(ignore_deserialization_errors: true)
|
194
128
|
|
195
129
|
raise ActiveJobDeserializationError if active_job.nil?
|
196
130
|
raise AdapterNotGoodJobError unless active_job.class.queue_adapter.is_a? GoodJob::Adapter
|
197
|
-
raise ActionForStateMismatchError if
|
131
|
+
raise ActionForStateMismatchError if finished_at.blank? || error.blank?
|
198
132
|
|
199
133
|
# Update the executions count because the previous execution will not have been preserved
|
200
134
|
# Do not update `exception_executions` because that comes from rescue_from's arguments
|
201
135
|
active_job.executions = (active_job.executions || 0) + 1
|
202
136
|
|
203
137
|
begin
|
204
|
-
error_class, error_message =
|
138
|
+
error_class, error_message = error.split(ERROR_MESSAGE_SEPARATOR).map(&:strip)
|
205
139
|
error = error_class.constantize.new(error_message)
|
206
140
|
rescue StandardError
|
207
|
-
error = StandardError.new(
|
141
|
+
error = StandardError.new(error)
|
208
142
|
end
|
209
143
|
|
210
144
|
new_active_job = nil
|
211
145
|
GoodJob::CurrentThread.within do |current_thread|
|
212
|
-
current_thread.
|
146
|
+
current_thread.job = self
|
213
147
|
current_thread.retry_now = true
|
214
148
|
|
215
|
-
|
149
|
+
self.class.transaction(joinable: false, requires_new: true) do
|
216
150
|
new_active_job = active_job.retry_job(wait: 0, error: error)
|
217
|
-
|
218
|
-
|
151
|
+
self.error_event = ERROR_EVENT_RETRIED if error
|
152
|
+
save!
|
219
153
|
end
|
220
154
|
end
|
221
155
|
|
@@ -244,11 +178,10 @@ module GoodJob
|
|
244
178
|
# @return [void]
|
245
179
|
def reschedule_job(scheduled_at = Time.current)
|
246
180
|
with_advisory_lock do
|
247
|
-
|
248
|
-
|
249
|
-
raise ActionForStateMismatchError if execution.finished_at.present?
|
181
|
+
reload
|
182
|
+
raise ActionForStateMismatchError if finished_at.present?
|
250
183
|
|
251
|
-
|
184
|
+
update(scheduled_at: scheduled_at)
|
252
185
|
end
|
253
186
|
end
|
254
187
|
|
@@ -256,9 +189,7 @@ module GoodJob
|
|
256
189
|
# @return [void]
|
257
190
|
def destroy_job
|
258
191
|
with_advisory_lock do
|
259
|
-
|
260
|
-
|
261
|
-
raise ActionForStateMismatchError if execution.finished_at.blank?
|
192
|
+
raise ActionForStateMismatchError if finished_at.blank?
|
262
193
|
|
263
194
|
destroy
|
264
195
|
end
|
@@ -270,37 +201,27 @@ module GoodJob
|
|
270
201
|
attributes['id']
|
271
202
|
end
|
272
203
|
|
273
|
-
# Utility method to test whether this job's underlying attributes represents its most recent execution.
|
274
|
-
# @return [Boolean]
|
275
|
-
def _head?
|
276
|
-
_execution_id == head_execution(reload: true).id
|
277
|
-
end
|
278
|
-
|
279
204
|
private
|
280
205
|
|
281
206
|
def _discard_job(message)
|
282
|
-
|
283
|
-
active_job = execution.active_job(ignore_deserialization_errors: true)
|
207
|
+
active_job = self.active_job(ignore_deserialization_errors: true)
|
284
208
|
|
285
|
-
raise ActionForStateMismatchError if
|
209
|
+
raise ActionForStateMismatchError if finished_at.present?
|
286
210
|
|
287
211
|
job_error = GoodJob::Job::DiscardJobError.new(message)
|
288
212
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
}.tap do |attrs|
|
295
|
-
attrs[:error_event] = ERROR_EVENT_DISCARDED if self.class.error_event_migrated?
|
296
|
-
end
|
213
|
+
update_record = proc do
|
214
|
+
update(
|
215
|
+
finished_at: Time.current,
|
216
|
+
error: self.class.format_error(job_error),
|
217
|
+
error_event: ERROR_EVENT_DISCARDED
|
297
218
|
)
|
298
219
|
end
|
299
220
|
|
300
221
|
if active_job.respond_to?(:instrument)
|
301
|
-
active_job.send :instrument, :discard, error: job_error, &
|
222
|
+
active_job.send :instrument, :discard, error: job_error, &update_record
|
302
223
|
else
|
303
|
-
|
224
|
+
update_record.call
|
304
225
|
end
|
305
226
|
end
|
306
227
|
end
|
@@ -33,13 +33,9 @@ module GoodJob # :nodoc:
|
|
33
33
|
# @!scope class
|
34
34
|
# @return [ActiveRecord::Relation]
|
35
35
|
scope :active, (lambda do
|
36
|
-
|
37
|
-
|
38
|
-
query.where(lock_type:
|
39
|
-
.or(query.where(lock_type: nil).where(arel_table[:updated_at].gt(EXPIRED_INTERVAL.ago)))
|
40
|
-
else
|
41
|
-
advisory_locked
|
42
|
-
end
|
36
|
+
query = joins_advisory_locks
|
37
|
+
query.where(lock_type: LOCK_TYPE_ENUMS[LOCK_TYPE_ADVISORY]).advisory_locked
|
38
|
+
.or(query.where(lock_type: nil).where(arel_table[:updated_at].gt(EXPIRED_INTERVAL.ago)))
|
43
39
|
end)
|
44
40
|
|
45
41
|
# Processes that are inactive and unlocked (e.g. SIGKILLed)
|
@@ -47,13 +43,9 @@ module GoodJob # :nodoc:
|
|
47
43
|
# @!scope class
|
48
44
|
# @return [ActiveRecord::Relation]
|
49
45
|
scope :inactive, (lambda do
|
50
|
-
|
51
|
-
|
52
|
-
query.where(lock_type:
|
53
|
-
.or(query.where(lock_type: nil).where(arel_table[:updated_at].lt(EXPIRED_INTERVAL.ago)))
|
54
|
-
else
|
55
|
-
advisory_unlocked
|
56
|
-
end
|
46
|
+
query = joins_advisory_locks
|
47
|
+
query.where(lock_type: LOCK_TYPE_ENUMS[LOCK_TYPE_ADVISORY]).advisory_unlocked
|
48
|
+
.or(query.where(lock_type: nil).where(arel_table[:updated_at].lt(EXPIRED_INTERVAL.ago)))
|
57
49
|
end)
|
58
50
|
|
59
51
|
# Deletes all inactive process records.
|
@@ -64,11 +56,6 @@ module GoodJob # :nodoc:
|
|
64
56
|
end
|
65
57
|
end
|
66
58
|
|
67
|
-
# @return [Boolean]
|
68
|
-
def self.lock_type_migrated?
|
69
|
-
columns_hash["lock_type"].present?
|
70
|
-
end
|
71
|
-
|
72
59
|
def self.create_record(id:, with_advisory_lock: false)
|
73
60
|
attributes = {
|
74
61
|
id: id,
|
@@ -76,7 +63,7 @@ module GoodJob # :nodoc:
|
|
76
63
|
}
|
77
64
|
if with_advisory_lock
|
78
65
|
attributes[:create_with_advisory_lock] = true
|
79
|
-
attributes[:lock_type] = LOCK_TYPE_ADVISORY
|
66
|
+
attributes[:lock_type] = LOCK_TYPE_ADVISORY
|
80
67
|
end
|
81
68
|
create!(attributes)
|
82
69
|
end
|
@@ -2,19 +2,15 @@
|
|
2
2
|
<h2 class="pt-3 pb-2"><%= t ".title" %></h2>
|
3
3
|
</div>
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
<
|
9
|
-
<
|
10
|
-
|
11
|
-
<%=
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
</nav>
|
17
|
-
<% end %>
|
18
|
-
<% else %>
|
19
|
-
<h3 class="text-center my-5"><%= t ".pending_migrations" %></h3>
|
5
|
+
<%= render 'good_job/batches/table', batches: @filter.records, filter: @filter %>
|
6
|
+
<% if @filter.records.present? %>
|
7
|
+
<nav aria-label="Batch pagination" class="mt-3">
|
8
|
+
<ul class="pagination">
|
9
|
+
<li class="page-item">
|
10
|
+
<%= link_to(@filter.to_params(after_created_at: @filter.last.created_at, after_id: @filter.last.id), class: "page-link") do %>
|
11
|
+
<%= t ".older_batches" %> <span aria-hidden="true">»</span>
|
12
|
+
<% end %>
|
13
|
+
</li>
|
14
|
+
</ul>
|
15
|
+
</nav>
|
20
16
|
<% end %>
|
@@ -38,7 +38,7 @@
|
|
38
38
|
<strong class="small text-danger"><%= t "good_job.shared.error" %>:</strong>
|
39
39
|
<code class="text-wrap text-break m-0 text-secondary-emphasis"><%= execution.error %></code>
|
40
40
|
</div>
|
41
|
-
<% if
|
41
|
+
<% if execution.error_backtrace&.any? %>
|
42
42
|
<%= tag.ul class: "nav nav-tabs small w-fit-content", id: dom_id(execution, :tab), role: "tablist" do %>
|
43
43
|
<li class="nav-item" role="presentation">
|
44
44
|
<%= tag.button t(".application_trace"), class: "nav-link active p-1", id: dom_id(execution, :application), data: { bs_toggle: "tab", bs_target: dom_id(execution, :"#application_pane") }, type: "button", role: "tab", aria: { controls: dom_id(execution, :application_pane), selected: true } %>
|
@@ -102,7 +102,7 @@
|
|
102
102
|
<%= tag.span relative_time(job.last_status_at), class: "small mt-1" %>
|
103
103
|
<div>
|
104
104
|
<%= status_badge job.status %>
|
105
|
-
<% if job.status == :discarded && job.
|
105
|
+
<% if job.status == :discarded && job.error_event %>
|
106
106
|
<div class="text-black text-center">
|
107
107
|
<small><%= t(job.error_event, scope: 'good_job.error_event') %></small>
|
108
108
|
</div>
|
@@ -5,9 +5,6 @@
|
|
5
5
|
<li class="breadcrumb-item"><%= link_to t(".jobs"), jobs_path %></li>
|
6
6
|
<li class="breadcrumb-item active" aria-current="page">
|
7
7
|
<%= tag.code @job.id, class: "text-muted" %>
|
8
|
-
<% if @job.discrete? %>
|
9
|
-
<span class="badge bg-info">Discrete</span>
|
10
|
-
<% end %>
|
11
8
|
</li>
|
12
9
|
</ol>
|
13
10
|
</nav>
|
@@ -87,8 +84,4 @@
|
|
87
84
|
<%= tag.pre JSON.pretty_generate(@job.display_serialized_params) %>
|
88
85
|
<% end %>
|
89
86
|
|
90
|
-
|
91
|
-
<%= render 'executions', executions: @job.discrete_executions.reverse %>
|
92
|
-
<% else %>
|
93
|
-
<%= render 'executions', executions: @job.executions.includes_advisory_locks.reverse %>
|
94
|
-
<% end %>
|
87
|
+
<%= render 'executions', executions: @job.executions.reverse %>
|
@@ -2,49 +2,42 @@
|
|
2
2
|
<h2 class="pt-3 pb-2"><%= t ".title" %></h2>
|
3
3
|
</div>
|
4
4
|
|
5
|
-
|
6
|
-
<div class="
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
<div class="my-3 card">
|
12
|
-
<div class="list-group list-group-flush text-nowrap" role="table">
|
13
|
-
<header class="list-group-item bg-body-tertiary">
|
14
|
-
<div class="row small text-muted text-uppercase align-items-center">
|
15
|
-
<div class="col-12 col-lg-4"><%= t ".job_class" %></div>
|
16
|
-
<div class="col-lg-2 d-none d-lg-block"><%= t ".executions" %></div>
|
5
|
+
<div class="my-3 card">
|
6
|
+
<div class="list-group list-group-flush text-nowrap" role="table">
|
7
|
+
<header class="list-group-item bg-body-tertiary">
|
8
|
+
<div class="row small text-muted text-uppercase align-items-center">
|
9
|
+
<div class="col-12 col-lg-4"><%= t ".job_class" %></div>
|
10
|
+
<div class="col-lg-2 d-none d-lg-block"><%= t ".executions" %></div>
|
17
11
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
12
|
+
<div class="col-lg-2 d-none d-lg-block"><%= t ".average_duration" %></div>
|
13
|
+
<div class="col-lg-2 d-none d-lg-block"><%= t ".minimum_duration" %></div>
|
14
|
+
<div class="col-lg-2 d-none d-lg-block"><%= t ".maximum_duration" %></div>
|
15
|
+
</div>
|
16
|
+
</header>
|
23
17
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
18
|
+
<% @performances.each do |performance| %>
|
19
|
+
<div role="row" class="list-group-item py-3">
|
20
|
+
<div class="row align-items-center">
|
21
|
+
<div class="col-12 col-lg-4"><%= performance.job_class %></div>
|
22
|
+
<div class="col-6 col-lg-2 text-wrap">
|
23
|
+
<div class="d-lg-none small text-muted mt-1"><%= t ".executions" %></div>
|
24
|
+
<%= performance.executions_count %>
|
25
|
+
</div>
|
32
26
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
</div>
|
27
|
+
<div class="col-6 col-lg-2 text-wrap">
|
28
|
+
<div class="d-lg-none small text-muted mt-1"><%= t ".average_duration" %></div>
|
29
|
+
<%= format_duration performance.avg_duration %>
|
30
|
+
</div>
|
31
|
+
<div class="col-6 col-lg-2 text-wrap">
|
32
|
+
<div class="d-lg-none small text-muted mt-1"><%= t ".minimum_duration" %></div>
|
33
|
+
<%= format_duration performance.min_duration %>
|
34
|
+
</div>
|
35
|
+
<div class="col-6 col-lg-2 text-wrap">
|
36
|
+
<div class="d-lg-none small text-muted mt-1"><%= t ".maximum_duration" %></div>
|
37
|
+
<%= format_duration performance.max_duration %>
|
45
38
|
</div>
|
46
39
|
</div>
|
47
|
-
|
48
|
-
|
40
|
+
</div>
|
41
|
+
<% end %>
|
49
42
|
</div>
|
50
|
-
|
43
|
+
</div>
|
data/config/locales/de.yml
CHANGED
@@ -11,7 +11,6 @@ de:
|
|
11
11
|
batches:
|
12
12
|
index:
|
13
13
|
older_batches: Ältere Chargen
|
14
|
-
pending_migrations: GoodJob hat ausstehende Datenbankmigrationen.
|
15
14
|
title: Chargen
|
16
15
|
jobs:
|
17
16
|
actions:
|
@@ -48,7 +47,6 @@ de:
|
|
48
47
|
index:
|
49
48
|
no_cron_schedules_found: Keine Cron-Zeitpläne gefunden.
|
50
49
|
title: Cron-Zeitpläne
|
51
|
-
pending_migrations: Erfordert ausstehende GoodJob-Datenbankmigration.
|
52
50
|
show:
|
53
51
|
cron_entry_key: Cron-Eingabetaste
|
54
52
|
datetime:
|
@@ -240,6 +238,7 @@ de:
|
|
240
238
|
dark: Dunkel
|
241
239
|
light: Licht
|
242
240
|
theme: Thema
|
241
|
+
pending_migrations: GoodJob hat ausstehende Datenbankmigrationen.
|
243
242
|
secondary_navbar:
|
244
243
|
inspiration: Denk daran, auch du machst einen guten Job!
|
245
244
|
last_updated: Zuletzt aktualisiert
|
@@ -250,5 +249,3 @@ de:
|
|
250
249
|
running: Laufend
|
251
250
|
scheduled: Geplant
|
252
251
|
succeeded: Erfolgreich
|
253
|
-
shared:
|
254
|
-
needs_migration: Bitte führen Sie GoodJob-Migrationen aus.
|
data/config/locales/en.yml
CHANGED
@@ -11,7 +11,6 @@ en:
|
|
11
11
|
batches:
|
12
12
|
index:
|
13
13
|
older_batches: Older batches
|
14
|
-
pending_migrations: GoodJob has pending database migrations.
|
15
14
|
title: Batches
|
16
15
|
jobs:
|
17
16
|
actions:
|
@@ -48,7 +47,6 @@ en:
|
|
48
47
|
index:
|
49
48
|
no_cron_schedules_found: No cron schedules found.
|
50
49
|
title: Cron Schedules
|
51
|
-
pending_migrations: Requires pending GoodJob database migration.
|
52
50
|
show:
|
53
51
|
cron_entry_key: Cron Entry Key
|
54
52
|
datetime:
|
@@ -240,6 +238,7 @@ en:
|
|
240
238
|
dark: Dark
|
241
239
|
light: Light
|
242
240
|
theme: Theme
|
241
|
+
pending_migrations: GoodJob has pending database migrations.
|
243
242
|
secondary_navbar:
|
244
243
|
inspiration: Remember, you're doing a Good Job too!
|
245
244
|
last_updated: Last updated
|
@@ -250,5 +249,3 @@ en:
|
|
250
249
|
running: Running
|
251
250
|
scheduled: Scheduled
|
252
251
|
succeeded: Succeeded
|
253
|
-
shared:
|
254
|
-
needs_migration: Please run GoodJob migrations.
|
data/config/locales/es.yml
CHANGED
@@ -11,7 +11,6 @@ es:
|
|
11
11
|
batches:
|
12
12
|
index:
|
13
13
|
older_batches: Batches anteriores
|
14
|
-
pending_migrations: GoodJob tiene migraciones pendientes.
|
15
14
|
title: Batches
|
16
15
|
jobs:
|
17
16
|
actions:
|
@@ -48,7 +47,6 @@ es:
|
|
48
47
|
index:
|
49
48
|
no_cron_schedules_found: No hay tareas programadas.
|
50
49
|
title: Tareas Programadas
|
51
|
-
pending_migrations: Require las migraciones de GoodJob pendientes.
|
52
50
|
show:
|
53
51
|
cron_entry_key: Cron Entry Key
|
54
52
|
datetime:
|
@@ -240,6 +238,7 @@ es:
|
|
240
238
|
dark: Oscuro
|
241
239
|
light: Luz
|
242
240
|
theme: Tema
|
241
|
+
pending_migrations: GoodJob tiene migraciones pendientes.
|
243
242
|
secondary_navbar:
|
244
243
|
inspiration: "¡Recuerda, también tú estás haciendo un buen trabajo!"
|
245
244
|
last_updated: Última actualización
|
@@ -250,5 +249,3 @@ es:
|
|
250
249
|
running: Ejecutando
|
251
250
|
scheduled: Programado
|
252
251
|
succeeded: Exitoso
|
253
|
-
shared:
|
254
|
-
needs_migration: Ejecute las migraciones de GoodJob.
|
data/config/locales/fr.yml
CHANGED
@@ -11,7 +11,6 @@ fr:
|
|
11
11
|
batches:
|
12
12
|
index:
|
13
13
|
older_batches: Lots plus anciens
|
14
|
-
pending_migrations: GoodJob a des migrations de bases de données en attente.
|
15
14
|
title: Lots
|
16
15
|
jobs:
|
17
16
|
actions:
|
@@ -48,7 +47,6 @@ fr:
|
|
48
47
|
index:
|
49
48
|
no_cron_schedules_found: Aucune planification cron trouvée.
|
50
49
|
title: Entrées Cron
|
51
|
-
pending_migrations: Nécessite une migration de la base de données GoodJob en attente.
|
52
50
|
show:
|
53
51
|
cron_entry_key: Clé d'entrée Cron
|
54
52
|
datetime:
|
@@ -240,6 +238,7 @@ fr:
|
|
240
238
|
dark: Sombre
|
241
239
|
light: Lumière
|
242
240
|
theme: Thème
|
241
|
+
pending_migrations: GoodJob a des migrations de bases de données en attente.
|
243
242
|
secondary_navbar:
|
244
243
|
inspiration: N'oublie pas, toi aussi tu fais du bon boulot !
|
245
244
|
last_updated: Dernière mise à jour
|
@@ -250,5 +249,3 @@ fr:
|
|
250
249
|
running: En cours
|
251
250
|
scheduled: Planifiés
|
252
251
|
succeeded: Réussis
|
253
|
-
shared:
|
254
|
-
needs_migration: Veuillez exécuter des migrations GoodJob.
|