solid_queue 0.2.1 → 0.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/README.md +56 -7
- data/app/models/solid_queue/blocked_execution.rb +3 -3
- data/app/models/solid_queue/claimed_execution.rb +0 -1
- data/app/models/solid_queue/execution/dispatching.rb +1 -1
- data/app/models/solid_queue/job/clearable.rb +3 -3
- data/app/models/solid_queue/job/executable.rb +2 -6
- data/app/models/solid_queue/job/recurrable.rb +13 -0
- data/app/models/solid_queue/job.rb +1 -1
- data/app/models/solid_queue/recurring_execution.rb +26 -0
- data/app/models/solid_queue/scheduled_execution.rb +1 -1
- data/app/models/solid_queue/semaphore.rb +5 -22
- data/db/migrate/20240218110712_create_recurring_executions.rb +14 -0
- data/lib/solid_queue/configuration.rb +14 -5
- data/lib/solid_queue/dispatcher/concurrency_maintenance.rb +44 -0
- data/lib/solid_queue/dispatcher/recurring_schedule.rb +56 -0
- data/lib/solid_queue/dispatcher/recurring_task.rb +85 -0
- data/lib/solid_queue/dispatcher.rb +21 -36
- data/lib/solid_queue/processes/base.rb +1 -18
- data/lib/solid_queue/processes/callbacks.rb +19 -0
- data/lib/solid_queue/processes/poller.rb +28 -0
- data/lib/solid_queue/processes/registrable.rb +5 -6
- data/lib/solid_queue/processes/runnable.rb +31 -46
- data/lib/solid_queue/processes/supervised.rb +4 -0
- data/lib/solid_queue/recurring_tasks/manager.rb +31 -0
- data/lib/solid_queue/recurring_tasks/schedule.rb +58 -0
- data/lib/solid_queue/recurring_tasks/task.rb +87 -0
- data/lib/solid_queue/supervisor.rb +1 -1
- data/lib/solid_queue/version.rb +1 -1
- data/lib/solid_queue/worker.rb +11 -12
- data/lib/solid_queue.rb +22 -24
- metadata +116 -10
- data/lib/active_job/uniqueness.rb +0 -41
- data/lib/solid_queue/dispatcher/scheduled_executions_dispatcher.rb +0 -6
@@ -2,72 +2,57 @@
|
|
2
2
|
|
3
3
|
module SolidQueue
|
4
4
|
class Dispatcher < Processes::Base
|
5
|
-
include Processes::
|
5
|
+
include Processes::Poller
|
6
6
|
|
7
|
-
attr_accessor :batch_size, :
|
7
|
+
attr_accessor :batch_size, :concurrency_maintenance, :recurring_schedule
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
after_boot :start_concurrency_maintenance, :load_recurring_schedule
|
10
|
+
before_shutdown :stop_concurrency_maintenance, :unload_recurring_schedule
|
11
11
|
|
12
12
|
def initialize(**options)
|
13
13
|
options = options.dup.with_defaults(SolidQueue::Configuration::DISPATCHER_DEFAULTS)
|
14
14
|
|
15
15
|
@batch_size = options[:batch_size]
|
16
16
|
@polling_interval = options[:polling_interval]
|
17
|
-
|
17
|
+
|
18
|
+
@concurrency_maintenance = ConcurrencyMaintenance.new(options[:concurrency_maintenance_interval], options[:batch_size]) if options[:concurrency_maintenance]
|
19
|
+
@recurring_schedule = RecurringSchedule.new(options[:recurring_tasks])
|
18
20
|
end
|
19
21
|
|
20
22
|
private
|
21
|
-
def
|
23
|
+
def poll
|
22
24
|
batch = dispatch_next_batch
|
23
|
-
|
24
|
-
unless batch.size > 0
|
25
|
-
procline "waiting"
|
26
|
-
interruptible_sleep(polling_interval)
|
27
|
-
end
|
25
|
+
batch.size
|
28
26
|
end
|
29
27
|
|
30
28
|
def dispatch_next_batch
|
31
29
|
with_polling_volume do
|
32
|
-
|
30
|
+
ScheduledExecution.dispatch_next_batch(batch_size)
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
36
|
-
def
|
37
|
-
|
38
|
-
expire_semaphores
|
39
|
-
unblock_blocked_executions
|
40
|
-
end
|
41
|
-
|
42
|
-
@concurrency_maintenance_task.add_observer do |_, _, error|
|
43
|
-
handle_thread_error(error) if error
|
44
|
-
end
|
45
|
-
|
46
|
-
@concurrency_maintenance_task.execute
|
34
|
+
def start_concurrency_maintenance
|
35
|
+
concurrency_maintenance&.start
|
47
36
|
end
|
48
37
|
|
49
|
-
def
|
50
|
-
|
38
|
+
def load_recurring_schedule
|
39
|
+
recurring_schedule.load_tasks
|
51
40
|
end
|
52
41
|
|
53
|
-
def
|
54
|
-
|
55
|
-
Semaphore.expired.in_batches(of: batch_size, &:delete_all)
|
56
|
-
end
|
42
|
+
def stop_concurrency_maintenance
|
43
|
+
concurrency_maintenance&.stop
|
57
44
|
end
|
58
45
|
|
59
|
-
def
|
60
|
-
|
61
|
-
BlockedExecution.unblock(batch_size)
|
62
|
-
end
|
46
|
+
def unload_recurring_schedule
|
47
|
+
recurring_schedule.unload_tasks
|
63
48
|
end
|
64
49
|
|
65
|
-
def
|
66
|
-
|
50
|
+
def set_procline
|
51
|
+
procline "waiting"
|
67
52
|
end
|
68
53
|
|
69
54
|
def metadata
|
70
|
-
super.merge(batch_size: batch_size)
|
55
|
+
super.merge(batch_size: batch_size, concurrency_maintenance_interval: concurrency_maintenance&.interval, recurring_schedule: recurring_schedule.tasks.presence )
|
71
56
|
end
|
72
57
|
end
|
73
58
|
end
|
@@ -3,25 +3,8 @@
|
|
3
3
|
module SolidQueue
|
4
4
|
module Processes
|
5
5
|
class Base
|
6
|
-
include
|
7
|
-
define_callbacks :boot, :shutdown
|
8
|
-
|
6
|
+
include Callbacks # Defines callbacks needed by other concerns
|
9
7
|
include AppExecutor, Registrable, Interruptible, Procline
|
10
|
-
|
11
|
-
private
|
12
|
-
def observe_initial_delay
|
13
|
-
interruptible_sleep(initial_jitter)
|
14
|
-
end
|
15
|
-
|
16
|
-
def boot
|
17
|
-
end
|
18
|
-
|
19
|
-
def shutdown
|
20
|
-
end
|
21
|
-
|
22
|
-
def initial_jitter
|
23
|
-
0
|
24
|
-
end
|
25
8
|
end
|
26
9
|
end
|
27
10
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidQueue::Processes
|
4
|
+
module Callbacks
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
extend ActiveModel::Callbacks
|
9
|
+
define_model_callbacks :boot, :shutdown
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def boot
|
14
|
+
end
|
15
|
+
|
16
|
+
def shutdown
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -4,11 +4,39 @@ module SolidQueue::Processes
|
|
4
4
|
module Poller
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
|
+
include Runnable
|
8
|
+
|
7
9
|
included do
|
8
10
|
attr_accessor :polling_interval
|
9
11
|
end
|
10
12
|
|
11
13
|
private
|
14
|
+
def run
|
15
|
+
if mode.async?
|
16
|
+
@thread = Thread.new { start_loop }
|
17
|
+
else
|
18
|
+
start_loop
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def start_loop
|
23
|
+
loop do
|
24
|
+
break if shutting_down?
|
25
|
+
|
26
|
+
wrap_in_app_executor do
|
27
|
+
unless poll > 0
|
28
|
+
interruptible_sleep(polling_interval)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
ensure
|
33
|
+
run_callbacks(:shutdown) { shutdown }
|
34
|
+
end
|
35
|
+
|
36
|
+
def poll
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
12
40
|
def with_polling_volume
|
13
41
|
if SolidQueue.silence_polling?
|
14
42
|
ActiveRecord::Base.logger.silence { yield }
|
@@ -5,11 +5,10 @@ module SolidQueue::Processes
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
-
|
9
|
-
set_callback :boot, :after, :launch_heartbeat
|
8
|
+
after_boot :register, :launch_heartbeat
|
10
9
|
|
11
|
-
|
12
|
-
|
10
|
+
before_shutdown :stop_heartbeat
|
11
|
+
after_shutdown :deregister
|
13
12
|
end
|
14
13
|
|
15
14
|
def inspect
|
@@ -26,7 +25,7 @@ module SolidQueue::Processes
|
|
26
25
|
pid: process_pid,
|
27
26
|
hostname: hostname,
|
28
27
|
supervisor: try(:supervisor),
|
29
|
-
metadata: metadata
|
28
|
+
metadata: metadata.compact
|
30
29
|
end
|
31
30
|
|
32
31
|
def deregister
|
@@ -55,7 +54,7 @@ module SolidQueue::Processes
|
|
55
54
|
end
|
56
55
|
|
57
56
|
def hostname
|
58
|
-
@hostname ||= Socket.gethostname
|
57
|
+
@hostname ||= Socket.gethostname.force_encoding(Encoding::UTF_8)
|
59
58
|
end
|
60
59
|
|
61
60
|
def process_pid
|
@@ -8,11 +8,9 @@ module SolidQueue::Processes
|
|
8
8
|
|
9
9
|
def start
|
10
10
|
@stopping = false
|
11
|
-
|
12
|
-
observe_initial_delay
|
13
11
|
run_callbacks(:boot) { boot }
|
14
12
|
|
15
|
-
|
13
|
+
run
|
16
14
|
end
|
17
15
|
|
18
16
|
def stop
|
@@ -20,60 +18,47 @@ module SolidQueue::Processes
|
|
20
18
|
@thread&.join
|
21
19
|
end
|
22
20
|
|
23
|
-
|
24
|
-
|
21
|
+
private
|
22
|
+
DEFAULT_MODE = :async
|
25
23
|
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
def mode
|
25
|
+
(@mode || DEFAULT_MODE).to_s.inquiry
|
26
|
+
end
|
29
27
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
28
|
+
def boot
|
29
|
+
if supervised?
|
30
|
+
register_signal_handlers
|
31
|
+
set_procline
|
32
|
+
end
|
34
33
|
|
35
|
-
|
36
|
-
if mode.async?
|
37
|
-
@thread = Thread.new { do_start_loop }
|
38
|
-
else
|
39
|
-
do_start_loop
|
34
|
+
SolidQueue.logger.info("[SolidQueue] Starting #{self}")
|
40
35
|
end
|
41
|
-
end
|
42
36
|
|
43
|
-
|
44
|
-
|
45
|
-
break if shutting_down?
|
46
|
-
|
47
|
-
wrap_in_app_executor do
|
48
|
-
run
|
49
|
-
end
|
37
|
+
def shutting_down?
|
38
|
+
stopping? || supervisor_went_away? || finished?
|
50
39
|
end
|
51
|
-
ensure
|
52
|
-
run_callbacks(:shutdown) { shutdown }
|
53
|
-
end
|
54
40
|
|
55
|
-
|
56
|
-
|
57
|
-
|
41
|
+
def run
|
42
|
+
raise NotImplementedError
|
43
|
+
end
|
58
44
|
|
59
|
-
|
60
|
-
|
61
|
-
|
45
|
+
def stopping?
|
46
|
+
@stopping
|
47
|
+
end
|
62
48
|
|
63
|
-
|
64
|
-
|
65
|
-
|
49
|
+
def finished?
|
50
|
+
running_inline? && all_work_completed?
|
51
|
+
end
|
66
52
|
|
67
|
-
|
68
|
-
|
69
|
-
|
53
|
+
def all_work_completed?
|
54
|
+
false
|
55
|
+
end
|
70
56
|
|
71
|
-
|
72
|
-
|
73
|
-
end
|
57
|
+
def set_procline
|
58
|
+
end
|
74
59
|
|
75
|
-
|
76
|
-
|
77
|
-
|
60
|
+
def running_inline?
|
61
|
+
mode.inline?
|
62
|
+
end
|
78
63
|
end
|
79
64
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidQueue
|
4
|
+
module RecurringTasks
|
5
|
+
class Manager < Processes::Base
|
6
|
+
include Processes::Runnable
|
7
|
+
|
8
|
+
attr_accessor :schedule
|
9
|
+
|
10
|
+
after_boot :load_schedule
|
11
|
+
before_shutdown :unload_schedule
|
12
|
+
|
13
|
+
def initialize(tasks)
|
14
|
+
@schedule = Schedule.new(tasks)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def load_schedule
|
19
|
+
schedule.load_tasks
|
20
|
+
end
|
21
|
+
|
22
|
+
def unload_schedule
|
23
|
+
schedule.unload_tasks
|
24
|
+
end
|
25
|
+
|
26
|
+
def metadata
|
27
|
+
super.merge(schedule: schedule.tasks)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidQueue
|
4
|
+
module RecurringTasks
|
5
|
+
class Schedule
|
6
|
+
include AppExecutor
|
7
|
+
|
8
|
+
attr_reader :configured_tasks, :scheduled_tasks
|
9
|
+
|
10
|
+
def initialize(tasks)
|
11
|
+
@configured_tasks = Array(tasks).map { |task| Task.wrap(task) }
|
12
|
+
@scheduled_tasks = Concurrent::Hash.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def load_tasks
|
16
|
+
configured_tasks.each do |task|
|
17
|
+
load_task(task)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def load_task(task)
|
22
|
+
scheduled_tasks[task.key] = schedule(task)
|
23
|
+
end
|
24
|
+
|
25
|
+
def unload_tasks
|
26
|
+
scheduled_tasks.values.each(&:cancel)
|
27
|
+
scheduled_tasks.clear
|
28
|
+
end
|
29
|
+
|
30
|
+
def tasks
|
31
|
+
configured_tasks.each_with_object({}) { |task, hsh| hsh[task.key] = task.to_h }
|
32
|
+
end
|
33
|
+
|
34
|
+
def inspect
|
35
|
+
configured_tasks.map(&:to_s).join(" | ")
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def schedule(task)
|
40
|
+
scheduled_task = Concurrent::ScheduledTask.new(task.delay_from_now, args: [ self, task, task.next_time ]) do |thread_schedule, thread_task, thread_task_run_at|
|
41
|
+
thread_schedule.load_task(thread_task)
|
42
|
+
|
43
|
+
wrap_in_app_executor do
|
44
|
+
thread_task.enqueue(at: thread_task_run_at)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
scheduled_task.add_observer do |_, _, error|
|
49
|
+
# Don't notify on task cancellation before execution, as this will happen normally
|
50
|
+
# as part of unloading tasks
|
51
|
+
handle_thread_error(error) if error && !error.is_a?(Concurrent::CancelledOperationError)
|
52
|
+
end
|
53
|
+
|
54
|
+
scheduled_task.tap(&:execute)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "fugit"
|
2
|
+
|
3
|
+
module SolidQueue
|
4
|
+
module RecurringTasks
|
5
|
+
class Task
|
6
|
+
class << self
|
7
|
+
def wrap(args)
|
8
|
+
args.is_a?(self) ? args : from_configuration(args.first, **args.second)
|
9
|
+
end
|
10
|
+
|
11
|
+
def from_configuration(key, **options)
|
12
|
+
new(key, class_name: options[:class], schedule: options[:schedule], arguments: options[:args])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :key, :schedule, :class_name, :arguments
|
17
|
+
|
18
|
+
def initialize(key, class_name:, schedule:, arguments: nil)
|
19
|
+
@key = key
|
20
|
+
@class_name = class_name
|
21
|
+
@schedule = schedule
|
22
|
+
@arguments = Array(arguments)
|
23
|
+
end
|
24
|
+
|
25
|
+
def delay_from_now
|
26
|
+
[ (next_time - Time.current).to_f, 0 ].max
|
27
|
+
end
|
28
|
+
|
29
|
+
def next_time
|
30
|
+
parsed_schedule.next_time.utc
|
31
|
+
end
|
32
|
+
|
33
|
+
def enqueue(at:)
|
34
|
+
if using_solid_queue_adapter?
|
35
|
+
perform_later_and_record(run_at: at)
|
36
|
+
else
|
37
|
+
perform_later
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def valid?
|
42
|
+
parsed_schedule.instance_of?(Fugit::Cron)
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
"#{class_name}.perform_later(#{arguments.map(&:inspect).join(",")}) [ #{parsed_schedule.original.to_s} ]"
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_h
|
50
|
+
{
|
51
|
+
schedule: schedule,
|
52
|
+
class_name: class_name,
|
53
|
+
arguments: arguments
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def using_solid_queue_adapter?
|
59
|
+
job_class.queue_adapter_name.inquiry.solid_queue?
|
60
|
+
end
|
61
|
+
|
62
|
+
def perform_later_and_record(run_at:)
|
63
|
+
RecurringExecution.record(key, run_at) { perform_later.provider_job_id }
|
64
|
+
end
|
65
|
+
|
66
|
+
def perform_later
|
67
|
+
job_class.perform_later(*arguments_with_kwargs)
|
68
|
+
end
|
69
|
+
|
70
|
+
def arguments_with_kwargs
|
71
|
+
if arguments.last.is_a?(Hash)
|
72
|
+
arguments[0...-1] + [ Hash.ruby2_keywords_hash(arguments.last) ]
|
73
|
+
else
|
74
|
+
arguments
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def parsed_schedule
|
79
|
+
@parsed_schedule ||= Fugit.parse(schedule)
|
80
|
+
end
|
81
|
+
|
82
|
+
def job_class
|
83
|
+
@job_class ||= class_name.safe_constantize
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/solid_queue/version.rb
CHANGED
data/lib/solid_queue/worker.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module SolidQueue
|
4
4
|
class Worker < Processes::Base
|
5
|
-
include Processes::
|
5
|
+
include Processes::Poller
|
6
6
|
|
7
7
|
attr_accessor :queues, :pool
|
8
8
|
|
@@ -15,22 +15,17 @@ module SolidQueue
|
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
if polled_executions.size > 0
|
22
|
-
procline "performing #{polled_executions.count} jobs"
|
23
|
-
|
24
|
-
polled_executions.each do |execution|
|
18
|
+
def poll
|
19
|
+
claim_executions.then do |executions|
|
20
|
+
executions.each do |execution|
|
25
21
|
pool.post(execution)
|
26
22
|
end
|
27
|
-
|
28
|
-
|
29
|
-
interruptible_sleep(polling_interval)
|
23
|
+
|
24
|
+
executions.size
|
30
25
|
end
|
31
26
|
end
|
32
27
|
|
33
|
-
def
|
28
|
+
def claim_executions
|
34
29
|
with_polling_volume do
|
35
30
|
SolidQueue::ReadyExecution.claim(queues, pool.idle_threads, process.id)
|
36
31
|
end
|
@@ -47,6 +42,10 @@ module SolidQueue
|
|
47
42
|
SolidQueue::ReadyExecution.aggregated_count_across(queues).zero?
|
48
43
|
end
|
49
44
|
|
45
|
+
def set_procline
|
46
|
+
procline "waiting for jobs in #{queues.join(",")}"
|
47
|
+
end
|
48
|
+
|
50
49
|
def metadata
|
51
50
|
super.merge(queues: queues.join(","), thread_pool_size: pool.size)
|
52
51
|
end
|
data/lib/solid_queue.rb
CHANGED
@@ -3,24 +3,16 @@
|
|
3
3
|
require "solid_queue/version"
|
4
4
|
require "solid_queue/engine"
|
5
5
|
|
6
|
-
require "active_job
|
7
|
-
require "active_job/
|
8
|
-
|
9
|
-
require "
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
require "solid_queue/processes/base"
|
17
|
-
require "solid_queue/processes/runnable"
|
18
|
-
require "solid_queue/processes/signals"
|
19
|
-
require "solid_queue/configuration"
|
20
|
-
require "solid_queue/pool"
|
21
|
-
require "solid_queue/worker"
|
22
|
-
require "solid_queue/dispatcher"
|
23
|
-
require "solid_queue/supervisor"
|
6
|
+
require "active_job"
|
7
|
+
require "active_job/queue_adapters"
|
8
|
+
|
9
|
+
require "zeitwerk"
|
10
|
+
|
11
|
+
loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
|
12
|
+
loader.ignore("#{__dir__}/solid_queue/tasks.rb")
|
13
|
+
loader.ignore("#{__dir__}/generators")
|
14
|
+
loader.ignore("#{__dir__}/puma")
|
15
|
+
loader.setup
|
24
16
|
|
25
17
|
module SolidQueue
|
26
18
|
mattr_accessor :logger, default: ActiveSupport::Logger.new($stdout)
|
@@ -33,7 +25,7 @@ module SolidQueue
|
|
33
25
|
|
34
26
|
mattr_accessor :shutdown_timeout, default: 5.seconds
|
35
27
|
|
36
|
-
mattr_accessor :silence_polling, default:
|
28
|
+
mattr_accessor :silence_polling, default: true
|
37
29
|
|
38
30
|
mattr_accessor :supervisor_pidfile
|
39
31
|
mattr_accessor :supervisor, default: false
|
@@ -42,11 +34,17 @@ module SolidQueue
|
|
42
34
|
mattr_accessor :clear_finished_jobs_after, default: 1.day
|
43
35
|
mattr_accessor :default_concurrency_control_period, default: 3.minutes
|
44
36
|
|
45
|
-
|
46
|
-
supervisor
|
47
|
-
|
37
|
+
class << self
|
38
|
+
def supervisor?
|
39
|
+
supervisor
|
40
|
+
end
|
41
|
+
|
42
|
+
def silence_polling?
|
43
|
+
silence_polling
|
44
|
+
end
|
48
45
|
|
49
|
-
|
50
|
-
|
46
|
+
def preserve_finished_jobs?
|
47
|
+
preserve_finished_jobs
|
48
|
+
end
|
51
49
|
end
|
52
50
|
end
|