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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +79 -0
  3. data/README.md +12 -4
  4. data/engine/app/assets/scripts.js +1 -0
  5. data/engine/app/assets/style.css +5 -0
  6. data/engine/app/assets/vendor/chartjs/chart.min.js +13 -0
  7. data/engine/app/assets/vendor/rails_ujs.js +747 -0
  8. data/engine/app/charts/good_job/scheduled_by_queue_chart.rb +69 -0
  9. data/engine/app/controllers/good_job/assets_controller.rb +8 -4
  10. data/engine/app/controllers/good_job/cron_entries_controller.rb +19 -0
  11. data/engine/app/controllers/good_job/jobs_controller.rb +36 -0
  12. data/engine/app/filters/good_job/base_filter.rb +18 -56
  13. data/engine/app/filters/good_job/executions_filter.rb +9 -8
  14. data/engine/app/filters/good_job/jobs_filter.rb +12 -9
  15. data/engine/app/views/good_job/cron_entries/index.html.erb +51 -0
  16. data/engine/app/views/good_job/cron_entries/show.html.erb +4 -0
  17. data/engine/app/views/good_job/{shared/_executions_table.erb → executions/_table.erb} +1 -1
  18. data/engine/app/views/good_job/executions/index.html.erb +2 -2
  19. data/engine/app/views/good_job/{shared/_jobs_table.erb → jobs/_table.erb} +18 -6
  20. data/engine/app/views/good_job/jobs/index.html.erb +15 -2
  21. data/engine/app/views/good_job/jobs/show.html.erb +2 -2
  22. data/engine/app/views/good_job/shared/_chart.erb +19 -46
  23. data/engine/app/views/good_job/shared/_filter.erb +27 -13
  24. data/engine/app/views/good_job/shared/icons/_arrow_clockwise.html.erb +5 -0
  25. data/engine/app/views/good_job/shared/icons/_play.html.erb +4 -0
  26. data/engine/app/views/good_job/shared/icons/_skip_forward.html.erb +4 -0
  27. data/engine/app/views/good_job/shared/icons/_stop.html.erb +4 -0
  28. data/engine/app/views/layouts/good_job/base.html.erb +6 -4
  29. data/engine/config/routes.rb +17 -4
  30. data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +2 -0
  31. data/lib/generators/good_job/templates/update/migrations/02_add_cron_at_to_good_jobs.rb.erb +14 -0
  32. data/lib/generators/good_job/templates/update/migrations/03_add_cron_key_cron_at_index_to_good_jobs.rb.erb +20 -0
  33. data/lib/good_job/active_job_job.rb +228 -0
  34. data/lib/good_job/configuration.rb +1 -1
  35. data/lib/good_job/cron_entry.rb +78 -5
  36. data/lib/good_job/cron_manager.rb +4 -6
  37. data/lib/good_job/current_thread.rb +38 -5
  38. data/lib/good_job/execution.rb +53 -39
  39. data/lib/good_job/filterable.rb +42 -0
  40. data/lib/good_job/notifier.rb +17 -7
  41. data/lib/good_job/version.rb +1 -1
  42. metadata +31 -21
  43. data/engine/app/assets/vendor/chartist/chartist.css +0 -613
  44. data/engine/app/assets/vendor/chartist/chartist.js +0 -4516
  45. data/engine/app/controllers/good_job/cron_schedules_controller.rb +0 -9
  46. data/engine/app/models/good_job/active_job_job.rb +0 -127
  47. data/engine/app/views/good_job/cron_schedules/index.html.erb +0 -72
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
- module GoodJob
3
- class CronSchedulesController < GoodJob::BaseController
4
- def index
5
- configuration = GoodJob::Configuration.new({})
6
- @cron_entries = configuration.cron_entries
7
- end
8
- end
9
- end
@@ -1,127 +0,0 @@
1
- # frozen_string_literal: true
2
- module GoodJob
3
- # ActiveRecord model that represents an +ActiveJob+ job.
4
- # Is the same record data as a {GoodJob::Execution} but only the most recent execution.
5
- # Parent class can be configured with +GoodJob.active_record_parent_class+.
6
- # @!parse
7
- # class ActiveJob < ActiveRecord::Base; end
8
- class ActiveJobJob < Object.const_get(GoodJob.active_record_parent_class)
9
- include GoodJob::Lockable
10
-
11
- self.table_name = 'good_jobs'
12
- self.primary_key = 'active_job_id'
13
- self.advisory_lockable_column = 'active_job_id'
14
-
15
- has_many :executions, -> { order(created_at: :asc) }, class_name: 'GoodJob::Execution', foreign_key: 'active_job_id'
16
-
17
- # Only the most-recent unretried execution represents a "Job"
18
- default_scope { where(retried_good_job_id: nil) }
19
-
20
- # Get Jobs with given class name
21
- # @!method job_class
22
- # @!scope class
23
- # @param string [String]
24
- # Execution class name
25
- # @return [ActiveRecord::Relation]
26
- scope :job_class, ->(job_class) { where("serialized_params->>'job_class' = ?", job_class) }
27
-
28
- # First execution will run in the future
29
- scope :scheduled, -> { where(finished_at: nil).where('COALESCE(scheduled_at, created_at) > ?', DateTime.current).where("(serialized_params->>'executions')::integer < 2") }
30
- # Execution errored, will run in the future
31
- scope :retried, -> { where(finished_at: nil).where('COALESCE(scheduled_at, created_at) > ?', DateTime.current).where("(serialized_params->>'executions')::integer > 1") }
32
- # Immediate/Scheduled time to run has passed, waiting for an available thread run
33
- scope :queued, -> { where(finished_at: nil).where('COALESCE(scheduled_at, created_at) <= ?', DateTime.current).joins_advisory_locks.where(pg_locks: { locktype: nil }) }
34
- # Advisory locked and executing
35
- scope :running, -> { where(finished_at: nil).joins_advisory_locks.where.not(pg_locks: { locktype: nil }) }
36
- # Completed executing successfully
37
- scope :finished, -> { where.not(finished_at: nil).where(error: nil) }
38
- # Errored but will not be retried
39
- scope :discarded, -> { where.not(finished_at: nil).where.not(error: nil) }
40
-
41
- # Get Jobs in display order with optional keyset pagination.
42
- # @!method display_all(after_scheduled_at: nil, after_id: nil)
43
- # @!scope class
44
- # @param after_scheduled_at [DateTime, String, nil]
45
- # Display records scheduled after this time for keyset pagination
46
- # @param after_id [Numeric, String, nil]
47
- # Display records after this ID for keyset pagination
48
- # @return [ActiveRecord::Relation]
49
- scope :display_all, (lambda do |after_scheduled_at: nil, after_id: nil|
50
- query = order(Arel.sql('COALESCE(scheduled_at, created_at) DESC, id DESC'))
51
- if after_scheduled_at.present? && after_id.present?
52
- 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)
53
- elsif after_scheduled_at.present?
54
- query = query.where(Arel.sql('(COALESCE(scheduled_at, created_at)) < (:after_scheduled_at)'), after_scheduled_at: after_scheduled_at)
55
- end
56
- query
57
- end)
58
-
59
- def id
60
- active_job_id
61
- end
62
-
63
- def _execution_id
64
- attributes['id']
65
- end
66
-
67
- def job_class
68
- serialized_params['job_class']
69
- end
70
-
71
- def status
72
- if finished_at.present?
73
- if error.present?
74
- :discarded
75
- else
76
- :finished
77
- end
78
- elsif (scheduled_at || created_at) > DateTime.current
79
- if serialized_params.fetch('executions', 0) > 1
80
- :retried
81
- else
82
- :scheduled
83
- end
84
- elsif running?
85
- :running
86
- else
87
- :queued
88
- end
89
- end
90
-
91
- def head_execution
92
- executions.last
93
- end
94
-
95
- def tail_execution
96
- executions.first
97
- end
98
-
99
- def executions_count
100
- aj_count = serialized_params.fetch('executions', 0)
101
- # The execution count within serialized_params is not updated
102
- # once the underlying execution has been executed.
103
- if status.in? [:discarded, :finished, :running]
104
- aj_count + 1
105
- else
106
- aj_count
107
- end
108
- end
109
-
110
- def preserved_executions_count
111
- executions.size
112
- end
113
-
114
- def recent_error
115
- error.presence || executions[-2]&.error
116
- end
117
-
118
- def running?
119
- # Avoid N+1 Query: `.joins_advisory_locks.select('good_jobs.*', 'pg_locks.locktype AS locktype')`
120
- if has_attribute?(:locktype)
121
- self['locktype'].present?
122
- else
123
- advisory_locked?
124
- end
125
- end
126
- end
127
- end
@@ -1,72 +0,0 @@
1
- <% if @cron_entries.present? %>
2
- <div class="card my-3">
3
- <div class="table-responsive">
4
- <table class="table card-table table-bordered table-hover table-sm mb-0">
5
- <thead>
6
- <th>Cron Job Name</th>
7
- <th>Configuration</th>
8
- <th>
9
- Set&nbsp;
10
- <%= tag.button "Toggle", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
11
- data: { bs_toggle: "collapse", bs_target: ".job-properties" },
12
- aria: { expanded: false, controls: @cron_entries.map { |cron_entry| dom_id(cron_entry, 'properties') }.join(" ") }
13
- %>
14
- </th>
15
- <th>
16
- Args&nbsp;
17
- <%= tag.button "Toggle", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
18
- data: { bs_toggle: "collapse", bs_target: ".job-args" },
19
- aria: { expanded: false, controls: @cron_entries.map { |cron_entry| dom_id(cron_entry, 'args') }.join(" ") }
20
- %>
21
- </th>
22
- <th>Class</th>
23
- <th>Description</th>
24
- <th>Next scheduled</th>
25
- </thead>
26
- <tbody>
27
- <% @cron_entries.each do |cron_entry| %>
28
- <tr>
29
- <td class="font-monospace"><%= cron_entry.key %></td>
30
- <td class="font-monospace"><%= cron_entry.cron %></td>
31
- <td>
32
- <%=
33
- case cron_entry.set
34
- when NilClass
35
- "None"
36
- when Proc
37
- "Lambda/Callable"
38
- when Hash
39
- tag.button("Preview", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
40
- data: { bs_toggle: "collapse", bs_target: "##{dom_id(cron_entry, 'properties')}" },
41
- aria: { expanded: false, controls: dom_id(cron_entry, 'properties') }) +
42
- tag.pre(JSON.pretty_generate(cron_entry.set), id: dom_id(cron_entry, 'properties'), class: "collapse job-properties")
43
- end
44
- %>
45
- </td>
46
- <td>
47
- <%=
48
- case cron_entry.args
49
- when NilClass
50
- "None"
51
- when Proc
52
- "Lambda/Callable"
53
- when Hash
54
- tag.button("Preview", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
55
- data: { bs_toggle: "collapse", bs_target: "##{dom_id(cron_entry, 'args')}" },
56
- aria: { expanded: false, controls: dom_id(cron_entry, 'args') }) +
57
- tag.pre(JSON.pretty_generate(cron_entry.args), id: dom_id(cron_entry, 'args'), class: "collapse job-args")
58
- end
59
- %>
60
- </td>
61
- <td class="font-monospace"><%= cron_entry.job_class %></td>
62
- <td><%= cron_entry.description %></td>
63
- <td><%= cron_entry.next_at.to_local_time %></td>
64
- </tr>
65
- <% end %>
66
- </tbody>
67
- </table>
68
- </div>
69
- </div>
70
- <% else %>
71
- <em>No cron jobs present.</em>
72
- <% end %>