inst-jobs 2.0.0 → 3.0.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/db/migrate/20101216224513_create_delayed_jobs.rb +9 -7
- data/db/migrate/20110531144916_cleanup_delayed_jobs_indexes.rb +8 -13
- data/db/migrate/20110610213249_optimize_delayed_jobs.rb +8 -8
- data/db/migrate/20110831210257_add_delayed_jobs_next_in_strand.rb +25 -25
- data/db/migrate/20120510004759_delayed_jobs_delete_trigger_lock_for_update.rb +4 -8
- data/db/migrate/20120531150712_drop_psql_jobs_pop_fn.rb +1 -3
- data/db/migrate/20120607164022_delayed_jobs_use_advisory_locks.rb +11 -15
- data/db/migrate/20120607181141_index_jobs_on_locked_by.rb +1 -1
- data/db/migrate/20120608191051_add_jobs_run_at_index.rb +2 -2
- data/db/migrate/20120927184213_change_delayed_jobs_handler_to_text.rb +1 -1
- data/db/migrate/20140505215510_copy_failed_jobs_original_id.rb +2 -3
- data/db/migrate/20150807133223_add_max_concurrent_to_jobs.rb +9 -13
- data/db/migrate/20151210162949_improve_max_concurrent.rb +4 -8
- data/db/migrate/20161206323555_add_back_default_string_limits_jobs.rb +3 -2
- data/db/migrate/20181217155351_speed_up_max_concurrent_triggers.rb +13 -17
- data/db/migrate/20200330230722_add_id_to_get_delayed_jobs_index.rb +8 -8
- data/db/migrate/20200824222232_speed_up_max_concurrent_delete_trigger.rb +72 -77
- data/db/migrate/20200825011002_add_strand_order_override.rb +93 -97
- data/db/migrate/20210809145804_add_n_strand_index.rb +12 -0
- data/db/migrate/20210812210128_add_singleton_column.rb +200 -0
- data/db/migrate/20210917232626_add_delete_conflicting_singletons_before_unlock_trigger.rb +27 -0
- data/db/migrate/20210928174754_fix_singleton_condition_in_before_insert.rb +56 -0
- data/db/migrate/20210929204903_update_conflicting_singleton_function_to_use_index.rb +27 -0
- data/exe/inst_jobs +3 -2
- data/lib/delayed/backend/active_record.rb +211 -168
- data/lib/delayed/backend/base.rb +110 -72
- data/lib/delayed/batch.rb +11 -9
- data/lib/delayed/cli.rb +98 -84
- data/lib/delayed/core_ext/kernel.rb +4 -2
- data/lib/delayed/daemon.rb +70 -74
- data/lib/delayed/job_tracking.rb +26 -25
- data/lib/delayed/lifecycle.rb +27 -23
- data/lib/delayed/log_tailer.rb +17 -17
- data/lib/delayed/logging.rb +13 -16
- data/lib/delayed/message_sending.rb +43 -52
- data/lib/delayed/performable_method.rb +6 -8
- data/lib/delayed/periodic.rb +72 -68
- data/lib/delayed/plugin.rb +2 -4
- data/lib/delayed/pool.rb +205 -168
- data/lib/delayed/server/helpers.rb +6 -6
- data/lib/delayed/server.rb +51 -54
- data/lib/delayed/settings.rb +94 -81
- data/lib/delayed/testing.rb +21 -22
- data/lib/delayed/version.rb +1 -1
- data/lib/delayed/work_queue/in_process.rb +21 -17
- data/lib/delayed/work_queue/parent_process/client.rb +55 -53
- data/lib/delayed/work_queue/parent_process/server.rb +245 -207
- data/lib/delayed/work_queue/parent_process.rb +52 -53
- data/lib/delayed/worker/consul_health_check.rb +32 -33
- data/lib/delayed/worker/health_check.rb +34 -26
- data/lib/delayed/worker/null_health_check.rb +3 -1
- data/lib/delayed/worker/process_helper.rb +8 -9
- data/lib/delayed/worker.rb +272 -241
- data/lib/delayed/yaml_extensions.rb +12 -10
- data/lib/delayed_job.rb +37 -37
- data/lib/inst-jobs.rb +1 -1
- data/spec/active_record_job_spec.rb +143 -139
- data/spec/delayed/cli_spec.rb +7 -7
- data/spec/delayed/daemon_spec.rb +10 -9
- data/spec/delayed/message_sending_spec.rb +16 -9
- data/spec/delayed/periodic_spec.rb +14 -21
- data/spec/delayed/server_spec.rb +38 -38
- data/spec/delayed/settings_spec.rb +26 -25
- data/spec/delayed/work_queue/in_process_spec.rb +7 -8
- data/spec/delayed/work_queue/parent_process/client_spec.rb +17 -12
- data/spec/delayed/work_queue/parent_process/server_spec.rb +117 -41
- data/spec/delayed/work_queue/parent_process_spec.rb +21 -23
- data/spec/delayed/worker/consul_health_check_spec.rb +37 -50
- data/spec/delayed/worker/health_check_spec.rb +60 -52
- data/spec/delayed/worker_spec.rb +44 -21
- data/spec/sample_jobs.rb +45 -15
- data/spec/shared/delayed_batch.rb +74 -67
- data/spec/shared/delayed_method.rb +143 -102
- data/spec/shared/performable_method.rb +39 -38
- data/spec/shared/shared_backend.rb +550 -437
- data/spec/shared/testing.rb +14 -14
- data/spec/shared/worker.rb +156 -148
- data/spec/shared_jobs_specs.rb +13 -13
- data/spec/spec_helper.rb +53 -55
- metadata +148 -82
- data/lib/delayed/backend/redis/bulk_update.lua +0 -50
- data/lib/delayed/backend/redis/destroy_job.lua +0 -2
- data/lib/delayed/backend/redis/enqueue.lua +0 -29
- data/lib/delayed/backend/redis/fail_job.lua +0 -5
- data/lib/delayed/backend/redis/find_available.lua +0 -3
- data/lib/delayed/backend/redis/functions.rb +0 -59
- data/lib/delayed/backend/redis/get_and_lock_next_available.lua +0 -17
- data/lib/delayed/backend/redis/includes/jobs_common.lua +0 -203
- data/lib/delayed/backend/redis/job.rb +0 -535
- data/lib/delayed/backend/redis/set_running.lua +0 -5
- data/lib/delayed/backend/redis/tickle_strand.lua +0 -2
- data/spec/gemfiles/42.gemfile +0 -7
- data/spec/gemfiles/50.gemfile +0 -7
- data/spec/gemfiles/51.gemfile +0 -7
- data/spec/gemfiles/52.gemfile +0 -7
- data/spec/gemfiles/60.gemfile +0 -7
- data/spec/redis_job_spec.rb +0 -148
data/lib/delayed/server.rb
CHANGED
@@ -1,34 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require "sinatra/base"
|
4
|
+
require "sinatra/json"
|
5
|
+
require "json"
|
6
|
+
require "delayed_job"
|
7
7
|
|
8
8
|
module Delayed
|
9
9
|
class Server < Sinatra::Base
|
10
10
|
APP_DIR = File.dirname(File.expand_path(__FILE__))
|
11
|
-
set :views, File.join(APP_DIR,
|
12
|
-
set :public_folder, File.join(APP_DIR,
|
11
|
+
set :views, File.join(APP_DIR, "server", "views")
|
12
|
+
set :public_folder, File.join(APP_DIR, "server", "public")
|
13
13
|
|
14
|
-
def initialize(*args
|
14
|
+
def initialize(*args)
|
15
15
|
super()
|
16
16
|
# Rails will take care of establishing the DB connection for us if there is
|
17
17
|
# an application present
|
18
18
|
if using_active_record? && !ActiveRecord::Base.connected?
|
19
|
-
ActiveRecord::Base.establish_connection(ENV[
|
19
|
+
ActiveRecord::Base.establish_connection(ENV["DATABASE_URL"])
|
20
20
|
end
|
21
21
|
|
22
|
-
@allow_update = args.length
|
22
|
+
@allow_update = args.length.positive? && args[0][:update]
|
23
23
|
end
|
24
24
|
|
25
25
|
def using_active_record?
|
26
26
|
Delayed::Job == Delayed::Backend::ActiveRecord::Job
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
@allow_update
|
31
|
-
end
|
29
|
+
attr_reader :allow_update
|
32
30
|
|
33
31
|
# Ensure we're connected to the DB before processing the request
|
34
32
|
before do
|
@@ -43,72 +41,72 @@ module Delayed
|
|
43
41
|
end
|
44
42
|
|
45
43
|
configure :development do
|
46
|
-
require
|
44
|
+
require "sinatra/reloader"
|
47
45
|
register Sinatra::Reloader
|
48
46
|
end
|
49
47
|
|
50
48
|
helpers do
|
51
49
|
# this can't get required until the class has been opened for the first time
|
52
|
-
require
|
50
|
+
require "delayed/server/helpers"
|
53
51
|
include Delayed::Server::Helpers
|
54
52
|
end
|
55
53
|
|
56
|
-
get
|
54
|
+
get "/" do
|
57
55
|
erb :index
|
58
56
|
end
|
59
57
|
|
60
|
-
get
|
58
|
+
get "/running" do
|
61
59
|
content_type :json
|
62
60
|
json({
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
61
|
+
draw: params["draw"].to_i,
|
62
|
+
recordsTotal: Delayed::Job.running.count,
|
63
|
+
recordsFiltered: Delayed::Job.running.count,
|
64
|
+
data: Delayed::Job.running_jobs.map do |j|
|
65
|
+
j.as_json(include_root: false, except: %i[handler last_error])
|
66
|
+
end
|
67
|
+
})
|
70
68
|
end
|
71
69
|
|
72
|
-
get
|
70
|
+
get "/tags" do
|
73
71
|
content_type :json
|
74
72
|
json({
|
75
|
-
|
76
|
-
|
77
|
-
|
73
|
+
draw: params["draw"].to_i,
|
74
|
+
data: Delayed::Job.tag_counts("current", 10)
|
75
|
+
})
|
78
76
|
end
|
79
77
|
|
80
78
|
DEFAULT_PAGE_SIZE = 10
|
81
79
|
MAX_PAGE_SIZE = 100
|
82
|
-
get
|
80
|
+
get "/jobs" do
|
83
81
|
content_type :json
|
84
|
-
flavor = params[
|
82
|
+
flavor = params["flavor"] || "current"
|
85
83
|
page_size = extract_page_size
|
86
|
-
offset = Integer(params[
|
84
|
+
offset = Integer(params["start"] || 0)
|
87
85
|
case flavor
|
88
|
-
when
|
89
|
-
jobs = Delayed::Job.where(id: params[
|
86
|
+
when "id"
|
87
|
+
jobs = Delayed::Job.where(id: params["search_term"])
|
90
88
|
total_records = 1
|
91
|
-
when
|
89
|
+
when "future", "current", "failed"
|
92
90
|
jobs = Delayed::Job.list_jobs(flavor, page_size, offset)
|
93
|
-
total_records =
|
91
|
+
total_records = Delayed::Job.jobs_count(flavor)
|
94
92
|
else
|
95
|
-
query = params[
|
96
|
-
if query.present?
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
total_records =
|
93
|
+
query = params["search_term"]
|
94
|
+
jobs = if query.present?
|
95
|
+
Delayed::Job.list_jobs(flavor, page_size, offset, query)
|
96
|
+
else
|
97
|
+
[]
|
98
|
+
end
|
99
|
+
total_records = Delayed::Job.jobs_count(flavor, query)
|
102
100
|
end
|
103
101
|
json({
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
102
|
+
draw: params["draw"].to_i,
|
103
|
+
recordsTotal: total_records,
|
104
|
+
recordsFiltered: jobs.size,
|
105
|
+
data: build_jobs_json(jobs)
|
106
|
+
})
|
109
107
|
end
|
110
108
|
|
111
|
-
post
|
109
|
+
post "/bulk_update" do
|
112
110
|
content_type :json
|
113
111
|
|
114
112
|
halt 403 unless @allow_update
|
@@ -117,25 +115,24 @@ module Delayed
|
|
117
115
|
Delayed::Job.bulk_update(payload[:action], { ids: payload[:ids] })
|
118
116
|
|
119
117
|
json({
|
120
|
-
|
121
|
-
|
118
|
+
success: true
|
119
|
+
})
|
122
120
|
end
|
123
121
|
|
124
122
|
private
|
125
123
|
|
126
124
|
def extract_page_size
|
127
|
-
page_size = Integer(params[
|
125
|
+
page_size = Integer(params["length"] || DEFAULT_PAGE_SIZE)
|
128
126
|
# if dataTables wants all of the records it will send us -1 but we don't
|
129
127
|
# want the potential to kill our servers with this request so we'll limit it
|
130
128
|
page_size = DEFAULT_PAGE_SIZE if page_size == -1
|
131
129
|
[page_size, MAX_PAGE_SIZE].min
|
132
130
|
end
|
133
131
|
|
134
|
-
|
135
132
|
def build_jobs_json(jobs)
|
136
|
-
|
137
|
-
j.as_json(root: false, except: [
|
138
|
-
|
133
|
+
jobs.map do |j|
|
134
|
+
j.as_json(root: false, except: %i[handler last_error])
|
135
|
+
end
|
139
136
|
end
|
140
137
|
end
|
141
138
|
end
|
data/lib/delayed/settings.rb
CHANGED
@@ -1,16 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "yaml"
|
4
|
+
require "erb"
|
5
|
+
require "active_support/core_ext/hash/indifferent_access"
|
6
6
|
|
7
7
|
module Delayed
|
8
8
|
module Settings
|
9
9
|
SETTINGS = [
|
10
10
|
:default_job_options,
|
11
|
+
:disable_abandoned_job_cleanup,
|
11
12
|
:disable_periodic_jobs,
|
12
13
|
:disable_automatic_orphan_unlocking,
|
13
14
|
:fetch_batch_size,
|
15
|
+
# this is a transitional setting, so that you don't have a time where a
|
16
|
+
# singleton switches to using the singleton column, but there are old
|
17
|
+
# jobs that only used strand
|
18
|
+
:infer_strand_from_singleton,
|
14
19
|
:kill_workers_on_exit,
|
15
20
|
:last_ditch_logfile,
|
16
21
|
:max_attempts,
|
@@ -23,23 +28,13 @@ module Delayed
|
|
23
28
|
:slow_exit_timeout,
|
24
29
|
:worker_health_check_type,
|
25
30
|
:worker_health_check_config,
|
26
|
-
:worker_procname_prefix
|
27
|
-
]
|
28
|
-
SETTINGS_WITH_ARGS = [
|
29
|
-
:job_detailed_log_format,
|
30
|
-
:num_strands
|
31
|
-
]
|
32
|
-
|
33
|
-
SETTINGS.each do |setting|
|
34
|
-
mattr_writer(setting)
|
35
|
-
self.send("#{setting}=", nil)
|
36
|
-
define_singleton_method(setting) do
|
37
|
-
val = class_variable_get(:"@@#{setting}")
|
38
|
-
val.respond_to?(:call) ? val.call() : val
|
39
|
-
end
|
40
|
-
end
|
31
|
+
:worker_procname_prefix
|
32
|
+
].freeze
|
41
33
|
|
42
|
-
|
34
|
+
SETTINGS_WITH_ARGS = %i[
|
35
|
+
job_detailed_log_format
|
36
|
+
num_strands
|
37
|
+
].freeze
|
43
38
|
|
44
39
|
PARENT_PROCESS_DEFAULTS = {
|
45
40
|
server_socket_timeout: 10.0,
|
@@ -49,17 +44,86 @@ module Delayed
|
|
49
44
|
|
50
45
|
# We'll accept a partial, relative path and assume we want it inside
|
51
46
|
# Rails.root with inst-jobs.sock appended if provided a directory.
|
52
|
-
server_address:
|
47
|
+
server_address: "tmp"
|
53
48
|
}.with_indifferent_access.freeze
|
54
49
|
|
55
|
-
|
56
|
-
|
50
|
+
class << self
|
51
|
+
attr_accessor(*SETTINGS_WITH_ARGS)
|
52
|
+
attr_reader :parent_process
|
53
|
+
|
54
|
+
SETTINGS.each do |setting|
|
55
|
+
attr_writer setting
|
56
|
+
|
57
|
+
define_method(setting) do
|
58
|
+
val = instance_variable_get(:"@#{setting}")
|
59
|
+
val.respond_to?(:call) ? val.call : val
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def queue=(queue_name)
|
64
|
+
raise ArgumentError, "queue_name must not be blank" if queue_name.blank?
|
65
|
+
|
66
|
+
@queue = queue_name
|
67
|
+
end
|
68
|
+
|
69
|
+
def worker_config(config_filename = nil)
|
70
|
+
config_filename ||= default_worker_config_name
|
71
|
+
config = YAML.load(ERB.new(File.read(config_filename)).result)
|
72
|
+
env = Rails.env || "development"
|
73
|
+
config = config[env] || config["default"]
|
74
|
+
# Backwards compatibility from when the config was just an array of queues
|
75
|
+
config = { workers: config } if config.is_a?(Array)
|
76
|
+
unless config.is_a?(Hash)
|
77
|
+
raise ArgumentError,
|
78
|
+
"Invalid config file #{config_filename}"
|
79
|
+
end
|
80
|
+
config = config.with_indifferent_access
|
81
|
+
config[:workers].map! do |worker_config|
|
82
|
+
config.except(:workers).merge(worker_config.with_indifferent_access)
|
83
|
+
end
|
84
|
+
config
|
85
|
+
end
|
86
|
+
|
87
|
+
def apply_worker_config!(config)
|
88
|
+
SETTINGS.each do |setting|
|
89
|
+
send("#{setting}=", config[setting.to_s]) if config.key?(setting.to_s)
|
90
|
+
end
|
91
|
+
if config.key?("parent_process_client_timeout")
|
92
|
+
parent_process.client_timeout = config["parent_process_client_timeout"]
|
93
|
+
end
|
94
|
+
self.parent_process = config["parent_process"] if config.key?("parent_process")
|
95
|
+
end
|
96
|
+
|
97
|
+
def default_worker_config_name
|
98
|
+
expand_rails_path("config/delayed_jobs.yml")
|
99
|
+
end
|
100
|
+
|
101
|
+
# Expands rails-relative paths, without depending on rails being loaded.
|
102
|
+
def expand_rails_path(path)
|
103
|
+
root = if defined?(Rails) && Rails.root
|
104
|
+
Rails.root.join("Gemfile")
|
105
|
+
else
|
106
|
+
ENV.fetch("BUNDLE_GEMFILE", "#{Dir.pwd}/Gemfile")
|
107
|
+
end
|
108
|
+
File.expand_path("../#{path}", root)
|
109
|
+
end
|
110
|
+
|
111
|
+
def parent_process_client_timeout=(val)
|
112
|
+
parent_process["server_socket_timeout"] = Integer(val)
|
113
|
+
end
|
114
|
+
|
115
|
+
def parent_process=(new_config)
|
116
|
+
raise "Parent process configurations must be a hash!" unless new_config.is_a?(Hash)
|
117
|
+
|
118
|
+
@parent_process = PARENT_PROCESS_DEFAULTS.merge(new_config)
|
119
|
+
end
|
57
120
|
|
58
|
-
|
59
|
-
|
60
|
-
|
121
|
+
def worker_health_check_config=(new_config)
|
122
|
+
@worker_health_check_config = (new_config || {}).with_indifferent_access
|
123
|
+
end
|
61
124
|
end
|
62
125
|
|
126
|
+
self.parent_process = PARENT_PROCESS_DEFAULTS.dup
|
63
127
|
self.queue = "queue"
|
64
128
|
self.max_attempts = 1
|
65
129
|
self.sleep_delay = 2.0
|
@@ -68,9 +132,11 @@ module Delayed
|
|
68
132
|
self.select_random_from_batch = false
|
69
133
|
self.silence_periodic_log = false
|
70
134
|
|
71
|
-
self.num_strands = ->(
|
72
|
-
self.default_job_options = ->{
|
73
|
-
self.job_detailed_log_format =
|
135
|
+
self.num_strands = ->(_strand_name) {}
|
136
|
+
self.default_job_options = -> { {} }
|
137
|
+
self.job_detailed_log_format = lambda { |job|
|
138
|
+
job.to_json(include_root: false, only: %w[tag strand priority attempts created_at max_attempts source])
|
139
|
+
}
|
74
140
|
|
75
141
|
# Send workers KILL after QUIT if they haven't exited within the
|
76
142
|
# slow_exit_timeout
|
@@ -79,58 +145,5 @@ module Delayed
|
|
79
145
|
|
80
146
|
self.worker_health_check_type = :none
|
81
147
|
self.worker_health_check_config = {}
|
82
|
-
|
83
|
-
def self.worker_config(config_filename = nil)
|
84
|
-
config_filename ||= default_worker_config_name
|
85
|
-
config = YAML.load(ERB.new(File.read(config_filename)).result)
|
86
|
-
env = defined?(RAILS_ENV) ? RAILS_ENV : ENV['RAILS_ENV'] || 'development'
|
87
|
-
config = config[env] || config['default']
|
88
|
-
# Backwards compatibility from when the config was just an array of queues
|
89
|
-
config = { :workers => config } if config.is_a?(Array)
|
90
|
-
unless config && config.is_a?(Hash)
|
91
|
-
raise ArgumentError,
|
92
|
-
"Invalid config file #{config_filename}"
|
93
|
-
end
|
94
|
-
config = config.with_indifferent_access
|
95
|
-
config[:workers].map! do |worker_config|
|
96
|
-
config.except(:workers).merge(worker_config.with_indifferent_access)
|
97
|
-
end
|
98
|
-
config
|
99
|
-
end
|
100
|
-
|
101
|
-
def self.apply_worker_config!(config)
|
102
|
-
SETTINGS.each do |setting|
|
103
|
-
self.send("#{setting}=", config[setting.to_s]) if config.key?(setting.to_s)
|
104
|
-
end
|
105
|
-
parent_process.client_timeout = config['parent_process_client_timeout'] if config.key?('parent_process_client_timeout')
|
106
|
-
self.parent_process = config['parent_process'] if config.key?('parent_process')
|
107
|
-
end
|
108
|
-
|
109
|
-
def self.default_worker_config_name
|
110
|
-
expand_rails_path("config/delayed_jobs.yml")
|
111
|
-
end
|
112
|
-
|
113
|
-
# Expands rails-relative paths, without depending on rails being loaded.
|
114
|
-
def self.expand_rails_path(path)
|
115
|
-
root = if defined?(Rails) && Rails.root
|
116
|
-
(Rails.root+"Gemfile").to_s
|
117
|
-
else
|
118
|
-
ENV.fetch('BUNDLE_GEMFILE', Dir.pwd+"/Gemfile")
|
119
|
-
end
|
120
|
-
File.expand_path("../#{path}", root)
|
121
|
-
end
|
122
|
-
|
123
|
-
def self.parent_process_client_timeout=(val)
|
124
|
-
parent_process['server_socket_timeout'] = Integer(val)
|
125
|
-
end
|
126
|
-
|
127
|
-
def self.parent_process=(new_config)
|
128
|
-
raise 'Parent process configurations must be a hash!' unless Hash === new_config
|
129
|
-
@@parent_process = PARENT_PROCESS_DEFAULTS.merge(new_config)
|
130
|
-
end
|
131
|
-
|
132
|
-
def self.worker_health_check_config=(new_config)
|
133
|
-
@@worker_health_check_config = (new_config || {}).with_indifferent_access
|
134
|
-
end
|
135
148
|
end
|
136
149
|
end
|
data/lib/delayed/testing.rb
CHANGED
@@ -1,34 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Delayed
|
4
|
-
module Testing
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
module Testing
|
5
|
+
def self.run_job(job)
|
6
|
+
Delayed::Worker.new.perform(job)
|
7
|
+
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
def self.drain
|
10
|
+
while (job = Delayed::Job.get_and_lock_next_available(
|
11
|
+
"spec run_jobs",
|
12
12
|
Delayed::Settings.queue,
|
13
13
|
0,
|
14
|
-
Delayed::MAX_PRIORITY
|
15
|
-
|
14
|
+
Delayed::MAX_PRIORITY
|
15
|
+
))
|
16
|
+
run_job(job)
|
17
|
+
end
|
16
18
|
end
|
17
|
-
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
def self.track_created(&block)
|
21
|
+
job_tracking = JobTracking.track(&block)
|
22
|
+
job_tracking.created
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
Delayed::Job::Failed.delete_all
|
25
|
+
def self.clear_all!
|
26
|
+
case Delayed::Job.name
|
27
|
+
when /ActiveRecord/
|
28
|
+
Delayed::Job.delete_all
|
29
|
+
Delayed::Job::Failed.delete_all
|
30
|
+
end
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
34
|
-
end
|
data/lib/delayed/version.rb
CHANGED
@@ -1,23 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Delayed
|
4
|
-
module WorkQueue
|
5
|
-
# The simplest possible implementation of a WorkQueue -- just turns around and
|
6
|
-
# queries the queue inline.
|
7
|
-
class InProcess
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
4
|
+
module WorkQueue
|
5
|
+
# The simplest possible implementation of a WorkQueue -- just turns around and
|
6
|
+
# queries the queue inline.
|
7
|
+
class InProcess
|
8
|
+
def get_and_lock_next_available(worker_name, worker_config)
|
9
|
+
Delayed::Worker.lifecycle.run_callbacks(:work_queue_pop, self, worker_config) do
|
10
|
+
Delayed::Job.get_and_lock_next_available(
|
11
|
+
worker_name,
|
12
|
+
worker_config[:queue],
|
13
|
+
worker_config[:min_priority],
|
14
|
+
worker_config[:max_priority]
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# intentional nops for compatibility w/ parent process
|
20
|
+
def init; end
|
21
|
+
|
22
|
+
def close; end
|
23
|
+
|
24
|
+
def wake_up; end
|
15
25
|
end
|
16
26
|
end
|
17
|
-
|
18
|
-
# intentional nops for compatibility w/ parent process
|
19
|
-
def close; end
|
20
|
-
def wake_up; end
|
21
|
-
end
|
22
|
-
end
|
23
27
|
end
|
@@ -1,73 +1,75 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Delayed
|
4
|
-
module WorkQueue
|
5
|
-
class ParentProcess
|
6
|
-
|
7
|
-
|
4
|
+
module WorkQueue
|
5
|
+
class ParentProcess
|
6
|
+
class Client
|
7
|
+
attr_reader :addrinfo
|
8
8
|
|
9
|
-
|
9
|
+
include Delayed::Logging
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
11
|
+
def initialize(addrinfo, config: Settings.parent_process)
|
12
|
+
@addrinfo = addrinfo
|
13
|
+
@connect_timeout = config["client_connect_timeout"] || 2
|
14
|
+
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
def init
|
17
|
+
@self_pipe ||= IO.pipe # rubocop:disable Naming/MemoizedInstanceVariableName
|
18
|
+
end
|
20
19
|
|
21
|
-
|
22
|
-
|
20
|
+
def close
|
21
|
+
reset_connection
|
22
|
+
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
24
|
+
def get_and_lock_next_available(worker_name, worker_config)
|
25
|
+
Marshal.dump([worker_name, worker_config], socket)
|
26
|
+
|
27
|
+
# We're assuming there won't ever be a partial write here so we only need
|
28
|
+
# to wait for anything to be available on the 'wire', this is a valid
|
29
|
+
# assumption because we control the server and it's a Unix domain socket,
|
30
|
+
# not TCP.
|
31
|
+
if socket.eof?
|
32
|
+
# Other end closed gracefully, so should we
|
33
|
+
logger.debug("server closed connection")
|
34
|
+
return reset_connection
|
35
|
+
end
|
36
|
+
|
37
|
+
readers, = IO.select([socket, @self_pipe[0]])
|
33
38
|
|
34
|
-
|
39
|
+
if readers.include?(@self_pipe[0])
|
40
|
+
# we're probably exiting so we just want to break out of the blocking read
|
41
|
+
logger.debug("Broke out of select due to being awakened, exiting")
|
42
|
+
else
|
43
|
+
Marshal.load(socket).tap do |response|
|
44
|
+
unless response.nil? || (response.is_a?(Delayed::Job) && response.locked_by == worker_name)
|
45
|
+
raise(ProtocolError, "response is not a locked job: #{response.inspect}")
|
46
|
+
end
|
35
47
|
|
36
|
-
|
37
|
-
|
38
|
-
logger.debug("Broke out of select due to being awakened, exiting")
|
39
|
-
else
|
40
|
-
Marshal.load(socket).tap do |response|
|
41
|
-
unless response.nil? || (response.is_a?(Delayed::Job) && response.locked_by == worker_name)
|
42
|
-
raise(ProtocolError, "response is not a locked job: #{response.inspect}")
|
48
|
+
logger.debug("Received job #{response.id}")
|
49
|
+
end
|
43
50
|
end
|
44
|
-
|
51
|
+
rescue SystemCallError, IOError => e
|
52
|
+
logger.error("Work queue connection lost, reestablishing on next poll. (#{e})")
|
53
|
+
# The work queue process died. Return nil to signal the worker
|
54
|
+
# process should sleep as if no job was found, and then retry.
|
55
|
+
reset_connection
|
45
56
|
end
|
46
|
-
end
|
47
|
-
rescue SystemCallError, IOError => ex
|
48
|
-
logger.error("Work queue connection lost, reestablishing on next poll. (#{ex})")
|
49
|
-
# The work queue process died. Return nil to signal the worker
|
50
|
-
# process should sleep as if no job was found, and then retry.
|
51
|
-
reset_connection
|
52
|
-
end
|
53
57
|
|
54
|
-
|
55
|
-
|
56
|
-
|
58
|
+
def wake_up
|
59
|
+
@self_pipe[1].write_nonblock(".", exception: false)
|
60
|
+
end
|
57
61
|
|
58
|
-
|
62
|
+
private
|
59
63
|
|
60
|
-
|
61
|
-
|
62
|
-
|
64
|
+
def socket
|
65
|
+
@socket ||= @addrinfo.connect(timeout: @connect_timeout)
|
66
|
+
end
|
63
67
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
+
def reset_connection
|
69
|
+
@socket&.close
|
70
|
+
@socket = nil
|
71
|
+
end
|
68
72
|
end
|
69
73
|
end
|
70
74
|
end
|
71
75
|
end
|
72
|
-
end
|
73
|
-
end
|