good_job 3.18.3 → 3.19.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 930bb77e947ac155ee655210ab15ef32ac40fc2d6487eaf80b0005d7e5675156
4
- data.tar.gz: b3e26f0b7f4d60483a6d0646d5797654e447542b8d6cb9eca22e2cb7cbfaac78
3
+ metadata.gz: 6d7862005653a1c6fd88d20ffdf1f15e6168dea77f8f47ca1f967a646b7f6a3d
4
+ data.tar.gz: eb267fbf670b7e2cf0b2b67365f32b28b48378bfd1c36f0dc2decae6b5d2d91b
5
5
  SHA512:
6
- metadata.gz: d92deaa0fa930cd8e8bf9b6b51f37c6d8ec1b75d221d939b6b5d2cb2ec30153931873951bbfca9af5dd24867e6131133f865368845a2cf783e8212137ed3bd76
7
- data.tar.gz: 0c3ff2fd89cb38061ab145d74180596334fc8f38cba5f2a61d11fcf791d03534b823361bd9546cea4cea316d04d3cfba979d459b1b3d8185386298cdb71593a0
6
+ metadata.gz: c87de07512abd5b7f51e170b69a2568df98878e8c6a572141cb2f4126bc4c4a21b80f49fed0a5946e1c4b7f2743b463b37d605432c2d63b651b0d84503c80d72
7
+ data.tar.gz: 9349bc27172b1051d90e942b90ed961a30a23ba68d0960d66e007c7018cbca8984af8080dc846fc100f4abf4602cd47efb040e185db30b5e2528ba5a7d51a2bd
data/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.19.1](https://github.com/bensheldon/good_job/tree/v3.19.1) (2023-09-20)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.19.0...v3.19.1)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Add `good_job_application_controller` load hook [\#1081](https://github.com/bensheldon/good_job/pull/1081) ([bensheldon](https://github.com/bensheldon))
10
+
11
+ ## [v3.19.0](https://github.com/bensheldon/good_job/tree/v3.19.0) (2023-09-19)
12
+
13
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.18.3...v3.19.0)
14
+
15
+ **Implemented enhancements:**
16
+
17
+ - "Force" discard jobs that are already running/runaway to prevent retry [\#1073](https://github.com/bensheldon/good_job/pull/1073) ([jgrau](https://github.com/jgrau))
18
+
19
+ **Closed issues:**
20
+
21
+ - Possible Memory Leak [\#1074](https://github.com/bensheldon/good_job/issues/1074)
22
+ - What's the best way to stop and discard a running job? [\#625](https://github.com/bensheldon/good_job/issues/625)
23
+
24
+ **Merged pull requests:**
25
+
26
+ - AdvisoryLockable: Abort record create if with\_advisory\_lock fails to acquire advisory lock [\#1078](https://github.com/bensheldon/good_job/pull/1078) ([bensheldon](https://github.com/bensheldon))
27
+ - Wrap all test background threads in Rails executors; better test logging/debugging [\#1077](https://github.com/bensheldon/good_job/pull/1077) ([bensheldon](https://github.com/bensheldon))
28
+
3
29
  ## [v3.18.3](https://github.com/bensheldon/good_job/tree/v3.18.3) (2023-09-16)
4
30
 
5
31
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.18.2...v3.18.3)
@@ -57,5 +57,7 @@ module GoodJob
57
57
  I18n.default_locale
58
58
  end
59
59
  end
60
+
61
+ ActiveSupport.run_load_hooks(:good_job_application_controller, self)
60
62
  end
61
63
  end
@@ -3,6 +3,7 @@
3
3
  module GoodJob
4
4
  class JobsController < GoodJob::ApplicationController
5
5
  DISCARD_MESSAGE = "Discarded through dashboard"
6
+ FORCE_DISCARD_MESSAGE = "Force discarded through dashboard"
6
7
 
7
8
  ACTIONS = {
8
9
  discard: "discarded",
@@ -66,6 +67,12 @@ module GoodJob
66
67
  redirect_back(fallback_location: jobs_path, notice: t(".notice"))
67
68
  end
68
69
 
70
+ def force_discard
71
+ @job = Job.find(params[:id])
72
+ @job.force_discard_job(FORCE_DISCARD_MESSAGE)
73
+ redirect_back(fallback_location: jobs_path, notice: t(".notice"))
74
+ end
75
+
69
76
  def reschedule
70
77
  @job = Job.find(params[:id])
71
78
  @job.reschedule_job
@@ -133,7 +133,12 @@ module GoodJob
133
133
  # @return [Boolean]
134
134
  attr_accessor :create_with_advisory_lock
135
135
 
136
- after_create -> { advisory_lock }, if: :create_with_advisory_lock
136
+ after_create lambda {
137
+ advisory_lock || begin
138
+ errors.add(self.class.advisory_lockable_column, "Failed to acquire advisory lock: #{lockable_key}")
139
+ raise ActiveRecord::RecordInvalid # do not reference the record because it can cause I18n missing translation error
140
+ end
141
+ }, if: :create_with_advisory_lock
137
142
  end
138
143
 
139
144
  class_methods do
@@ -222,6 +227,47 @@ module GoodJob
222
227
  connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Unlock', binds).first['unlocked']
223
228
  end
224
229
 
230
+ # Tests whether the provided key has an advisory lock on it.
231
+ # @param key [String, Symbol] Key to test lock against
232
+ # @return [Boolean]
233
+ def advisory_locked_key?(key)
234
+ query = <<~SQL.squish
235
+ SELECT 1 AS one
236
+ FROM pg_locks
237
+ WHERE pg_locks.locktype = 'advisory'
238
+ AND pg_locks.objsubid = 1
239
+ AND pg_locks.classid = ('x' || substr(md5($1::text), 1, 16))::bit(32)::int
240
+ AND pg_locks.objid = (('x' || substr(md5($2::text), 1, 16))::bit(64) << 32)::bit(32)::int
241
+ LIMIT 1
242
+ SQL
243
+ binds = [
244
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
245
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
246
+ ]
247
+ connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Locked?', binds).any?
248
+ end
249
+
250
+ # Tests whether this record is locked by the current database session.
251
+ # @param key [String, Symbol] Key to test lock against
252
+ # @return [Boolean]
253
+ def owns_advisory_lock_key?(key)
254
+ query = <<~SQL.squish
255
+ SELECT 1 AS one
256
+ FROM pg_locks
257
+ WHERE pg_locks.locktype = 'advisory'
258
+ AND pg_locks.objsubid = 1
259
+ AND pg_locks.classid = ('x' || substr(md5($1::text), 1, 16))::bit(32)::int
260
+ AND pg_locks.objid = (('x' || substr(md5($2::text), 1, 16))::bit(64) << 32)::bit(32)::int
261
+ AND pg_locks.pid = pg_backend_pid()
262
+ LIMIT 1
263
+ SQL
264
+ binds = [
265
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
266
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
267
+ ]
268
+ connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Owns Advisory Lock?', binds).any?
269
+ end
270
+
225
271
  def _advisory_lockable_column
226
272
  advisory_lockable_column || primary_key
227
273
  end
@@ -318,20 +364,7 @@ module GoodJob
318
364
  # @param key [String, Symbol] Key to test lock against
319
365
  # @return [Boolean]
320
366
  def advisory_locked?(key: lockable_key)
321
- query = <<~SQL.squish
322
- SELECT 1 AS one
323
- FROM pg_locks
324
- WHERE pg_locks.locktype = 'advisory'
325
- AND pg_locks.objsubid = 1
326
- AND pg_locks.classid = ('x' || substr(md5($1::text), 1, 16))::bit(32)::int
327
- AND pg_locks.objid = (('x' || substr(md5($2::text), 1, 16))::bit(64) << 32)::bit(32)::int
328
- LIMIT 1
329
- SQL
330
- binds = [
331
- ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
332
- ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
333
- ]
334
- self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Locked?', binds).any?
367
+ self.class.advisory_locked_key?(key)
335
368
  end
336
369
 
337
370
  # Tests whether this record does not have an advisory lock on it.
@@ -345,6 +378,7 @@ module GoodJob
345
378
  # @param key [String, Symbol] Key to test lock against
346
379
  # @return [Boolean]
347
380
  def owns_advisory_lock?(key: lockable_key)
381
+ self.class.owns_advisory_lock_key?(key)
348
382
  query = <<~SQL.squish
349
383
  SELECT 1 AS one
350
384
  FROM pg_locks
@@ -30,10 +30,10 @@ module GoodJob
30
30
 
31
31
  # Runs the block with self.logger silenced.
32
32
  # If self.logger is nil, simply runs the block.
33
- def self.with_logger_silenced(&block)
33
+ def self.with_logger_silenced(silent: true, &block)
34
34
  # Assign to a local variable, just in case it's modified in another thread concurrently
35
35
  logger = self.logger
36
- if logger.respond_to? :silence
36
+ if silent && logger.respond_to?(:silence)
37
37
  logger.silence(&block)
38
38
  else
39
39
  yield
@@ -215,32 +215,17 @@ module GoodJob
215
215
  # @return [void]
216
216
  def discard_job(message)
217
217
  with_advisory_lock do
218
- execution = head_execution(reload: true)
219
- active_job = execution.active_job(ignore_deserialization_errors: true)
220
-
221
- raise ActionForStateMismatchError if execution.finished_at.present?
222
-
223
- job_error = GoodJob::Job::DiscardJobError.new(message)
224
-
225
- update_execution = proc do
226
- execution.update(
227
- {
228
- finished_at: Time.current,
229
- error: GoodJob::Execution.format_error(job_error),
230
- }.tap do |attrs|
231
- attrs[:error_event] = ERROR_EVENT_DISCARDED if self.class.error_event_migrated?
232
- end
233
- )
234
- end
235
-
236
- if active_job.respond_to?(:instrument)
237
- active_job.send :instrument, :discard, error: job_error, &update_execution
238
- else
239
- update_execution.call
240
- end
218
+ _discard_job(message)
241
219
  end
242
220
  end
243
221
 
222
+ # Force discard a job so that it will not be executed further. Force discard allows discarding
223
+ # a running job.
224
+ # This action will add a {DiscardJobError} to the job's {Execution} and mark it as finished.
225
+ def force_discard_job(message)
226
+ _discard_job(message)
227
+ end
228
+
244
229
  # Reschedule a scheduled job so that it executes immediately (or later) by the next available execution thread.
245
230
  # @param scheduled_at [DateTime, Time] When to reschedule the job
246
231
  # @return [void]
@@ -277,5 +262,33 @@ module GoodJob
277
262
  def _head?
278
263
  _execution_id == head_execution(reload: true).id
279
264
  end
265
+
266
+ private
267
+
268
+ def _discard_job(message)
269
+ execution = head_execution(reload: true)
270
+ active_job = execution.active_job(ignore_deserialization_errors: true)
271
+
272
+ raise ActionForStateMismatchError if execution.finished_at.present?
273
+
274
+ job_error = GoodJob::Job::DiscardJobError.new(message)
275
+
276
+ update_execution = proc do
277
+ execution.update(
278
+ {
279
+ finished_at: Time.current,
280
+ error: GoodJob::Execution.format_error(job_error),
281
+ }.tap do |attrs|
282
+ attrs[:error_event] = ERROR_EVENT_DISCARDED if self.class.error_event_migrated?
283
+ end
284
+ )
285
+ end
286
+
287
+ if active_job.respond_to?(:instrument)
288
+ active_job.send :instrument, :discard, error: job_error, &update_execution
289
+ else
290
+ update_execution.call
291
+ end
292
+ end
280
293
  end
281
294
  end
@@ -127,6 +127,13 @@
127
127
  <%=t "good_job.actions.discard" %>
128
128
  <% end %>
129
129
  </li>
130
+ <li>
131
+ <% job_force_discardable = job.status.in? [:running] %>
132
+ <%= link_to force_discard_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_force_discardable}", title: t("good_job.jobs.actions.force_discard"), data: { confirm: t("good_job.jobs.actions.confirm_force_discard"), disable: true } do %>
133
+ <%= render "good_job/shared/icons/eject" %>
134
+ <%=t "good_job.actions.force_discard" %>
135
+ <% end %>
136
+ </li>
130
137
  <li>
131
138
  <%= link_to retry_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job.status == :discarded}", title: t("good_job.jobs.actions.retry"), data: { confirm: t("good_job.jobs.actions.confirm_retry"), disable: true } do %>
132
139
  <%= render "good_job/shared/icons/arrow_clockwise" %>
@@ -0,0 +1,4 @@
1
+ <!-- https://icons.getbootstrap.com/icons/eject/ -->
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eject" viewBox="0 0 16 16">
3
+ <path d="M7.27 1.047a1 1 0 0 1 1.46 0l6.345 6.77c.6.638.146 1.683-.73 1.683H1.656C.78 9.5.326 8.455.926 7.816L7.27 1.047zM14.346 8.5 8 1.731 1.654 8.5h12.692zM.5 11.5a1 1 0 0 1 1-1h13a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1h-13a1 1 0 0 1-1-1v-1zm14 0h-13v1h13v-1z" />
4
+ </svg>
@@ -4,6 +4,7 @@ de:
4
4
  actions:
5
5
  destroy: Zerstören
6
6
  discard: Verwerfen
7
+ force_discard: Verwerfen erzwingen
7
8
  inspect: Prüfen
8
9
  reschedule: Umplanen
9
10
  retry: Wiederholen
@@ -110,10 +111,14 @@ de:
110
111
  actions:
111
112
  confirm_destroy: Sind Sie sicher, dass Sie den Job zerstören wollen?
112
113
  confirm_discard: Sind Sie sicher, dass Sie den Job verwerfen wollen?
114
+ confirm_force_discard: 'Sind Sie sicher, dass Sie das Verwerfen dieses Jobs erzwingen möchten? Der Job wird als verworfen markiert, aber der laufende Job wird nicht gestoppt – er wird jedoch bei Fehlern nicht erneut versucht.
115
+
116
+ '
113
117
  confirm_reschedule: Möchten Sie den Auftrag wirklich verschieben?
114
118
  confirm_retry: Möchten Sie den Job wirklich wiederholen?
115
119
  destroy: Arbeit vernichten
116
120
  discard: Auftrag verwerfen
121
+ force_discard: Job verwerfen erzwingen
117
122
  reschedule: Auftrag neu planen
118
123
  retry: Job wiederholen
119
124
  destroy:
@@ -124,6 +129,8 @@ de:
124
129
  in_queue: in der Warteschlange
125
130
  runtime: Laufzeit
126
131
  title: Hinrichtungen
132
+ force_discard:
133
+ notice: Der Job wurde zwangsweise verworfen. Die Ausführung wird fortgesetzt, bei Fehlern wird der Vorgang jedoch nicht wiederholt
127
134
  index:
128
135
  job_pagination: Job-Paginierung
129
136
  older_jobs: Ältere Berufe
@@ -4,6 +4,7 @@ en:
4
4
  actions:
5
5
  destroy: Destroy
6
6
  discard: Discard
7
+ force_discard: Force discard
7
8
  inspect: Inspect
8
9
  reschedule: Reschedule
9
10
  retry: Retry
@@ -110,10 +111,14 @@ en:
110
111
  actions:
111
112
  confirm_destroy: Are you sure you want to destroy the job?
112
113
  confirm_discard: Are you usure you want to discard the job?
114
+ confirm_force_discard: 'Are you sure you want to force discard this job? The job will be marked as discarded but the running job will not be stopped - it will, however, not be retried on failures.
115
+
116
+ '
113
117
  confirm_reschedule: Are you sure you want to reschedule the job?
114
118
  confirm_retry: Are you sure you want to retry the job?
115
119
  destroy: Destroy job
116
120
  discard: Discard job
121
+ force_discard: Force discard job
117
122
  reschedule: Reschedule job
118
123
  retry: Retry job
119
124
  destroy:
@@ -124,6 +129,8 @@ en:
124
129
  in_queue: in queue
125
130
  runtime: runtime
126
131
  title: Executions
132
+ force_discard:
133
+ notice: Job has been force discarded. It will continue to run but it will not be retried on failures
127
134
  index:
128
135
  job_pagination: Job pagination
129
136
  older_jobs: Older jobs
@@ -4,6 +4,7 @@ es:
4
4
  actions:
5
5
  destroy: Eliminar
6
6
  discard: Descartar
7
+ force_discard: Forzar descarte
7
8
  inspect: Inspeccionar
8
9
  reschedule: Reprogramar
9
10
  retry: Reintentar
@@ -110,10 +111,12 @@ es:
110
111
  actions:
111
112
  confirm_destroy: "¿Estás seguro que querés eliminar esta tarea?"
112
113
  confirm_discard: "¿Estás seguro que querés descartar esta tarea?"
114
+ confirm_force_discard: "¿Está seguro de que desea forzar el descarte de este trabajo? El trabajo se marcará como descartado, pero el trabajo en ejecución no se detendrá; sin embargo, no se volverá a intentar en caso de falla.\n"
113
115
  confirm_reschedule: "¿Estás seguro que querés reprogramar esta tarea?"
114
116
  confirm_retry: "¿Estás seguro que querés reintentar esta tarea?"
115
117
  destroy: Eliminar tarea
116
118
  discard: Descartar tarea
119
+ force_discard: Forzar el descarte del trabajo
117
120
  reschedule: Reprogramar tarea
118
121
  retry: Reiuntentar tarea
119
122
  destroy:
@@ -124,6 +127,8 @@ es:
124
127
  in_queue: en cola
125
128
  runtime: en ejecución
126
129
  title: Ejecuciones
130
+ force_discard:
131
+ notice: Job ha sido descartado a la fuerza. Continuará ejecutándose pero no se volverá a intentar en caso de fallas.
127
132
  index:
128
133
  job_pagination: Paginación de tareas
129
134
  older_jobs: Tareas anteriores
@@ -4,6 +4,7 @@ fr:
4
4
  actions:
5
5
  destroy: Détruire
6
6
  discard: Mettre au rebut
7
+ force_discard: Forcer le rejet
7
8
  inspect: Inspecter
8
9
  reschedule: Reprogrammer
9
10
  retry: Recommencez
@@ -110,10 +111,14 @@ fr:
110
111
  actions:
111
112
  confirm_destroy: Voulez-vous vraiment détruire le job ?
112
113
  confirm_discard: Voulez-vous vraiment mettre au rebut le job ?
114
+ confirm_force_discard: 'Êtes-vous sûr de vouloir forcer l''abandon de cette tâche ? Le travail sera marqué comme abandonné mais le travail en cours d''exécution ne sera pas arrêté - il ne sera cependant pas réessayé en cas d''échec.
115
+
116
+ '
113
117
  confirm_reschedule: Voulez-vous vraiment replanifier le job ?
114
118
  confirm_retry: Voulez-vous vraiment réessayer le job ?
115
119
  destroy: Détruire le job
116
120
  discard: Mettre au rebut le job
121
+ force_discard: Forcer l'abandon du travail
117
122
  reschedule: Replanifier le job
118
123
  retry: Réessayer le job
119
124
  destroy:
@@ -124,6 +129,8 @@ fr:
124
129
  in_queue: Dans la file d'attente
125
130
  runtime: Durée
126
131
  title: Exécutions
132
+ force_discard:
133
+ notice: Le travail a été abandonné de force. Il continuera à fonctionner mais il ne sera pas réessayé en cas d'échec
127
134
  index:
128
135
  job_pagination: Pagination du job
129
136
  older_jobs: Jobs plus anciens
@@ -4,6 +4,7 @@ ja:
4
4
  actions:
5
5
  destroy: 削除する
6
6
  discard: 破棄する
7
+ force_discard: 強制破棄
7
8
  inspect: 調査する
8
9
  reschedule: 再スケジュールする
9
10
  retry: 再試行する
@@ -110,10 +111,14 @@ ja:
110
111
  actions:
111
112
  confirm_destroy: ジョブを削除してもよろしいですか?
112
113
  confirm_discard: ジョブを破棄してもよろしいですか?
114
+ confirm_force_discard: 'このジョブを強制的に破棄してもよろしいですか?ジョブは破棄済みとしてマークされますが、実行中のジョブは停止されません。ただし、失敗した場合は再試行されません。
115
+
116
+ '
113
117
  confirm_reschedule: ジョブを再スケジュールしてもよろしいですか?
114
118
  confirm_retry: ジョブを再試行してもよろしいですか?
115
119
  destroy: ジョブを削除
116
120
  discard: ジョブを破棄
121
+ force_discard: ジョブを強制的に破棄する
117
122
  reschedule: ジョブを再スケジュール
118
123
  retry: ジョブを再試行
119
124
  destroy:
@@ -124,6 +129,8 @@ ja:
124
129
  in_queue: 待機中
125
130
  runtime: 実行時間
126
131
  title: 実行
132
+ force_discard:
133
+ notice: ジョブは強制的に破棄されました。実行は継続されますが、失敗した場合は再試行されません
127
134
  index:
128
135
  job_pagination: ジョブのページネーション
129
136
  older_jobs: 古いジョブ
@@ -4,6 +4,7 @@ nl:
4
4
  actions:
5
5
  destroy: Vernietigen
6
6
  discard: Weggooien
7
+ force_discard: Forceer weggooien
7
8
  inspect: Inspecteren
8
9
  reschedule: Opnieuw plannen
9
10
  retry: Opnieuw proberen
@@ -110,10 +111,14 @@ nl:
110
111
  actions:
111
112
  confirm_destroy: Weet je zeker dat je de baan wilt vernietigen?
112
113
  confirm_discard: Wilt u de baan afwijzen?
114
+ confirm_force_discard: 'Weet u zeker dat u deze taak geforceerd wilt weggooien? De taak wordt gemarkeerd als verwijderd, maar de lopende taak wordt niet gestopt. Bij fouten wordt deze echter niet opnieuw geprobeerd.
115
+
116
+ '
113
117
  confirm_reschedule: Weet u zeker dat u de taak opnieuw wilt inplannen?
114
118
  confirm_retry: Weet u zeker dat u de taak opnieuw wilt proberen?
115
119
  destroy: Baan vernietigen
116
120
  discard: Gooi de baan weg
121
+ force_discard: Forceer taak weggooien
117
122
  reschedule: Taak opnieuw plannen
118
123
  retry: Taak opnieuw proberen
119
124
  destroy:
@@ -124,6 +129,8 @@ nl:
124
129
  in_queue: in de wachtrij
125
130
  runtime: looptijd
126
131
  title: Executies
132
+ force_discard:
133
+ notice: Baan is gedwongen verwijderd. Het blijft actief, maar wordt niet opnieuw geprobeerd als er fouten optreden
127
134
  index:
128
135
  job_pagination: Taak paginering
129
136
  older_jobs: Oudere banen
@@ -4,6 +4,7 @@ ru:
4
4
  actions:
5
5
  destroy: Разрушать
6
6
  discard: Отказаться
7
+ force_discard: Принудительно отменить
7
8
  inspect: Осмотреть
8
9
  reschedule: Перенести
9
10
  retry: Повторить попытку
@@ -134,10 +135,14 @@ ru:
134
135
  actions:
135
136
  confirm_destroy: Вы уверены, что хотите уничтожить задание?
136
137
  confirm_discard: Вы уверены, что хотите отказаться от задания?
138
+ confirm_force_discard: 'Вы уверены, что хотите принудительно отменить это задание? Задание будет помечено как отброшенное, но выполняемое задание не будет остановлено, однако в случае сбоя оно не будет повторено.
139
+
140
+ '
137
141
  confirm_reschedule: Вы уверены, что хотите перенести задание?
138
142
  confirm_retry: Вы уверены, что хотите повторить задание?
139
143
  destroy: Уничтожить работу
140
144
  discard: Отменить задание
145
+ force_discard: Принудительно отменить задание
141
146
  reschedule: Перенести задание
142
147
  retry: Повторить задание
143
148
  destroy:
@@ -148,6 +153,8 @@ ru:
148
153
  in_queue: в очереди
149
154
  runtime: время выполнения
150
155
  title: Казни
156
+ force_discard:
157
+ notice: Иов был принудительно отброшен. Он продолжит работу, но не будет повторяться в случае сбоя.
151
158
  index:
152
159
  job_pagination: Пагинация вакансий
153
160
  older_jobs: Старые рабочие места
@@ -4,6 +4,7 @@ tr:
4
4
  actions:
5
5
  destroy: Sil
6
6
  discard: İptal Et
7
+ force_discard: Atmaya zorla
7
8
  inspect: İncele
8
9
  reschedule: Yeniden planla
9
10
  retry: Tekrar dene
@@ -110,10 +111,14 @@ tr:
110
111
  actions:
111
112
  confirm_destroy: Bu işi silmek istediğinizden emin misiniz?
112
113
  confirm_discard: Bu işi iptal etmek istediğinizden emin misiniz?
114
+ confirm_force_discard: 'Bu işi zorla iptal etmek istediğinizden emin misiniz? İş atıldı olarak işaretlenecek ancak devam eden iş durdurulmayacak; ancak başarısızlık durumunda yeniden denenmeyecek.
115
+
116
+ '
113
117
  confirm_reschedule: Bu işi yeniden planlamak istediğinizden emin misiniz?
114
118
  confirm_retry: Bu işi tekrar denemek istediğinizden emin misiniz?
115
119
  destroy: İşi Sil
116
120
  discard: İşi İptal Et
121
+ force_discard: İşi zorla atmaya zorla
117
122
  reschedule: İşi Yeniden Planla
118
123
  retry: İşi Tekrar Dene
119
124
  destroy:
@@ -124,6 +129,8 @@ tr:
124
129
  in_queue: sırada
125
130
  runtime: çalışma süresi
126
131
  title: İşlemler
132
+ force_discard:
133
+ notice: İş zorla atıldı. Çalışmaya devam edecek ancak arıza durumunda yeniden denenmeyecek
127
134
  index:
128
135
  job_pagination: İş sayfalandırması
129
136
  older_jobs: Daha eski işler
@@ -4,6 +4,7 @@ uk:
4
4
  actions:
5
5
  destroy: Видалити
6
6
  discard: Відхилити
7
+ force_discard: Примусово скинути
7
8
  inspect: Оглянути
8
9
  reschedule: Перепланувати
9
10
  retry: Повторити
@@ -134,10 +135,14 @@ uk:
134
135
  actions:
135
136
  confirm_destroy: Ви впевнені, що хочете видалити завдання?
136
137
  confirm_discard: Ви впевнені, що хочете відхилити завдання?
138
+ confirm_force_discard: 'Ви впевнені, що хочете примусово скасувати цю роботу? Завдання буде позначено як відхилене, але виконуване завдання не буде зупинено – однак його не буде повторено у випадку помилок.
139
+
140
+ '
137
141
  confirm_reschedule: Ви впевнені, що хочете перепланувати завдання?
138
142
  confirm_retry: Ви впевнені, що хочете повторити завдання?
139
143
  destroy: Видалити завдання
140
144
  discard: Відхилити завдання
145
+ force_discard: Примусово скасувати завдання
141
146
  reschedule: Перепланувати завдання
142
147
  retry: Повторити завдання
143
148
  destroy:
@@ -148,6 +153,8 @@ uk:
148
153
  in_queue: у черзі
149
154
  runtime: час виконання
150
155
  title: Виконання
156
+ force_discard:
157
+ notice: Роботу примусово скасовано. Він продовжуватиме працювати, але не буде повторюватися в разі помилок
151
158
  index:
152
159
  job_pagination: Пагінація робіт
153
160
  older_jobs: Старі роботи
data/config/routes.rb CHANGED
@@ -11,6 +11,7 @@ GoodJob::Engine.routes.draw do
11
11
 
12
12
  member do
13
13
  put :discard
14
+ put :force_discard
14
15
  put :reschedule
15
16
  put :retry
16
17
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module GoodJob
4
4
  # GoodJob gem version.
5
- VERSION = '3.18.3'
5
+ VERSION = '3.19.1'
6
6
 
7
7
  # GoodJob version as Gem::Version object
8
8
  GEM_VERSION = Gem::Version.new(VERSION)
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: 3.18.3
4
+ version: 3.19.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-16 00:00:00.000000000 Z
11
+ date: 2023-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -313,6 +313,7 @@ files:
313
313
  - app/views/good_job/shared/icons/_clock.html.erb
314
314
  - app/views/good_job/shared/icons/_dash_circle.html.erb
315
315
  - app/views/good_job/shared/icons/_dots.html.erb
316
+ - app/views/good_job/shared/icons/_eject.html.erb
316
317
  - app/views/good_job/shared/icons/_exclamation.html.erb
317
318
  - app/views/good_job/shared/icons/_info.html.erb
318
319
  - app/views/good_job/shared/icons/_moon_stars_fill.html.erb