good_job 1.2.2 → 1.3.0
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 +4 -4
- data/CHANGELOG.md +111 -2
- data/README.md +324 -160
- data/engine/app/controllers/good_job/active_jobs_controller.rb +8 -0
- data/engine/app/controllers/good_job/base_controller.rb +5 -0
- data/engine/app/controllers/good_job/dashboards_controller.rb +50 -0
- data/engine/app/helpers/good_job/application_helper.rb +4 -0
- data/engine/app/views/assets/_style.css.erb +16 -0
- data/engine/app/views/good_job/active_jobs/show.html.erb +1 -0
- data/engine/app/views/good_job/dashboards/index.html.erb +19 -0
- data/engine/app/views/layouts/good_job/base.html.erb +61 -0
- data/engine/app/views/shared/_chart.erb +51 -0
- data/engine/app/views/shared/_jobs_table.erb +26 -0
- data/engine/app/views/vendor/bootstrap/_bootstrap-native.js.erb +1662 -0
- data/engine/app/views/vendor/bootstrap/_bootstrap.css.erb +10258 -0
- data/engine/app/views/vendor/chartist/_chartist.css.erb +613 -0
- data/engine/app/views/vendor/chartist/_chartist.js.erb +4516 -0
- data/engine/config/routes.rb +4 -0
- data/engine/lib/good_job/engine.rb +5 -0
- data/lib/active_job/queue_adapters/good_job_adapter.rb +3 -2
- data/lib/generators/good_job/install_generator.rb +8 -0
- data/lib/good_job.rb +59 -27
- data/lib/good_job/adapter.rb +38 -0
- data/lib/good_job/cli.rb +66 -13
- data/lib/good_job/configuration.rb +61 -2
- data/lib/good_job/job.rb +126 -36
- data/lib/good_job/lockable.rb +119 -6
- data/lib/good_job/log_subscriber.rb +70 -6
- data/lib/good_job/multi_scheduler.rb +6 -0
- data/lib/good_job/notifier.rb +55 -29
- data/lib/good_job/performer.rb +38 -0
- data/lib/good_job/railtie.rb +1 -0
- data/lib/good_job/scheduler.rb +48 -40
- data/lib/good_job/version.rb +2 -1
- metadata +163 -7
- data/lib/good_job/pg_locks.rb +0 -21
@@ -1,23 +1,29 @@
|
|
1
1
|
module GoodJob
|
2
|
+
# Delegates the interface of a single {Scheduler} to multiple Schedulers.
|
2
3
|
class MultiScheduler
|
4
|
+
# @return [array<Scheduler>] List of the scheduler delegates
|
3
5
|
attr_reader :schedulers
|
4
6
|
|
5
7
|
def initialize(schedulers)
|
6
8
|
@schedulers = schedulers
|
7
9
|
end
|
8
10
|
|
11
|
+
# Delegates to {Scheduler#shutdown}.
|
9
12
|
def shutdown(wait: true)
|
10
13
|
schedulers.each { |s| s.shutdown(wait: wait) }
|
11
14
|
end
|
12
15
|
|
16
|
+
# Delegates to {Scheduler#shutdown?}.
|
13
17
|
def shutdown?
|
14
18
|
schedulers.all?(&:shutdown?)
|
15
19
|
end
|
16
20
|
|
21
|
+
# Delegates to {Scheduler#restart}.
|
17
22
|
def restart(wait: true)
|
18
23
|
schedulers.each { |s| s.restart(wait: wait) }
|
19
24
|
end
|
20
25
|
|
26
|
+
# Delegates to {Scheduler#create_thread}.
|
21
27
|
def create_thread(state = nil)
|
22
28
|
results = []
|
23
29
|
any_true = schedulers.any? do |scheduler|
|
data/lib/good_job/notifier.rb
CHANGED
@@ -2,10 +2,16 @@ require 'concurrent/atomic/atomic_boolean'
|
|
2
2
|
|
3
3
|
module GoodJob # :nodoc:
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# Notifiers hook into Postgres LISTEN/NOTIFY functionality to emit and listen for notifications across processes.
|
6
|
+
#
|
7
|
+
# Notifiers can emit NOTIFY messages through Postgres.
|
8
|
+
# A notifier will LISTEN for messages by creating a background thread that runs in an instance of +Concurrent::ThreadPoolExecutor+.
|
9
|
+
# When a message is received, the notifier passes the message to each of its recipients.
|
6
10
|
#
|
7
11
|
class Notifier
|
12
|
+
# Default Postgres channel for LISTEN/NOTIFY
|
8
13
|
CHANNEL = 'good_job'.freeze
|
14
|
+
# Defaults for instance of Concurrent::ThreadPoolExecutor
|
9
15
|
POOL_OPTIONS = {
|
10
16
|
name: name,
|
11
17
|
min_threads: 0,
|
@@ -15,13 +21,17 @@ module GoodJob # :nodoc:
|
|
15
21
|
max_queue: 1,
|
16
22
|
fallback_policy: :discard,
|
17
23
|
}.freeze
|
24
|
+
# Seconds to block while LISTENing for a message
|
18
25
|
WAIT_INTERVAL = 1
|
19
26
|
|
20
27
|
# @!attribute [r] instances
|
21
28
|
# @!scope class
|
22
|
-
#
|
29
|
+
# List of all instantiated Notifiers in the current process.
|
30
|
+
# @return [array<GoodJob:Adapter>]
|
23
31
|
cattr_reader :instances, default: [], instance_reader: false
|
24
32
|
|
33
|
+
# Send a message via Postgres NOTIFY
|
34
|
+
# @param message [#to_json]
|
25
35
|
def self.notify(message)
|
26
36
|
connection = ActiveRecord::Base.connection
|
27
37
|
connection.exec_query <<~SQL
|
@@ -29,8 +39,11 @@ module GoodJob # :nodoc:
|
|
29
39
|
SQL
|
30
40
|
end
|
31
41
|
|
42
|
+
# List of recipients that will receive notifications.
|
43
|
+
# @return [Array<#call, Array(Object, Symbol)>]
|
32
44
|
attr_reader :recipients
|
33
45
|
|
46
|
+
# @param recipients [Array<#call, Array(Object, Symbol)>]
|
34
47
|
def initialize(*recipients)
|
35
48
|
@recipients = Concurrent::Array.new(recipients)
|
36
49
|
@listening = Concurrent::AtomicBoolean.new(false)
|
@@ -41,16 +54,29 @@ module GoodJob # :nodoc:
|
|
41
54
|
listen
|
42
55
|
end
|
43
56
|
|
57
|
+
# Tests whether the notifier is active and listening for new messages.
|
58
|
+
# @return [true, false, nil]
|
44
59
|
def listening?
|
45
60
|
@listening.true?
|
46
61
|
end
|
47
62
|
|
63
|
+
# Restart the notifier.
|
64
|
+
# When shutdown, start; or shutdown and start.
|
65
|
+
# @param wait [Boolean] Wait for background thread to finish
|
66
|
+
# @return [void]
|
48
67
|
def restart(wait: true)
|
49
68
|
shutdown(wait: wait)
|
50
69
|
create_pool
|
51
70
|
listen
|
52
71
|
end
|
53
72
|
|
73
|
+
# Shut down the notifier.
|
74
|
+
# This stops the background LISTENing thread.
|
75
|
+
# If +wait+ is +true+, the notifier will wait for background thread to shutdown.
|
76
|
+
# If +wait+ is +false+, this method will return immediately even though threads may still be running.
|
77
|
+
# Use {#shutdown?} to determine whether threads have stopped.
|
78
|
+
# @param wait [Boolean] Wait for actively executing jobs to finish
|
79
|
+
# @return [void]
|
54
80
|
def shutdown(wait: true)
|
55
81
|
return unless @pool.running?
|
56
82
|
|
@@ -58,6 +84,8 @@ module GoodJob # :nodoc:
|
|
58
84
|
@pool.wait_for_termination if wait
|
59
85
|
end
|
60
86
|
|
87
|
+
# Tests whether the notifier is shutdown.
|
88
|
+
# @return [true, false, nil]
|
61
89
|
def shutdown?
|
62
90
|
!@pool.running?
|
63
91
|
end
|
@@ -70,38 +98,36 @@ module GoodJob # :nodoc:
|
|
70
98
|
|
71
99
|
def listen
|
72
100
|
future = Concurrent::Future.new(args: [@recipients, @pool, @listening], executor: @pool) do |recipients, pool, listening|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
101
|
+
with_listen_connection do |conn|
|
102
|
+
ActiveSupport::Notifications.instrument("notifier_listen.good_job") do
|
103
|
+
conn.async_exec "LISTEN #{CHANNEL}"
|
104
|
+
end
|
78
105
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
listening.make_false
|
84
|
-
next unless channel == CHANNEL
|
85
|
-
|
86
|
-
ActiveSupport::Notifications.instrument("notifier_notified.good_job", { payload: payload })
|
87
|
-
parsed_payload = JSON.parse(payload, symbolize_names: true)
|
88
|
-
recipients.each do |recipient|
|
89
|
-
target, method_name = recipient.is_a?(Array) ? recipient : [recipient, :call]
|
90
|
-
target.send(method_name, parsed_payload)
|
91
|
-
end
|
92
|
-
end
|
106
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
107
|
+
while pool.running?
|
108
|
+
listening.make_true
|
109
|
+
conn.wait_for_notify(WAIT_INTERVAL) do |channel, _pid, payload|
|
93
110
|
listening.make_false
|
111
|
+
next unless channel == CHANNEL
|
112
|
+
|
113
|
+
ActiveSupport::Notifications.instrument("notifier_notified.good_job", { payload: payload })
|
114
|
+
parsed_payload = JSON.parse(payload, symbolize_names: true)
|
115
|
+
recipients.each do |recipient|
|
116
|
+
target, method_name = recipient.is_a?(Array) ? recipient : [recipient, :call]
|
117
|
+
target.send(method_name, parsed_payload)
|
118
|
+
end
|
94
119
|
end
|
120
|
+
listening.make_false
|
95
121
|
end
|
96
122
|
end
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
123
|
+
end
|
124
|
+
rescue StandardError => e
|
125
|
+
ActiveSupport::Notifications.instrument("notifier_notify_error.good_job", { error: e })
|
126
|
+
raise
|
127
|
+
ensure
|
128
|
+
@listening.make_false
|
129
|
+
ActiveSupport::Notifications.instrument("notifier_unlisten.good_job") do
|
130
|
+
conn.async_exec "UNLISTEN *"
|
105
131
|
end
|
106
132
|
end
|
107
133
|
|
data/lib/good_job/performer.rb
CHANGED
@@ -1,7 +1,33 @@
|
|
1
1
|
module GoodJob
|
2
|
+
#
|
3
|
+
# Performer queries the database for jobs and performs them on behalf of a
|
4
|
+
# {Scheduler}. It mainly functions as glue between a {Scheduler} and the jobs
|
5
|
+
# it should be executing.
|
6
|
+
#
|
7
|
+
# The Performer enforces a callable that does not rely on scoped/closure
|
8
|
+
# variables because they might not be available when executed in a different
|
9
|
+
# thread.
|
10
|
+
#
|
2
11
|
class Performer
|
12
|
+
# @!attribute [r] name
|
13
|
+
# @return [String]
|
14
|
+
# a meaningful name to identify the performer in logs and for debugging.
|
15
|
+
# This is usually set to the list of queues the performer will query,
|
16
|
+
# e.g. +"-transactional_messages,batch_processing"+.
|
3
17
|
attr_reader :name
|
4
18
|
|
19
|
+
# @param target [Object]
|
20
|
+
# An object that can perform jobs. It must respond to +method_name+ by
|
21
|
+
# finding and performing jobs and is usually a {Job} query,
|
22
|
+
# e.g. +GoodJob::Job.where(queue_name: ['queue1', 'queue2'])+.
|
23
|
+
# @param method_name [Symbol]
|
24
|
+
# The name of a method on +target+ that finds and performs jobs.
|
25
|
+
# @param name [String]
|
26
|
+
# A name for the performer to be used in logs and for debugging.
|
27
|
+
# @param filter [#call]
|
28
|
+
# Used to determine whether the performer should be used in GoodJob's
|
29
|
+
# current state. GoodJob state is a +Hash+ that will be passed as the
|
30
|
+
# first argument to +filter+ and includes info like the current queue.
|
5
31
|
def initialize(target, method_name, name: nil, filter: nil)
|
6
32
|
@target = target
|
7
33
|
@method_name = method_name
|
@@ -9,10 +35,22 @@ module GoodJob
|
|
9
35
|
@filter = filter
|
10
36
|
end
|
11
37
|
|
38
|
+
# Find and perform any eligible jobs.
|
12
39
|
def next
|
13
40
|
@target.public_send(@method_name)
|
14
41
|
end
|
15
42
|
|
43
|
+
# Tests whether this performer should be used in GoodJob's current state by
|
44
|
+
# calling the +filter+ callable set in {#initialize}. Always returns +true+
|
45
|
+
# if there is no filter.
|
46
|
+
#
|
47
|
+
# For example, state will be a LISTEN/NOTIFY message that is passed down
|
48
|
+
# from the Notifier to the Scheduler. The Scheduler is able to ask
|
49
|
+
# its performer "does this message relate to you?", and if not, ignore it
|
50
|
+
# to minimize thread wake-ups, database queries, and thundering herds.
|
51
|
+
#
|
52
|
+
# @return [Boolean] whether the performer's {#next} method should be
|
53
|
+
# called in the current state.
|
16
54
|
def next?(state = {})
|
17
55
|
return true unless @filter.respond_to?(:call)
|
18
56
|
|
data/lib/good_job/railtie.rb
CHANGED
data/lib/good_job/scheduler.rb
CHANGED
@@ -4,39 +4,44 @@ require "concurrent/utility/processor_counter"
|
|
4
4
|
|
5
5
|
module GoodJob # :nodoc:
|
6
6
|
#
|
7
|
-
# Schedulers are generic thread
|
8
|
-
# periodically checking for available
|
9
|
-
#
|
7
|
+
# Schedulers are generic thread pools that are responsible for
|
8
|
+
# periodically checking for available tasks, executing tasks within a thread,
|
9
|
+
# and efficiently scaling active threads.
|
10
10
|
#
|
11
|
-
#
|
12
|
-
#
|
11
|
+
# Every scheduler has a single {Performer} that will execute tasks.
|
12
|
+
# The scheduler is responsible for calling its performer efficiently across threads managed by an instance of +Concurrent::ThreadPoolExecutor+.
|
13
|
+
# If a performer does not have work, the thread will go to sleep.
|
14
|
+
# The scheduler maintains an instance of +Concurrent::TimerTask+, which wakes sleeping threads and causes them to check whether the performer has new work.
|
13
15
|
#
|
14
16
|
class Scheduler
|
15
|
-
# Defaults for instance of Concurrent::TimerTask
|
16
|
-
DEFAULT_TIMER_OPTIONS = {
|
17
|
-
execution_interval: 1,
|
18
|
-
timeout_interval: 1,
|
19
|
-
run_now: true,
|
20
|
-
}.freeze
|
21
|
-
|
22
17
|
# Defaults for instance of Concurrent::ThreadPoolExecutor
|
18
|
+
# The thread pool is where work is performed.
|
23
19
|
DEFAULT_POOL_OPTIONS = {
|
24
20
|
name: name,
|
25
21
|
min_threads: 0,
|
26
|
-
max_threads:
|
22
|
+
max_threads: Configuration::DEFAULT_MAX_THREADS,
|
27
23
|
auto_terminate: true,
|
28
24
|
idletime: 60,
|
29
25
|
max_queue: -1,
|
30
26
|
fallback_policy: :discard,
|
31
27
|
}.freeze
|
32
28
|
|
29
|
+
# Defaults for instance of Concurrent::TimerTask.
|
30
|
+
# The timer controls how and when sleeping threads check for new work.
|
31
|
+
DEFAULT_TIMER_OPTIONS = {
|
32
|
+
execution_interval: Configuration::DEFAULT_POLL_INTERVAL,
|
33
|
+
timeout_interval: 1,
|
34
|
+
run_now: true,
|
35
|
+
}.freeze
|
36
|
+
|
33
37
|
# @!attribute [r] instances
|
34
38
|
# @!scope class
|
35
|
-
#
|
39
|
+
# List of all instantiated Schedulers in the current process.
|
36
40
|
# @return [array<GoodJob:Scheduler>]
|
37
41
|
cattr_reader :instances, default: [], instance_reader: false
|
38
42
|
|
39
43
|
# Creates GoodJob::Scheduler(s) and Performers from a GoodJob::Configuration instance.
|
44
|
+
# TODO: move this to GoodJob::Configuration
|
40
45
|
# @param configuration [GoodJob::Configuration]
|
41
46
|
# @return [GoodJob::Scheduler, GoodJob::MultiScheduler]
|
42
47
|
def self.from_configuration(configuration)
|
@@ -57,14 +62,7 @@ module GoodJob # :nodoc:
|
|
57
62
|
end
|
58
63
|
job_performer = GoodJob::Performer.new(job_query, :perform_with_advisory_lock, name: queue_string, filter: job_filter)
|
59
64
|
|
60
|
-
|
61
|
-
timer_options[:execution_interval] = configuration.poll_interval
|
62
|
-
|
63
|
-
pool_options = {
|
64
|
-
max_threads: max_threads,
|
65
|
-
}
|
66
|
-
|
67
|
-
GoodJob::Scheduler.new(job_performer, timer_options: timer_options, pool_options: pool_options)
|
65
|
+
GoodJob::Scheduler.new(job_performer, max_threads: max_threads, poll_interval: configuration.poll_interval)
|
68
66
|
end
|
69
67
|
|
70
68
|
if schedulers.size > 1
|
@@ -75,23 +73,30 @@ module GoodJob # :nodoc:
|
|
75
73
|
end
|
76
74
|
|
77
75
|
# @param performer [GoodJob::Performer]
|
78
|
-
# @param
|
79
|
-
# @param
|
80
|
-
def initialize(performer,
|
76
|
+
# @param max_threads [Numeric, nil] the number of execution threads to use
|
77
|
+
# @param poll_interval [Numeric, nil] the number of seconds between polls for jobs
|
78
|
+
def initialize(performer, max_threads: nil, poll_interval: nil)
|
81
79
|
raise ArgumentError, "Performer argument must implement #next" unless performer.respond_to?(:next)
|
82
80
|
|
83
81
|
self.class.instances << self
|
84
82
|
|
85
83
|
@performer = performer
|
86
|
-
@pool_options = DEFAULT_POOL_OPTIONS.merge(pool_options)
|
87
|
-
@timer_options = DEFAULT_TIMER_OPTIONS.merge(timer_options)
|
88
84
|
|
85
|
+
@timer_options = DEFAULT_TIMER_OPTIONS.dup
|
86
|
+
@timer_options[:execution_interval] = poll_interval if poll_interval.present?
|
87
|
+
|
88
|
+
@pool_options = DEFAULT_POOL_OPTIONS.dup
|
89
|
+
@pool_options[:max_threads] = max_threads if max_threads.present?
|
89
90
|
@pool_options[:name] = "GoodJob::Scheduler(queues=#{@performer.name} max_threads=#{@pool_options[:max_threads]} poll_interval=#{@timer_options[:execution_interval]})"
|
90
91
|
|
91
92
|
create_pools
|
92
93
|
end
|
93
94
|
|
94
|
-
# Shut down the
|
95
|
+
# Shut down the scheduler.
|
96
|
+
# This stops all threads in the pool.
|
97
|
+
# If +wait+ is +true+, the scheduler will wait for any active tasks to finish.
|
98
|
+
# If +wait+ is +false+, this method will return immediately even though threads may still be running.
|
99
|
+
# Use {#shutdown?} to determine whether threads have stopped.
|
95
100
|
# @param wait [Boolean] Wait for actively executing jobs to finish
|
96
101
|
# @return [void]
|
97
102
|
def shutdown(wait: true)
|
@@ -102,22 +107,25 @@ module GoodJob # :nodoc:
|
|
102
107
|
if @timer&.running?
|
103
108
|
@timer.shutdown
|
104
109
|
@timer.wait_for_termination if wait
|
110
|
+
# TODO: Should be killed if wait is not true
|
105
111
|
end
|
106
112
|
|
107
113
|
if @pool&.running?
|
108
114
|
@pool.shutdown
|
109
115
|
@pool.wait_for_termination if wait
|
116
|
+
# TODO: Should be killed if wait is not true
|
110
117
|
end
|
111
118
|
end
|
112
119
|
end
|
113
120
|
|
114
|
-
#
|
121
|
+
# Tests whether the scheduler is shutdown.
|
115
122
|
# @return [true, false, nil]
|
116
123
|
def shutdown?
|
117
124
|
@_shutdown
|
118
125
|
end
|
119
126
|
|
120
|
-
# Restart the Scheduler.
|
127
|
+
# Restart the Scheduler.
|
128
|
+
# When shutdown, start; or shutdown and start.
|
121
129
|
# @param wait [Boolean] Wait for actively executing jobs to finish
|
122
130
|
# @return [void]
|
123
131
|
def restart(wait: true)
|
@@ -128,15 +136,15 @@ module GoodJob # :nodoc:
|
|
128
136
|
end
|
129
137
|
end
|
130
138
|
|
131
|
-
#
|
132
|
-
# @param state [nil, Object]
|
133
|
-
# @return [nil, Boolean]
|
139
|
+
# Wakes a thread to allow the performer to execute a task.
|
140
|
+
# @param state [nil, Object] Contextual information for the performer. See {Performer#next?}.
|
141
|
+
# @return [nil, Boolean] Whether work was started.
|
142
|
+
# Returns +nil+ if the scheduler is unable to take new work, for example if the thread pool is shut down or at capacity.
|
143
|
+
# Returns +true+ if the performer started executing work.
|
144
|
+
# Returns +false+ if the performer decides not to attempt to execute a task based on the +state+ that is passed to it.
|
134
145
|
def create_thread(state = nil)
|
135
146
|
return nil unless @pool.running? && @pool.ready_worker_count.positive?
|
136
|
-
|
137
|
-
if state
|
138
|
-
return false unless @performer.next?(state)
|
139
|
-
end
|
147
|
+
return false if state && !@performer.next?(state)
|
140
148
|
|
141
149
|
future = Concurrent::Future.new(args: [@performer], executor: @pool) do |performer|
|
142
150
|
output = nil
|
@@ -168,7 +176,6 @@ module GoodJob # :nodoc:
|
|
168
176
|
|
169
177
|
private
|
170
178
|
|
171
|
-
# @return [void]
|
172
179
|
def create_pools
|
173
180
|
instrument("scheduler_create_pools", { performer_name: @performer.name, max_threads: @pool_options[:max_threads], poll_interval: @timer_options[:execution_interval] }) do
|
174
181
|
@pool = ThreadPoolExecutor.new(@pool_options)
|
@@ -191,9 +198,10 @@ module GoodJob # :nodoc:
|
|
191
198
|
end
|
192
199
|
end
|
193
200
|
|
194
|
-
#
|
201
|
+
# Custom sub-class of +Concurrent::ThreadPoolExecutor+ to add additional worker status.
|
202
|
+
# @private
|
195
203
|
class ThreadPoolExecutor < Concurrent::ThreadPoolExecutor
|
196
|
-
# Number of
|
204
|
+
# Number of inactive threads available to execute tasks.
|
197
205
|
# https://github.com/ruby-concurrency/concurrent-ruby/issues/684#issuecomment-427594437
|
198
206
|
# @return [Integer]
|
199
207
|
def ready_worker_count
|
data/lib/good_job/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: good_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Sheldon
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activejob
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 5.2.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 5.2.0
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: concurrent-ruby
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -39,19 +67,19 @@ dependencies:
|
|
39
67
|
- !ruby/object:Gem::Version
|
40
68
|
version: 1.0.0
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
70
|
+
name: railties
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
44
72
|
requirements:
|
45
73
|
- - ">="
|
46
74
|
- !ruby/object:Gem::Version
|
47
|
-
version: 5.
|
75
|
+
version: 5.2.0
|
48
76
|
type: :runtime
|
49
77
|
prerelease: false
|
50
78
|
version_requirements: !ruby/object:Gem::Requirement
|
51
79
|
requirements:
|
52
80
|
- - ">="
|
53
81
|
- !ruby/object:Gem::Version
|
54
|
-
version: 5.
|
82
|
+
version: 5.2.0
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: thor
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +94,20 @@ dependencies:
|
|
66
94
|
- - ">="
|
67
95
|
- !ruby/object:Gem::Version
|
68
96
|
version: 0.14.1
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: zeitwerk
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '2.0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '2.0'
|
69
111
|
- !ruby/object:Gem::Dependency
|
70
112
|
name: appraisal
|
71
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +122,20 @@ dependencies:
|
|
80
122
|
- - ">="
|
81
123
|
- !ruby/object:Gem::Version
|
82
124
|
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: capybara
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
83
139
|
- !ruby/object:Gem::Dependency
|
84
140
|
name: database_cleaner
|
85
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,6 +164,20 @@ dependencies:
|
|
108
164
|
- - ">="
|
109
165
|
- !ruby/object:Gem::Version
|
110
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: erb_lint
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
111
181
|
- !ruby/object:Gem::Dependency
|
112
182
|
name: foreman
|
113
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,6 +220,48 @@ dependencies:
|
|
150
220
|
- - ">="
|
151
221
|
- !ruby/object:Gem::Version
|
152
222
|
version: '0'
|
223
|
+
- !ruby/object:Gem::Dependency
|
224
|
+
name: kramdown
|
225
|
+
requirement: !ruby/object:Gem::Requirement
|
226
|
+
requirements:
|
227
|
+
- - ">="
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '0'
|
230
|
+
type: :development
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
requirements:
|
234
|
+
- - ">="
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: '0'
|
237
|
+
- !ruby/object:Gem::Dependency
|
238
|
+
name: kramdown-parser-gfm
|
239
|
+
requirement: !ruby/object:Gem::Requirement
|
240
|
+
requirements:
|
241
|
+
- - ">="
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: '0'
|
244
|
+
type: :development
|
245
|
+
prerelease: false
|
246
|
+
version_requirements: !ruby/object:Gem::Requirement
|
247
|
+
requirements:
|
248
|
+
- - ">="
|
249
|
+
- !ruby/object:Gem::Version
|
250
|
+
version: '0'
|
251
|
+
- !ruby/object:Gem::Dependency
|
252
|
+
name: mdl
|
253
|
+
requirement: !ruby/object:Gem::Requirement
|
254
|
+
requirements:
|
255
|
+
- - ">="
|
256
|
+
- !ruby/object:Gem::Version
|
257
|
+
version: '0'
|
258
|
+
type: :development
|
259
|
+
prerelease: false
|
260
|
+
version_requirements: !ruby/object:Gem::Requirement
|
261
|
+
requirements:
|
262
|
+
- - ">="
|
263
|
+
- !ruby/object:Gem::Version
|
264
|
+
version: '0'
|
153
265
|
- !ruby/object:Gem::Dependency
|
154
266
|
name: pry-rails
|
155
267
|
requirement: !ruby/object:Gem::Requirement
|
@@ -262,6 +374,20 @@ dependencies:
|
|
262
374
|
- - ">="
|
263
375
|
- !ruby/object:Gem::Version
|
264
376
|
version: '0'
|
377
|
+
- !ruby/object:Gem::Dependency
|
378
|
+
name: selenium-webdriver
|
379
|
+
requirement: !ruby/object:Gem::Requirement
|
380
|
+
requirements:
|
381
|
+
- - ">="
|
382
|
+
- !ruby/object:Gem::Version
|
383
|
+
version: '0'
|
384
|
+
type: :development
|
385
|
+
prerelease: false
|
386
|
+
version_requirements: !ruby/object:Gem::Requirement
|
387
|
+
requirements:
|
388
|
+
- - ">="
|
389
|
+
- !ruby/object:Gem::Version
|
390
|
+
version: '0'
|
265
391
|
- !ruby/object:Gem::Dependency
|
266
392
|
name: sigdump
|
267
393
|
requirement: !ruby/object:Gem::Requirement
|
@@ -290,6 +416,20 @@ dependencies:
|
|
290
416
|
- - ">="
|
291
417
|
- !ruby/object:Gem::Version
|
292
418
|
version: '0'
|
419
|
+
- !ruby/object:Gem::Dependency
|
420
|
+
name: yard-activesupport-concern
|
421
|
+
requirement: !ruby/object:Gem::Requirement
|
422
|
+
requirements:
|
423
|
+
- - ">="
|
424
|
+
- !ruby/object:Gem::Version
|
425
|
+
version: '0'
|
426
|
+
type: :development
|
427
|
+
prerelease: false
|
428
|
+
version_requirements: !ruby/object:Gem::Requirement
|
429
|
+
requirements:
|
430
|
+
- - ">="
|
431
|
+
- !ruby/object:Gem::Version
|
432
|
+
version: '0'
|
293
433
|
description: A multithreaded, Postgres-based ActiveJob backend for Ruby on Rails
|
294
434
|
email:
|
295
435
|
- bensheldon@gmail.com
|
@@ -304,6 +444,22 @@ files:
|
|
304
444
|
- CHANGELOG.md
|
305
445
|
- LICENSE.txt
|
306
446
|
- README.md
|
447
|
+
- engine/app/controllers/good_job/active_jobs_controller.rb
|
448
|
+
- engine/app/controllers/good_job/base_controller.rb
|
449
|
+
- engine/app/controllers/good_job/dashboards_controller.rb
|
450
|
+
- engine/app/helpers/good_job/application_helper.rb
|
451
|
+
- engine/app/views/assets/_style.css.erb
|
452
|
+
- engine/app/views/good_job/active_jobs/show.html.erb
|
453
|
+
- engine/app/views/good_job/dashboards/index.html.erb
|
454
|
+
- engine/app/views/layouts/good_job/base.html.erb
|
455
|
+
- engine/app/views/shared/_chart.erb
|
456
|
+
- engine/app/views/shared/_jobs_table.erb
|
457
|
+
- engine/app/views/vendor/bootstrap/_bootstrap-native.js.erb
|
458
|
+
- engine/app/views/vendor/bootstrap/_bootstrap.css.erb
|
459
|
+
- engine/app/views/vendor/chartist/_chartist.css.erb
|
460
|
+
- engine/app/views/vendor/chartist/_chartist.js.erb
|
461
|
+
- engine/config/routes.rb
|
462
|
+
- engine/lib/good_job/engine.rb
|
307
463
|
- exe/good_job
|
308
464
|
- lib/active_job/queue_adapters/good_job_adapter.rb
|
309
465
|
- lib/generators/good_job/install_generator.rb
|
@@ -319,7 +475,6 @@ files:
|
|
319
475
|
- lib/good_job/multi_scheduler.rb
|
320
476
|
- lib/good_job/notifier.rb
|
321
477
|
- lib/good_job/performer.rb
|
322
|
-
- lib/good_job/pg_locks.rb
|
323
478
|
- lib/good_job/railtie.rb
|
324
479
|
- lib/good_job/scheduler.rb
|
325
480
|
- lib/good_job/version.rb
|
@@ -343,11 +498,12 @@ rdoc_options:
|
|
343
498
|
- "--quiet"
|
344
499
|
require_paths:
|
345
500
|
- lib
|
501
|
+
- engine/lib
|
346
502
|
required_ruby_version: !ruby/object:Gem::Requirement
|
347
503
|
requirements:
|
348
504
|
- - ">="
|
349
505
|
- !ruby/object:Gem::Version
|
350
|
-
version: 2.
|
506
|
+
version: 2.5.0
|
351
507
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
352
508
|
requirements:
|
353
509
|
- - ">="
|