exekutor 0.1.0 → 0.1.2
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
- checksums.yaml.gz.sig +2 -3
- data/exe/exekutor +2 -2
- data/lib/active_job/queue_adapters/exekutor_adapter.rb +2 -1
- data/lib/exekutor/asynchronous.rb +143 -75
- data/lib/exekutor/cleanup.rb +27 -28
- data/lib/exekutor/configuration.rb +102 -48
- data/lib/exekutor/hook.rb +15 -11
- data/lib/exekutor/info/worker.rb +3 -3
- data/lib/exekutor/internal/base_record.rb +2 -1
- data/lib/exekutor/internal/callbacks.rb +55 -35
- data/lib/exekutor/internal/cli/app.rb +33 -23
- data/lib/exekutor/internal/cli/application_loader.rb +17 -6
- data/lib/exekutor/internal/cli/cleanup.rb +54 -40
- data/lib/exekutor/internal/cli/daemon.rb +9 -11
- data/lib/exekutor/internal/cli/default_option_value.rb +3 -1
- data/lib/exekutor/internal/cli/info.rb +117 -84
- data/lib/exekutor/internal/cli/manager.rb +234 -123
- data/lib/exekutor/internal/configuration_builder.rb +49 -30
- data/lib/exekutor/internal/database_connection.rb +6 -0
- data/lib/exekutor/internal/executable.rb +12 -7
- data/lib/exekutor/internal/executor.rb +50 -21
- data/lib/exekutor/internal/hooks.rb +11 -8
- data/lib/exekutor/internal/listener.rb +85 -43
- data/lib/exekutor/internal/logger.rb +29 -10
- data/lib/exekutor/internal/provider.rb +96 -77
- data/lib/exekutor/internal/reserver.rb +66 -19
- data/lib/exekutor/internal/status_server.rb +87 -54
- data/lib/exekutor/job.rb +1 -1
- data/lib/exekutor/job_error.rb +1 -1
- data/lib/exekutor/job_options.rb +22 -13
- data/lib/exekutor/plugins/appsignal.rb +7 -5
- data/lib/exekutor/plugins.rb +8 -4
- data/lib/exekutor/queue.rb +69 -30
- data/lib/exekutor/version.rb +1 -1
- data/lib/exekutor/worker.rb +89 -48
- data/lib/exekutor.rb +2 -2
- data/lib/generators/exekutor/configuration_generator.rb +11 -6
- data/lib/generators/exekutor/install_generator.rb +24 -15
- data/lib/generators/exekutor/templates/install/functions/exekutor_broadcast_job_enqueued.sql +10 -0
- data/lib/generators/exekutor/templates/install/functions/exekutor_requeue_orphaned_jobs.sql +11 -0
- data/lib/generators/exekutor/templates/install/migrations/create_exekutor_schema.rb.erb +23 -22
- data/lib/generators/exekutor/templates/install/triggers/exekutor_broadcast_job_enqueued.sql +7 -0
- data/lib/generators/exekutor/templates/install/triggers/exekutor_requeue_orphaned_jobs.sql +5 -0
- data.tar.gz.sig +0 -0
- metadata +67 -23
- metadata.gz.sig +0 -0
- data/lib/generators/exekutor/templates/install/functions/job_notifier.sql +0 -7
- data/lib/generators/exekutor/templates/install/functions/requeue_orphaned_jobs.sql +0 -7
- data/lib/generators/exekutor/templates/install/triggers/notify_workers.sql +0 -6
- data/lib/generators/exekutor/templates/install/triggers/requeue_orphaned_jobs.sql +0 -5
data/lib/exekutor/hook.rb
CHANGED
@@ -27,12 +27,12 @@ module Exekutor
|
|
27
27
|
before_enqueue around_enqueue after_enqueue before_job_execution around_job_execution after_job_execution
|
28
28
|
on_job_failure on_fatal_error before_startup after_startup before_shutdown after_shutdown
|
29
29
|
].freeze
|
30
|
-
private_constant
|
30
|
+
private_constant :CALLBACK_NAMES
|
31
31
|
|
32
32
|
included do
|
33
33
|
class_attribute :__callbacks, default: Hash.new { |h, k| h[k] = [] }
|
34
34
|
|
35
|
-
private_class_method :
|
35
|
+
private_class_method :_add_callback
|
36
36
|
end
|
37
37
|
|
38
38
|
# Gets the registered callbacks
|
@@ -53,7 +53,6 @@ module Exekutor
|
|
53
53
|
end
|
54
54
|
|
55
55
|
class_methods do
|
56
|
-
|
57
56
|
# @!method before_enqueue
|
58
57
|
# Registers a callback to be called before a job is enqueued.
|
59
58
|
# @param methods [Symbol] the method(s) to call
|
@@ -141,9 +140,9 @@ module Exekutor
|
|
141
140
|
|
142
141
|
CALLBACK_NAMES.each do |name|
|
143
142
|
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
144
|
-
def #{name}(*methods, &callback)
|
145
|
-
|
146
|
-
end
|
143
|
+
def #{name}(*methods, &callback) # def callback_name(*methods, &callback
|
144
|
+
_add_callback :#{name}, methods, callback # _add_callback :callback_name, methods, callback
|
145
|
+
end # end
|
147
146
|
RUBY
|
148
147
|
end
|
149
148
|
|
@@ -156,16 +155,21 @@ module Exekutor
|
|
156
155
|
raise Error, "Invalid callback type: #{type} (Expected one of: #{CALLBACK_NAMES.map(&:inspect).join(", ")}"
|
157
156
|
end
|
158
157
|
|
159
|
-
|
158
|
+
_add_callback type, methods, callback
|
160
159
|
true
|
161
160
|
end
|
162
161
|
|
163
|
-
|
164
|
-
|
162
|
+
# @!visibility private
|
163
|
+
def _add_callback(type, methods, callback)
|
165
164
|
raise Error, "Either a method or a callback block must be supplied" if methods.present? && callback.present?
|
166
165
|
|
167
|
-
methods
|
168
|
-
|
166
|
+
if methods.present?
|
167
|
+
methods.each { |method| __callbacks[type] << [method, nil] }
|
168
|
+
elsif callback.present?
|
169
|
+
__callbacks[type] << [nil, callback]
|
170
|
+
else
|
171
|
+
raise Error, "No method or callback block supplied"
|
172
|
+
end
|
169
173
|
end
|
170
174
|
end
|
171
175
|
end
|
data/lib/exekutor/info/worker.rb
CHANGED
@@ -12,9 +12,9 @@ module Exekutor
|
|
12
12
|
|
13
13
|
# Registers a heartbeat for this worker, if necessary
|
14
14
|
def heartbeat!
|
15
|
-
now = Time.
|
16
|
-
touch :last_heartbeat_at, time: now if
|
15
|
+
now = Time.current.change(sec: 0)
|
16
|
+
touch :last_heartbeat_at, time: now if last_heartbeat_at.nil? || now >= last_heartbeat_at + 1.minute
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
20
|
-
end
|
20
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Exekutor
|
2
4
|
module Internal
|
3
5
|
# Mixin to define callbacks on a class
|
@@ -49,41 +51,21 @@ module Exekutor
|
|
49
51
|
# @param type [:on, :before, :around, :after] the type of the callback
|
50
52
|
# @param action [Symbol] the name of the callback
|
51
53
|
# @param args [Any] the callback args
|
52
|
-
def run_callbacks(type, action, *args)
|
54
|
+
def run_callbacks(type, action, *args, &block)
|
53
55
|
callbacks = __callbacks && __callbacks[:"#{type}_#{action}"]
|
54
56
|
unless callbacks
|
55
|
-
yield(*args) if
|
57
|
+
yield(*args) if block
|
56
58
|
return
|
57
59
|
end
|
58
60
|
if type == :around
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
lambda do
|
67
|
-
has_yielded = false
|
68
|
-
callback.call(*callback_args) { has_yielded = true; next_callback.call }
|
69
|
-
raise MissingYield, "Callback did not yield!" unless has_yielded
|
70
|
-
rescue StandardError => err
|
71
|
-
raise if err.is_a? MissingYield
|
72
|
-
Exekutor.on_fatal_error err, "[Executor] Callback error!"
|
73
|
-
next_callback.call
|
74
|
-
end
|
75
|
-
end.call
|
76
|
-
return
|
77
|
-
end
|
78
|
-
iterator = type == :after ? :each : :reverse_each
|
79
|
-
callbacks.send(iterator) do |(callback, extra_args)|
|
80
|
-
if callback.arity.zero?
|
81
|
-
callback.call
|
82
|
-
else
|
83
|
-
callback.call(*(args + extra_args))
|
61
|
+
chain_callbacks(callbacks, args, &block).call
|
62
|
+
else
|
63
|
+
# Invoke :before in reverse order (last registered first),
|
64
|
+
# invoke :after in original order (last registered last)
|
65
|
+
iterator = type == :after ? :each : :reverse_each
|
66
|
+
callbacks.send(iterator) do |(callback, extra_args)|
|
67
|
+
invoke_callback(callback, args, extra_args)
|
84
68
|
end
|
85
|
-
rescue StandardError => err
|
86
|
-
Exekutor.on_fatal_error err, "[Executor] Callback error!"
|
87
69
|
end
|
88
70
|
nil
|
89
71
|
end
|
@@ -98,6 +80,41 @@ module Exekutor
|
|
98
80
|
|
99
81
|
private
|
100
82
|
|
83
|
+
# Chain all callbacks together, ending with the original given block
|
84
|
+
def chain_callbacks(callbacks, args)
|
85
|
+
callbacks.inject(-> { yield(*args) }) do |next_callback, (callback, extra_args)|
|
86
|
+
# collect args outside of the lambda
|
87
|
+
callback_args = if callback.arity.zero?
|
88
|
+
[]
|
89
|
+
else
|
90
|
+
args + extra_args
|
91
|
+
end
|
92
|
+
lambda do
|
93
|
+
has_yielded = false
|
94
|
+
callback.call(*callback_args) do
|
95
|
+
has_yielded = true
|
96
|
+
next_callback.call
|
97
|
+
end
|
98
|
+
raise MissingYield, "Callback did not yield!" unless has_yielded
|
99
|
+
rescue StandardError => e
|
100
|
+
raise if e.is_a? MissingYield
|
101
|
+
|
102
|
+
Exekutor.on_fatal_error e, "[Executor] Callback error!"
|
103
|
+
next_callback.call
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def invoke_callback(callback, args, extra_args)
|
109
|
+
if callback.arity.zero?
|
110
|
+
callback.call
|
111
|
+
else
|
112
|
+
callback.call(*(args + extra_args))
|
113
|
+
end
|
114
|
+
rescue StandardError => e
|
115
|
+
Exekutor.on_fatal_error e, "[Executor] Callback error!"
|
116
|
+
end
|
117
|
+
|
101
118
|
def add_callback!(type, args, callback)
|
102
119
|
@__callbacks ||= Concurrent::Hash.new
|
103
120
|
__callbacks[type] ||= Concurrent::Array.new
|
@@ -105,12 +122,15 @@ module Exekutor
|
|
105
122
|
end
|
106
123
|
|
107
124
|
class_methods do
|
108
|
-
# Defines the specified callbacks on this class. Also defines a method with the given name to register the
|
109
|
-
#
|
125
|
+
# Defines the specified callbacks on this class. Also defines a method with the given name to register the
|
126
|
+
# callback.
|
127
|
+
# @param callbacks [Symbol] the callback names to define. Must start with +on_+, +before_+, +after_+, or
|
128
|
+
# +around_+.
|
110
129
|
# @param freeze [Boolean] if true, freezes the callbacks so that no other callbacks can be defined
|
111
130
|
# @raise [Error] if a callback name is invalid or if the callbacks are frozen
|
112
131
|
def define_callbacks(*callbacks, freeze: true)
|
113
132
|
raise Error, "Callbacks are frozen, no other callbacks may be defined" if __callback_names.frozen?
|
133
|
+
|
114
134
|
callbacks.each do |name|
|
115
135
|
unless /^(on_)|(before_)|(after_)|(around_)[a-z]+/.match? name.to_s
|
116
136
|
raise Error, "Callback name must start with `on_`, `before_`, `after_`, or `around_`"
|
@@ -118,9 +138,9 @@ module Exekutor
|
|
118
138
|
|
119
139
|
__callback_names << name
|
120
140
|
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
121
|
-
def #{name}(*args, &callback)
|
122
|
-
add_callback! :#{name}, args, callback
|
123
|
-
end
|
141
|
+
def #{name}(*args, &callback) # def callback_method(*args, &callback
|
142
|
+
add_callback! :#{name}, args, callback # add_callback! :callback_method, args, callback
|
143
|
+
end # end
|
124
144
|
RUBY
|
125
145
|
end
|
126
146
|
|
@@ -135,4 +155,4 @@ module Exekutor
|
|
135
155
|
class MissingYield < Exekutor::Error; end
|
136
156
|
end
|
137
157
|
end
|
138
|
-
end
|
158
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "gli"
|
2
4
|
require "rainbow"
|
3
5
|
require_relative "cleanup"
|
@@ -29,24 +31,30 @@ module Exekutor
|
|
29
31
|
switch %i[quiet], negatable: false, desc: "Enable less output"
|
30
32
|
|
31
33
|
# Defines start command flags
|
32
|
-
def self.define_start_options(
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
def self.define_start_options(cmd)
|
35
|
+
cmd.flag %i[env environment], desc: "The Rails environment"
|
36
|
+
cmd.flag %i[q queue], default_value: Manager::DEFAULT_QUEUE, multiple: true,
|
37
|
+
desc: "Queue to work from"
|
38
|
+
cmd.flag %i[p priority], type: String, default_value: Manager::DEFAULT_PRIORITIES,
|
39
|
+
desc: "The job priorities to execute, specified as `min` or `min:max`"
|
40
|
+
cmd.flag %i[t threads], type: String, default_value: Manager::DEFAULT_THREADS,
|
41
|
+
desc: "The number of threads for executing jobs, specified as `min:max`"
|
42
|
+
cmd.flag %i[i poll_interval], type: Integer, default_value: DefaultOptionValue.new(value: 60),
|
43
|
+
desc: "Interval between polls for available jobs (in seconds)"
|
44
|
+
cmd.flag %i[cfg configfile], type: String, default_value: Manager::DEFAULT_CONFIG_FILES, multiple: true,
|
45
|
+
desc: "The YAML configuration file to load. If specifying multiple files, the last file takes " \
|
46
|
+
"precedence"
|
42
47
|
end
|
48
|
+
|
43
49
|
private_class_method :define_start_options
|
44
50
|
|
45
51
|
# Defines stop command flags
|
46
|
-
def self.define_stop_options(
|
47
|
-
|
48
|
-
|
52
|
+
def self.define_stop_options(cmd)
|
53
|
+
cmd.flag %i[timeout shutdown_timeout], default_value: Manager::DEFAULT_FOREVER,
|
54
|
+
desc: "Number of seconds to wait for jobs to finish when shutting down before killing the worker. " \
|
55
|
+
"(in seconds)"
|
49
56
|
end
|
57
|
+
|
50
58
|
private_class_method :define_stop_options
|
51
59
|
|
52
60
|
desc "Starts a worker"
|
@@ -67,8 +75,8 @@ module Exekutor
|
|
67
75
|
|
68
76
|
desc "Stops a daemonized worker"
|
69
77
|
long_desc <<~TEXT
|
70
|
-
Stops a daemonized worker. This uses the PID file to send a shutdown command to a running worker. If the
|
71
|
-
does not exit within the shutdown timeout it will kill the process.
|
78
|
+
Stops a daemonized worker. This uses the PID file to send a shutdown command to a running worker. If the
|
79
|
+
worker does not exit within the shutdown timeout it will kill the process.
|
72
80
|
TEXT
|
73
81
|
command :stop do |c|
|
74
82
|
c.switch :all, negatable: false, desc: "Stops all workers with default pid files."
|
@@ -76,7 +84,9 @@ module Exekutor
|
|
76
84
|
|
77
85
|
c.action do |global_options, options|
|
78
86
|
if options[:all]
|
79
|
-
|
87
|
+
unless global_options[:identifier].nil? || global_options[:quiet]
|
88
|
+
puts "The identifier option is ignored for --all"
|
89
|
+
end
|
80
90
|
pidfile_pattern = if options[:pidfile].nil? || options[:pidfile] == Manager::DEFAULT_PIDFILE
|
81
91
|
"tmp/pids/exekutor*.pid"
|
82
92
|
else
|
@@ -98,8 +108,8 @@ module Exekutor
|
|
98
108
|
|
99
109
|
desc "Restarts a daemonized worker"
|
100
110
|
long_desc <<~TEXT
|
101
|
-
Restarts a daemonized worker. Will issue the stop command if a worker is running and wait for the active
|
102
|
-
to exit before starting a new worker. If no worker is currently running, a new worker will be started.
|
111
|
+
Restarts a daemonized worker. Will issue the stop command if a worker is running and wait for the active
|
112
|
+
worker to exit before starting a new worker. If no worker is currently running, a new worker will be started.
|
103
113
|
TEXT
|
104
114
|
command :restart do |c|
|
105
115
|
define_stop_options c
|
@@ -107,7 +117,7 @@ module Exekutor
|
|
107
117
|
|
108
118
|
c.action do |global_options, options|
|
109
119
|
Manager.new(global_options).restart(options.slice(:shutdown_timeout),
|
110
|
-
options.
|
120
|
+
options.except(:shutdown_timeout))
|
111
121
|
end
|
112
122
|
end
|
113
123
|
|
@@ -127,14 +137,15 @@ module Exekutor
|
|
127
137
|
long_desc <<~TEXT
|
128
138
|
Cleans up the finished jobs and stale workers
|
129
139
|
TEXT
|
130
|
-
command :cleanup do |c|
|
140
|
+
command :cleanup do |c| # rubocop:disable Metrics/BlockLength
|
131
141
|
c.flag %i[env environment], desc: "The Rails environment."
|
132
142
|
|
133
143
|
c.flag %i[t timeout],
|
134
144
|
desc: "The global timeout in hours. Workers and jobs before the timeout will be purged"
|
135
145
|
c.flag %i[worker_timeout],
|
136
146
|
default_value: 4,
|
137
|
-
desc: "The worker timeout in hours. Workers where the last heartbeat is before the timeout will be
|
147
|
+
desc: "The worker timeout in hours. Workers where the last heartbeat is before the timeout will be " \
|
148
|
+
"deleted."
|
138
149
|
c.flag %i[job_timeout],
|
139
150
|
default_value: 48,
|
140
151
|
desc: "The job timeout in hours. Jobs where scheduled at is before the timeout will be purged."
|
@@ -167,7 +178,6 @@ module Exekutor
|
|
167
178
|
end
|
168
179
|
end
|
169
180
|
end
|
170
|
-
|
171
181
|
end
|
172
182
|
end
|
173
|
-
end
|
183
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Exekutor
|
2
4
|
module Internal
|
3
5
|
module CLI
|
@@ -12,6 +14,7 @@ module Exekutor
|
|
12
14
|
# @param print_message [Boolean] whether to print a loading message to STDOUT
|
13
15
|
def load_application(environment, path = "config/environment.rb", print_message: false)
|
14
16
|
return if @application_loaded
|
17
|
+
|
15
18
|
if print_message
|
16
19
|
printf LOADING_MESSAGE
|
17
20
|
@loading_message_printed = true
|
@@ -23,14 +26,22 @@ module Exekutor
|
|
23
26
|
|
24
27
|
# Clears the loading message if it was printed
|
25
28
|
def clear_application_loading_message
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
29
|
+
return unless @loading_message_printed
|
30
|
+
|
31
|
+
printf "\r#{" " * LOADING_MESSAGE.length}\r"
|
32
|
+
@loading_message_printed = false
|
30
33
|
end
|
31
34
|
|
35
|
+
# @return Whether the system time zone differs from the app time zone
|
36
|
+
def different_time_zone?
|
37
|
+
Time.zone.name != Time.new.zone
|
38
|
+
end
|
39
|
+
|
40
|
+
# Prints a message to STDOUT indicating in which time zone times are printed
|
41
|
+
def print_time_zone_warning
|
42
|
+
puts "(times are printed in the #{Time.zone.name} time zone)\n\n"
|
43
|
+
end
|
32
44
|
end
|
33
45
|
end
|
34
46
|
end
|
35
|
-
|
36
|
-
end
|
47
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative "application_loader"
|
2
4
|
require_relative "default_option_value"
|
3
5
|
require "terminal-table"
|
@@ -21,26 +23,13 @@ module Exekutor
|
|
21
23
|
load_application options[:environment], print_message: !quiet?
|
22
24
|
|
23
25
|
ActiveSupport.on_load(:active_record, yield: true) do
|
24
|
-
|
25
|
-
|
26
|
+
clear_application_loading_message
|
27
|
+
print_time_zone_warning if different_time_zone? && !quiet?
|
26
28
|
|
27
|
-
|
28
|
-
timeout = options[:timeout] || options[:worker_timeout] || 4
|
29
|
+
timeout = worker_cleanup_timeout(options)
|
29
30
|
workers = cleaner.cleanup_workers timeout: timeout.hours
|
30
|
-
|
31
|
-
|
32
|
-
puts Rainbow("Workers").bright.blue if options[:print_header]
|
33
|
-
if workers.present?
|
34
|
-
puts "Purged #{workers.size} worker#{"s" if workers.many?}"
|
35
|
-
if verbose?
|
36
|
-
table = Terminal::Table.new
|
37
|
-
table.headings = ["id", "Last heartbeat"]
|
38
|
-
workers.each { |w| table << [w.id.split("-").first << "…", w.last_heartbeat_at] }
|
39
|
-
puts table
|
40
|
-
end
|
41
|
-
else
|
42
|
-
puts "Nothing purged"
|
43
|
-
end
|
31
|
+
|
32
|
+
print_worker_cleanup_result(options, workers) unless quiet?
|
44
33
|
end
|
45
34
|
end
|
46
35
|
|
@@ -50,30 +39,56 @@ module Exekutor
|
|
50
39
|
load_application options[:environment], print_message: !quiet?
|
51
40
|
|
52
41
|
ActiveSupport.on_load(:active_record, yield: true) do
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
elsif options[:job_status] && options[:job_status] != DEFAULT_STATUSES
|
61
|
-
options[:job_status]
|
62
|
-
end
|
63
|
-
purged_count = cleaner.cleanup_jobs before: timeout.hours.ago, status: status
|
64
|
-
return if quiet?
|
65
|
-
|
66
|
-
puts Rainbow("Jobs").bright.blue if options[:print_header]
|
67
|
-
if purged_count.zero?
|
68
|
-
puts "Nothing purged"
|
69
|
-
else
|
70
|
-
puts "Purged #{purged_count} job#{"s" if purged_count > 1}"
|
71
|
-
end
|
42
|
+
clear_application_loading_message
|
43
|
+
print_time_zone_warning if different_time_zone? && !quiet?
|
44
|
+
|
45
|
+
timeout = job_cleanup_timeout(options)
|
46
|
+
purged_count = cleaner.cleanup_jobs before: timeout.hours.ago, status: job_cleanup_statuses(options)
|
47
|
+
|
48
|
+
print_job_cleanup_result(options, purged_count) unless quiet?
|
72
49
|
end
|
73
50
|
end
|
74
51
|
|
75
52
|
private
|
76
53
|
|
54
|
+
def job_cleanup_statuses(options)
|
55
|
+
options[:job_status] if options[:job_status] && options[:job_status] != DEFAULT_STATUSES
|
56
|
+
end
|
57
|
+
|
58
|
+
def job_cleanup_timeout(options)
|
59
|
+
options[:timeout] || options[:job_timeout] || 48
|
60
|
+
end
|
61
|
+
|
62
|
+
def print_job_cleanup_result(options, purged_count)
|
63
|
+
puts Rainbow("Jobs").bright.blue if options[:print_header]
|
64
|
+
if purged_count.zero?
|
65
|
+
puts "Nothing purged"
|
66
|
+
else
|
67
|
+
puts "Purged #{purged_count} job#{"s" if purged_count > 1}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def worker_cleanup_timeout(options)
|
72
|
+
options[:timeout] || options[:worker_timeout] || 4
|
73
|
+
end
|
74
|
+
|
75
|
+
def print_worker_cleanup_result(options, workers)
|
76
|
+
puts Rainbow("Workers").bright.blue if options[:print_header]
|
77
|
+
if workers.present?
|
78
|
+
puts "Purged #{workers.size} worker#{"s" if workers.many?}"
|
79
|
+
print_worker_info(workers) if verbose?
|
80
|
+
else
|
81
|
+
puts "Nothing purged"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def print_worker_info(workers)
|
86
|
+
table = Terminal::Table.new
|
87
|
+
table.headings = ["id", "Last heartbeat"]
|
88
|
+
workers.each { |w| table << [w.id.split("-").first << "…", w.last_heartbeat_at] }
|
89
|
+
puts table
|
90
|
+
end
|
91
|
+
|
77
92
|
# @return [Boolean] Whether quiet mode is enabled. Overrides verbose mode.
|
78
93
|
def quiet?
|
79
94
|
!!@global_options[:quiet]
|
@@ -85,12 +100,11 @@ module Exekutor
|
|
85
100
|
end
|
86
101
|
|
87
102
|
def cleaner
|
88
|
-
@
|
103
|
+
@cleaner ||= ::Exekutor::Cleanup.new
|
89
104
|
end
|
90
105
|
|
91
106
|
DEFAULT_STATUSES = DefaultOptionValue.new("All except :pending").freeze
|
92
107
|
end
|
93
108
|
end
|
94
|
-
|
95
109
|
end
|
96
|
-
end
|
110
|
+
end
|
@@ -31,11 +31,9 @@ module Exekutor
|
|
31
31
|
return nil unless ::File.exist? pidfile
|
32
32
|
|
33
33
|
pid = ::File.read(pidfile)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
raise Error, "Corrupt PID-file. Check #{pidfile}"
|
38
|
-
end
|
34
|
+
raise Error, "Corrupt PID-file. Check #{pidfile}" unless pid.to_i.positive?
|
35
|
+
|
36
|
+
pid.to_i
|
39
37
|
end
|
40
38
|
|
41
39
|
# The process status for this daemon. Possible states are:
|
@@ -48,8 +46,8 @@ module Exekutor
|
|
48
46
|
pid = self.pid
|
49
47
|
return :not_running if pid.nil?
|
50
48
|
|
51
|
-
# If sig is 0, then no signal is sent, but error checking is still performed; this can be used to check for
|
52
|
-
# existence of a process ID or process group ID.
|
49
|
+
# If sig is 0, then no signal is sent, but error checking is still performed; this can be used to check for
|
50
|
+
# the existence of a process ID or process group ID.
|
53
51
|
::Process.kill(0, pid)
|
54
52
|
:running
|
55
53
|
rescue Errno::ESRCH
|
@@ -63,14 +61,14 @@ module Exekutor
|
|
63
61
|
# @return [Boolean] whether the status matches
|
64
62
|
# @see #status
|
65
63
|
def status?(*statuses)
|
66
|
-
statuses.include?
|
64
|
+
statuses.include? status
|
67
65
|
end
|
68
66
|
|
69
67
|
# Raises an {Error} if a daemon is already running. Deletes the pidfile is the process is dead.
|
70
68
|
# @return [void]
|
71
69
|
# @raise [Error] when the daemon is running
|
72
70
|
def validate!
|
73
|
-
case
|
71
|
+
case status
|
74
72
|
when :running, :not_owned
|
75
73
|
raise Error, "A worker is already running. Check #{pidfile}"
|
76
74
|
else
|
@@ -97,7 +95,7 @@ module Exekutor
|
|
97
95
|
# @return [void]
|
98
96
|
# @see #pidfile
|
99
97
|
def delete_pid
|
100
|
-
|
98
|
+
FileUtils.rm_f(pidfile)
|
101
99
|
end
|
102
100
|
|
103
101
|
# Raised when spawning a daemon process fails
|
@@ -105,4 +103,4 @@ module Exekutor
|
|
105
103
|
end
|
106
104
|
end
|
107
105
|
end
|
108
|
-
end
|
106
|
+
end
|