inst-jobs 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/db/migrate/20101216224513_create_delayed_jobs.rb +9 -7
  3. data/db/migrate/20110531144916_cleanup_delayed_jobs_indexes.rb +8 -13
  4. data/db/migrate/20110610213249_optimize_delayed_jobs.rb +8 -8
  5. data/db/migrate/20110831210257_add_delayed_jobs_next_in_strand.rb +25 -25
  6. data/db/migrate/20120510004759_delayed_jobs_delete_trigger_lock_for_update.rb +4 -8
  7. data/db/migrate/20120531150712_drop_psql_jobs_pop_fn.rb +1 -3
  8. data/db/migrate/20120607164022_delayed_jobs_use_advisory_locks.rb +11 -15
  9. data/db/migrate/20120607181141_index_jobs_on_locked_by.rb +1 -1
  10. data/db/migrate/20120608191051_add_jobs_run_at_index.rb +2 -2
  11. data/db/migrate/20120927184213_change_delayed_jobs_handler_to_text.rb +1 -1
  12. data/db/migrate/20140505215510_copy_failed_jobs_original_id.rb +2 -3
  13. data/db/migrate/20150807133223_add_max_concurrent_to_jobs.rb +9 -13
  14. data/db/migrate/20151210162949_improve_max_concurrent.rb +4 -8
  15. data/db/migrate/20161206323555_add_back_default_string_limits_jobs.rb +3 -2
  16. data/db/migrate/20181217155351_speed_up_max_concurrent_triggers.rb +13 -17
  17. data/db/migrate/20200330230722_add_id_to_get_delayed_jobs_index.rb +8 -8
  18. data/db/migrate/20200824222232_speed_up_max_concurrent_delete_trigger.rb +72 -77
  19. data/db/migrate/20200825011002_add_strand_order_override.rb +93 -97
  20. data/db/migrate/20210809145804_add_n_strand_index.rb +12 -0
  21. data/db/migrate/20210812210128_add_singleton_column.rb +200 -0
  22. data/db/migrate/20210917232626_add_delete_conflicting_singletons_before_unlock_trigger.rb +27 -0
  23. data/db/migrate/20210928174754_fix_singleton_condition_in_before_insert.rb +56 -0
  24. data/db/migrate/20210929204903_update_conflicting_singleton_function_to_use_index.rb +27 -0
  25. data/exe/inst_jobs +3 -2
  26. data/lib/delayed/backend/active_record.rb +211 -168
  27. data/lib/delayed/backend/base.rb +110 -72
  28. data/lib/delayed/batch.rb +11 -9
  29. data/lib/delayed/cli.rb +98 -84
  30. data/lib/delayed/core_ext/kernel.rb +4 -2
  31. data/lib/delayed/daemon.rb +70 -74
  32. data/lib/delayed/job_tracking.rb +26 -25
  33. data/lib/delayed/lifecycle.rb +27 -23
  34. data/lib/delayed/log_tailer.rb +17 -17
  35. data/lib/delayed/logging.rb +13 -16
  36. data/lib/delayed/message_sending.rb +43 -52
  37. data/lib/delayed/performable_method.rb +6 -8
  38. data/lib/delayed/periodic.rb +72 -68
  39. data/lib/delayed/plugin.rb +2 -4
  40. data/lib/delayed/pool.rb +205 -168
  41. data/lib/delayed/server/helpers.rb +6 -6
  42. data/lib/delayed/server.rb +51 -54
  43. data/lib/delayed/settings.rb +94 -81
  44. data/lib/delayed/testing.rb +21 -22
  45. data/lib/delayed/version.rb +1 -1
  46. data/lib/delayed/work_queue/in_process.rb +21 -17
  47. data/lib/delayed/work_queue/parent_process/client.rb +55 -53
  48. data/lib/delayed/work_queue/parent_process/server.rb +245 -207
  49. data/lib/delayed/work_queue/parent_process.rb +52 -53
  50. data/lib/delayed/worker/consul_health_check.rb +32 -33
  51. data/lib/delayed/worker/health_check.rb +34 -26
  52. data/lib/delayed/worker/null_health_check.rb +3 -1
  53. data/lib/delayed/worker/process_helper.rb +8 -9
  54. data/lib/delayed/worker.rb +272 -241
  55. data/lib/delayed/yaml_extensions.rb +12 -10
  56. data/lib/delayed_job.rb +37 -37
  57. data/lib/inst-jobs.rb +1 -1
  58. data/spec/active_record_job_spec.rb +143 -139
  59. data/spec/delayed/cli_spec.rb +7 -7
  60. data/spec/delayed/daemon_spec.rb +10 -9
  61. data/spec/delayed/message_sending_spec.rb +16 -9
  62. data/spec/delayed/periodic_spec.rb +14 -21
  63. data/spec/delayed/server_spec.rb +38 -38
  64. data/spec/delayed/settings_spec.rb +26 -25
  65. data/spec/delayed/work_queue/in_process_spec.rb +7 -8
  66. data/spec/delayed/work_queue/parent_process/client_spec.rb +17 -12
  67. data/spec/delayed/work_queue/parent_process/server_spec.rb +117 -41
  68. data/spec/delayed/work_queue/parent_process_spec.rb +21 -23
  69. data/spec/delayed/worker/consul_health_check_spec.rb +37 -50
  70. data/spec/delayed/worker/health_check_spec.rb +60 -52
  71. data/spec/delayed/worker_spec.rb +44 -21
  72. data/spec/sample_jobs.rb +45 -15
  73. data/spec/shared/delayed_batch.rb +74 -67
  74. data/spec/shared/delayed_method.rb +143 -102
  75. data/spec/shared/performable_method.rb +39 -38
  76. data/spec/shared/shared_backend.rb +550 -437
  77. data/spec/shared/testing.rb +14 -14
  78. data/spec/shared/worker.rb +156 -148
  79. data/spec/shared_jobs_specs.rb +13 -13
  80. data/spec/spec_helper.rb +53 -55
  81. metadata +148 -82
  82. data/lib/delayed/backend/redis/bulk_update.lua +0 -50
  83. data/lib/delayed/backend/redis/destroy_job.lua +0 -2
  84. data/lib/delayed/backend/redis/enqueue.lua +0 -29
  85. data/lib/delayed/backend/redis/fail_job.lua +0 -5
  86. data/lib/delayed/backend/redis/find_available.lua +0 -3
  87. data/lib/delayed/backend/redis/functions.rb +0 -59
  88. data/lib/delayed/backend/redis/get_and_lock_next_available.lua +0 -17
  89. data/lib/delayed/backend/redis/includes/jobs_common.lua +0 -203
  90. data/lib/delayed/backend/redis/job.rb +0 -535
  91. data/lib/delayed/backend/redis/set_running.lua +0 -5
  92. data/lib/delayed/backend/redis/tickle_strand.lua +0 -2
  93. data/spec/gemfiles/42.gemfile +0 -7
  94. data/spec/gemfiles/50.gemfile +0 -7
  95. data/spec/gemfiles/51.gemfile +0 -7
  96. data/spec/gemfiles/52.gemfile +0 -7
  97. data/spec/gemfiles/60.gemfile +0 -7
  98. data/spec/redis_job_spec.rb +0 -148
@@ -1,34 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'sinatra/base'
4
- require 'sinatra/json'
5
- require 'json'
6
- require 'delayed_job'
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, 'server', 'views')
12
- set :public_folder, File.join(APP_DIR, 'server', 'public')
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, &block)
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['DATABASE_URL'])
19
+ ActiveRecord::Base.establish_connection(ENV["DATABASE_URL"])
20
20
  end
21
21
 
22
- @allow_update = args.length > 0 && args[0][:update]
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
- def allow_update
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 'sinatra/reloader'
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 'delayed/server/helpers'
50
+ require "delayed/server/helpers"
53
51
  include Delayed::Server::Helpers
54
52
  end
55
53
 
56
- get '/' do
54
+ get "/" do
57
55
  erb :index
58
56
  end
59
57
 
60
- get '/running' do
58
+ get "/running" do
61
59
  content_type :json
62
60
  json({
63
- draw: params['draw'].to_i,
64
- recordsTotal: Delayed::Job.running.count,
65
- recordsFiltered: Delayed::Job.running.count,
66
- data: Delayed::Job.running_jobs.map{ |j|
67
- j.as_json(include_root: false, except: [:handler, :last_error])
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 '/tags' do
70
+ get "/tags" do
73
71
  content_type :json
74
72
  json({
75
- draw: params['draw'].to_i,
76
- data: Delayed::Job.tag_counts('current', 10)
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 '/jobs' do
80
+ get "/jobs" do
83
81
  content_type :json
84
- flavor = params['flavor'] || 'current'
82
+ flavor = params["flavor"] || "current"
85
83
  page_size = extract_page_size
86
- offset = Integer(params['start'] || 0)
84
+ offset = Integer(params["start"] || 0)
87
85
  case flavor
88
- when 'id'
89
- jobs = Delayed::Job.where(id: params['search_term'])
86
+ when "id"
87
+ jobs = Delayed::Job.where(id: params["search_term"])
90
88
  total_records = 1
91
- when 'future', 'current', 'failed'
89
+ when "future", "current", "failed"
92
90
  jobs = Delayed::Job.list_jobs(flavor, page_size, offset)
93
- total_records = Delayed::Job.jobs_count(flavor)
91
+ total_records = Delayed::Job.jobs_count(flavor)
94
92
  else
95
- query = params['search_term']
96
- if query.present?
97
- jobs = Delayed::Job.list_jobs(flavor, page_size, offset, query)
98
- else
99
- jobs = []
100
- end
101
- total_records = Delayed::Job.jobs_count(flavor, query)
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
- draw: params['draw'].to_i,
105
- recordsTotal: total_records,
106
- recordsFiltered: jobs.size,
107
- data: build_jobs_json(jobs),
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 '/bulk_update' do
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
- success: true
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['length'] || DEFAULT_PAGE_SIZE)
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
- json = jobs.map{ |j|
137
- j.as_json(root: false, except: [:handler, :last_error])
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
@@ -1,16 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yaml'
4
- require 'erb'
5
- require 'active_support/core_ext/hash/indifferent_access'
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
- mattr_accessor(*SETTINGS_WITH_ARGS)
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: 'tmp',
47
+ server_address: "tmp"
53
48
  }.with_indifferent_access.freeze
54
49
 
55
- mattr_reader(:parent_process)
56
- @@parent_process = PARENT_PROCESS_DEFAULTS.dup
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
- def self.queue=(queue_name)
59
- raise(ArgumentError, "queue_name must not be blank") if queue_name.blank?
60
- @@queue = queue_name
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 = ->(strand_name){ nil }
72
- self.default_job_options = ->{ Hash.new }
73
- self.job_detailed_log_format = ->(job){ job.to_json(include_root: false, only: %w(tag strand priority attempts created_at max_attempts source)) }
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
@@ -1,34 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Delayed
4
- module Testing
5
- def self.run_job(job)
6
- Delayed::Worker.new.perform(job)
7
- end
4
+ module Testing
5
+ def self.run_job(job)
6
+ Delayed::Worker.new.perform(job)
7
+ end
8
8
 
9
- def self.drain
10
- while job = Delayed::Job.get_and_lock_next_available(
11
- 'spec run_jobs',
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
- run_job(job)
14
+ Delayed::MAX_PRIORITY
15
+ ))
16
+ run_job(job)
17
+ end
16
18
  end
17
- end
18
19
 
19
- def self.track_created
20
- job_tracking = JobTracking.track { yield }
21
- job_tracking.created
22
- end
20
+ def self.track_created(&block)
21
+ job_tracking = JobTracking.track(&block)
22
+ job_tracking.created
23
+ end
23
24
 
24
- def self.clear_all!
25
- case Delayed::Job.name
26
- when /Redis/
27
- Delayed::Job.redis.flushdb
28
- when /ActiveRecord/
29
- Delayed::Job.delete_all
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Delayed
4
- VERSION = "2.0.0"
4
+ VERSION = "3.0.0"
5
5
  end
@@ -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
- 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])
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
- class Client
7
- attr_reader :addrinfo
4
+ module WorkQueue
5
+ class ParentProcess
6
+ class Client
7
+ attr_reader :addrinfo
8
8
 
9
- include Delayed::Logging
9
+ include Delayed::Logging
10
10
 
11
- def initialize(addrinfo, config: Settings.parent_process)
12
- @addrinfo = addrinfo
13
- @connect_timeout = config['client_connect_timeout'] || 2
14
- @self_pipe = IO.pipe
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
- def close
18
- reset_connection
19
- end
16
+ def init
17
+ @self_pipe ||= IO.pipe # rubocop:disable Naming/MemoizedInstanceVariableName
18
+ end
20
19
 
21
- def get_and_lock_next_available(worker_name, worker_config)
22
- Marshal.dump([worker_name, worker_config], socket)
20
+ def close
21
+ reset_connection
22
+ end
23
23
 
24
- # We're assuming there won't ever be a partial write here so we only need
25
- # to wait for anything to be available on the 'wire', this is a valid
26
- # assumption because we control the server and it's a Unix domain socket,
27
- # not TCP.
28
- if socket.eof?
29
- # Other end closed gracefully, so should we
30
- logger.debug("server closed connection")
31
- return reset_connection
32
- end
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
- readers, _, _ = IO.select([socket, @self_pipe[0]])
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
- if readers.include?(@self_pipe[0])
37
- # we're probably exiting so we just want to break out of the blocking read
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
- logger.debug("Received job #{response.id}")
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
- def wake_up
55
- @self_pipe[1].write_nonblock('.', exception: false)
56
- end
58
+ def wake_up
59
+ @self_pipe[1].write_nonblock(".", exception: false)
60
+ end
57
61
 
58
- private
62
+ private
59
63
 
60
- def socket
61
- @socket ||= @addrinfo.connect(timeout: @connect_timeout)
62
- end
64
+ def socket
65
+ @socket ||= @addrinfo.connect(timeout: @connect_timeout)
66
+ end
63
67
 
64
- def reset_connection
65
- if @socket
66
- @socket.close
67
- @socket = nil
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