delayed_job_master 1.2.0 → 2.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/.gitignore +1 -9
- data/.travis.yml +13 -6
- data/CHANGELOG.md +7 -0
- data/README.md +12 -16
- data/delayed_job_master.gemspec +0 -2
- data/gemfiles/{mongoid.gemfile → rails50.gemfile} +2 -2
- data/gemfiles/rails51.gemfile +7 -0
- data/gemfiles/{active_record.gemfile → rails52.gemfile} +0 -0
- data/gemfiles/rails60.gemfile +6 -0
- data/lib/delayed/master.rb +34 -41
- data/lib/delayed/master/config.rb +16 -5
- data/lib/delayed/master/forker.rb +49 -0
- data/lib/delayed/master/job_checker.rb +98 -0
- data/lib/delayed/master/job_counter.rb +25 -7
- data/lib/delayed/master/monitoring.rb +59 -0
- data/lib/delayed/master/plugins/memory_checker.rb +24 -0
- data/lib/delayed/master/plugins/signal_handler.rb +31 -0
- data/lib/delayed/master/plugins/status_notifier.rb +17 -0
- data/lib/delayed/master/signaler.rb +41 -0
- data/lib/delayed/master/version.rb +1 -1
- data/lib/delayed/master/worker.rb +18 -8
- data/lib/delayed/master/worker_extension.rb +19 -0
- data/lib/delayed_job_master.rb +1 -0
- data/lib/generators/delayed_job_master/templates/config.rb +0 -4
- data/lib/generators/delayed_job_master/templates/script +3 -2
- metadata +15 -27
- data/lib/delayed/master/callback.rb +0 -13
- data/lib/delayed/master/job_counter/active_record.rb +0 -24
- data/lib/delayed/master/job_counter/mongoid.rb +0 -32
- data/lib/delayed/master/signal_handler.rb +0 -47
- data/lib/delayed/master/worker_pool.rb +0 -141
- data/lib/delayed/worker/extension.rb +0 -9
- data/lib/delayed/worker/plugins/memory_checker.rb +0 -20
- data/lib/delayed/worker/plugins/signal_handler.rb +0 -31
- data/lib/delayed/worker/plugins/status_notifier.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90f6ec1ebe00243f5d69adf6a92a6e2716772b7a9af68b7301220bae2cfc55d7
|
4
|
+
data.tar.gz: 30db6bc4a8183622514a9e4fcad3f5130e3061cff0bb973b834e5e0c9db5ffb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6958a025ee232142b6a3049b25ea62075a08dea1446ddc0b29ee3f26d5ef1dd9351e6df46a2ad2a8d8068238e44a84bce6140639ff62aa8a0c8f2ad0ba43c2fe
|
7
|
+
data.tar.gz: c863131bde412c128fc3f144fd5ba1bb472cd8850a0c31a32d0500e043572e389ce7184c6a98a0b57de31bd5c434601c5641c64ea00b7a7db1a41bd11095f4dc
|
data/.gitignore
CHANGED
@@ -1,19 +1,11 @@
|
|
1
1
|
/.bundle/
|
2
|
-
/.yardoc
|
3
2
|
/.project
|
4
3
|
/Gemfile.lock
|
5
|
-
/gemfiles/.bundle
|
6
|
-
/gemfiles/vendor
|
7
4
|
/gemfiles/*gemfile.lock
|
8
|
-
/_yardoc/
|
9
|
-
/bin/
|
10
5
|
/coverage/
|
11
|
-
/doc/
|
12
6
|
/pkg/
|
13
|
-
/log/
|
14
|
-
/tmp/
|
15
7
|
/spec/reports/
|
16
8
|
/spec/**/db/*.sqlite3
|
17
9
|
/spec/**/log/*.log
|
18
|
-
/spec/**/tmp
|
10
|
+
/spec/**/tmp/*
|
19
11
|
/vendor/
|
data/.travis.yml
CHANGED
@@ -1,16 +1,23 @@
|
|
1
1
|
language: ruby
|
2
|
-
services:
|
3
|
-
- mongodb
|
4
2
|
rvm:
|
5
3
|
- 2.3
|
6
4
|
- 2.4
|
7
5
|
- 2.5
|
8
6
|
- 2.6
|
9
7
|
gemfile:
|
10
|
-
- gemfiles/
|
11
|
-
- gemfiles/
|
8
|
+
- gemfiles/rails50.gemfile
|
9
|
+
- gemfiles/rails51.gemfile
|
10
|
+
- gemfiles/rails52.gemfile
|
11
|
+
- gemfiles/rails60.gemfile
|
12
|
+
matrix:
|
13
|
+
exclude:
|
14
|
+
- rvm: 2.3
|
15
|
+
gemfile: gemfiles/rails60.gemfile
|
16
|
+
- rvm: 2.4
|
17
|
+
gemfile: gemfiles/rails60.gemfile
|
12
18
|
before_script:
|
13
19
|
- cd spec/dummy
|
14
|
-
- bundle exec rake
|
20
|
+
- bundle exec rake dbs:migrate RAILS_ENV=test
|
15
21
|
- cd ../..
|
16
|
-
script:
|
22
|
+
script:
|
23
|
+
- bundle exec rspec
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -5,11 +5,9 @@ A simple delayed_job master process to control multiple workers.
|
|
5
5
|
## Features
|
6
6
|
|
7
7
|
* Preload application and fork workers fastly.
|
8
|
-
* Monitor
|
9
|
-
*
|
10
|
-
*
|
11
|
-
* Trap USR2 signal to restart master and workers.
|
12
|
-
* Auto-scale worker processes.
|
8
|
+
* Monitor job queues and fork new workers on demand.
|
9
|
+
* Trap signals to restart / reopen log files.
|
10
|
+
* Support multiple databases.
|
13
11
|
|
14
12
|
## Dependencies
|
15
13
|
|
@@ -19,7 +17,6 @@ A simple delayed_job master process to control multiple workers.
|
|
19
17
|
## Supported delayed_job backends
|
20
18
|
|
21
19
|
* delayed_job_active_record 4.1
|
22
|
-
* delayed_job_mongoid 2.3
|
23
20
|
|
24
21
|
## Installation
|
25
22
|
|
@@ -33,11 +30,11 @@ And then execute:
|
|
33
30
|
|
34
31
|
$ bundle
|
35
32
|
|
36
|
-
|
33
|
+
## Generate files
|
37
34
|
|
38
|
-
|
35
|
+
Generate `bin/delayed_job_master` and `config/delayed_job_master.rb`:
|
39
36
|
|
40
|
-
|
37
|
+
$ rails generate delayed_job_master:config
|
41
38
|
|
42
39
|
## Configuration
|
43
40
|
|
@@ -59,14 +56,14 @@ log_file "#{Dir.pwd}/log/delayed_job_master.log"
|
|
59
56
|
# log level
|
60
57
|
log_level :info
|
61
58
|
|
59
|
+
# databases for checking queued jobs if you have multiple databases
|
60
|
+
# databases [:production]
|
61
|
+
|
62
62
|
# worker1
|
63
63
|
add_worker do |worker|
|
64
64
|
# queue name for the worker
|
65
65
|
worker.queues %w(queue1)
|
66
66
|
|
67
|
-
# worker control (:static or :dynamic)
|
68
|
-
worker.control :static
|
69
|
-
|
70
67
|
# worker count
|
71
68
|
worker.count 1
|
72
69
|
|
@@ -86,7 +83,6 @@ end
|
|
86
83
|
# worker2
|
87
84
|
add_worker do |worker|
|
88
85
|
worker.queues %w(queue2)
|
89
|
-
worker.control :dynamic
|
90
86
|
worker.count 2
|
91
87
|
end
|
92
88
|
|
@@ -146,19 +142,19 @@ Workers handle each signal as follows:
|
|
146
142
|
|
147
143
|
```
|
148
144
|
$ ps aux
|
149
|
-
... delayed_job.0 (queue1) [BUSY] # BUSY process is currently proceeding some jobs
|
145
|
+
... delayed_job.0: worker[0] (queue1) [BUSY] # BUSY process is currently proceeding some jobs
|
150
146
|
```
|
151
147
|
|
152
148
|
After graceful restart, you may find OLD process.
|
153
149
|
|
154
150
|
```
|
155
151
|
$ ps aux
|
156
|
-
... delayed_job.0 (queue1) [BUSY] [OLD] # OLD process will stop after finishing current jobs.
|
152
|
+
... delayed_job.0: worker[0] (queue1) [BUSY] [OLD] # OLD process will stop after finishing current jobs.
|
157
153
|
```
|
158
154
|
|
159
155
|
## Contributing
|
160
156
|
|
161
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/kanety/delayed_job_master.
|
157
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/kanety/delayed_job_master.
|
162
158
|
|
163
159
|
## License
|
164
160
|
|
data/delayed_job_master.gemspec
CHANGED
@@ -8,7 +8,6 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Delayed::Master::VERSION
|
9
9
|
spec.authors = ["Yoshikazu Kaneta"]
|
10
10
|
spec.email = ["kaneta@sitebridge.co.jp"]
|
11
|
-
|
12
11
|
spec.summary = %q{A simple delayed_job master process to control multiple workers}
|
13
12
|
spec.description = %q{A simple delayed_job master process to control multiple workers}
|
14
13
|
spec.homepage = "https://github.com/kanety/delayed_job_master"
|
@@ -29,6 +28,5 @@ Gem::Specification.new do |spec|
|
|
29
28
|
spec.add_development_dependency "rspec-rails"
|
30
29
|
spec.add_development_dependency "simplecov"
|
31
30
|
spec.add_development_dependency "sqlite3"
|
32
|
-
spec.add_development_dependency "delayed_job_mongoid", ">= 2.3"
|
33
31
|
spec.add_development_dependency "delayed_job_active_record", ">= 4.1"
|
34
32
|
end
|
File without changes
|
data/lib/delayed/master.rb
CHANGED
@@ -2,10 +2,9 @@ require 'fileutils'
|
|
2
2
|
require 'logger'
|
3
3
|
require_relative 'master/version'
|
4
4
|
require_relative 'master/command'
|
5
|
-
require_relative 'master/callback'
|
6
5
|
require_relative 'master/worker'
|
7
|
-
require_relative 'master/
|
8
|
-
require_relative 'master/
|
6
|
+
require_relative 'master/monitoring'
|
7
|
+
require_relative 'master/signaler'
|
9
8
|
require_relative 'master/util/file_reopener'
|
10
9
|
|
11
10
|
module Delayed
|
@@ -17,79 +16,74 @@ module Delayed
|
|
17
16
|
@logger = setup_logger(@config.log_file, @config.log_level)
|
18
17
|
@workers = []
|
19
18
|
|
20
|
-
@
|
21
|
-
@
|
19
|
+
@signaler = Signaler.new(self)
|
20
|
+
@monitoring = Monitoring.new(self)
|
22
21
|
end
|
23
22
|
|
24
23
|
def run
|
25
|
-
|
26
|
-
show_config
|
24
|
+
print_config
|
27
25
|
daemonize if @config.daemon
|
28
26
|
|
29
|
-
|
30
|
-
@logger.info "started master #{Process.pid}"
|
27
|
+
@logger.info "started master #{Process.pid}".tap { |msg| puts msg }
|
31
28
|
|
32
|
-
|
33
|
-
|
34
|
-
|
29
|
+
handle_pid_file do
|
30
|
+
@signaler.register
|
31
|
+
@prepared = true
|
32
|
+
@monitoring.monitor_while { stop? }
|
33
|
+
end
|
35
34
|
|
36
|
-
remove_pid_file
|
37
35
|
@logger.info "shut down master"
|
38
36
|
end
|
39
37
|
|
40
|
-
def load_app
|
41
|
-
require File.join(@config.working_directory, 'config', 'environment')
|
42
|
-
require_relative 'master/job_counter'
|
43
|
-
require_relative 'worker/extension'
|
44
|
-
end
|
45
|
-
|
46
38
|
def prepared?
|
47
|
-
@
|
39
|
+
@prepared
|
48
40
|
end
|
49
41
|
|
50
42
|
def quit
|
51
|
-
|
43
|
+
@signaler.dispatch(:KILL)
|
52
44
|
@stop = true
|
53
45
|
end
|
54
46
|
|
55
47
|
def stop
|
56
|
-
@
|
48
|
+
@signaler.dispatch(:TERM)
|
57
49
|
@stop = true
|
58
50
|
end
|
59
51
|
|
60
52
|
def stop?
|
61
|
-
@stop
|
53
|
+
@stop == true
|
62
54
|
end
|
63
55
|
|
64
56
|
def reopen_files
|
65
|
-
@
|
57
|
+
@signaler.dispatch(:USR1)
|
66
58
|
@logger.info "reopening files..."
|
67
59
|
Util::FileReopener.reopen
|
68
60
|
@logger.info "reopened"
|
69
61
|
end
|
70
62
|
|
71
63
|
def restart
|
72
|
-
@
|
64
|
+
@signaler.dispatch(:USR2)
|
73
65
|
@logger.info "restarting master..."
|
74
66
|
exec(*([$0] + ARGV))
|
75
67
|
end
|
76
68
|
|
77
|
-
def kill_workers
|
78
|
-
@signal_handler.dispatch('KILL')
|
79
|
-
end
|
80
|
-
|
81
|
-
def wait_workers
|
82
|
-
Process.waitall
|
83
|
-
end
|
84
|
-
|
85
69
|
private
|
86
70
|
|
87
71
|
def setup_logger(log_file, log_level)
|
88
|
-
FileUtils.mkdir_p(File.dirname(log_file))
|
72
|
+
FileUtils.mkdir_p(File.dirname(log_file)) if log_file.is_a?(String)
|
89
73
|
logger = Logger.new(log_file)
|
90
74
|
logger.level = log_level
|
91
75
|
logger
|
92
76
|
end
|
77
|
+
|
78
|
+
def daemonize
|
79
|
+
Process.daemon(true)
|
80
|
+
end
|
81
|
+
|
82
|
+
def handle_pid_file
|
83
|
+
create_pid_file
|
84
|
+
yield
|
85
|
+
remove_pid_file
|
86
|
+
end
|
93
87
|
|
94
88
|
def create_pid_file
|
95
89
|
FileUtils.mkdir_p(File.dirname(@config.pid_file))
|
@@ -100,13 +94,12 @@ module Delayed
|
|
100
94
|
File.delete(@config.pid_file) if File.exist?(@config.pid_file)
|
101
95
|
end
|
102
96
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
puts "#{setting.count} worker for '#{setting.queues.join(',')}' under #{setting.control} control"
|
97
|
+
def print_config
|
98
|
+
@logger.info "databases: #{@config.databases.join(', ')}" if @config.databases
|
99
|
+
@config.worker_settings.each do |setting|
|
100
|
+
message = "worker[#{setting.id}]: #{setting.count} processes"
|
101
|
+
message << " (#{setting.queues.join(', ')})" if setting.queues.respond_to?(:join)
|
102
|
+
@logger.info message
|
110
103
|
end
|
111
104
|
end
|
112
105
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Delayed
|
2
2
|
class Master
|
3
3
|
class Config
|
4
|
-
SIMPLE_CONFIGS = [:working_directory, :log_file, :log_level, :pid_file, :monitor_wait, :daemon]
|
4
|
+
SIMPLE_CONFIGS = [:working_directory, :log_file, :log_level, :pid_file, :monitor_wait, :daemon, :databases]
|
5
5
|
CALLBACK_CONFIGS = [:before_fork, :after_fork, :before_monitor, :after_monitor]
|
6
6
|
|
7
7
|
attr_reader :data, :workers
|
@@ -12,14 +12,17 @@ module Delayed
|
|
12
12
|
read(file) if file
|
13
13
|
end
|
14
14
|
|
15
|
+
def worker_settings
|
16
|
+
@workers
|
17
|
+
end
|
18
|
+
|
15
19
|
def read(file)
|
16
|
-
instance_eval(File.read(file))
|
20
|
+
instance_eval(File.read(file), file)
|
17
21
|
end
|
18
22
|
|
19
23
|
def add_worker
|
20
|
-
worker = WorkerSetting.new(
|
24
|
+
worker = WorkerSetting.new(id: @workers.size, count: 1, exit_on_complete: true)
|
21
25
|
yield worker
|
22
|
-
worker.exit_on_complete(true) if worker.control == :dynamic
|
23
26
|
@workers << worker
|
24
27
|
end
|
25
28
|
|
@@ -27,6 +30,10 @@ module Delayed
|
|
27
30
|
@data.select { |k, _| CALLBACK_CONFIGS.include?(k) }
|
28
31
|
end
|
29
32
|
|
33
|
+
def run_callback(key, *args)
|
34
|
+
@data[key].call(*args)
|
35
|
+
end
|
36
|
+
|
30
37
|
SIMPLE_CONFIGS.each do |key|
|
31
38
|
define_method(key) do |value = nil|
|
32
39
|
if value
|
@@ -48,7 +55,7 @@ module Delayed
|
|
48
55
|
end
|
49
56
|
|
50
57
|
class WorkerSetting
|
51
|
-
SIMPLE_CONFIGS = [:
|
58
|
+
SIMPLE_CONFIGS = [:id, :count, :max_memory,
|
52
59
|
:min_priority, :max_priority, :sleep_delay, :read_ahead, :exit_on_complete,
|
53
60
|
:max_attempts, :max_run_time, :destroy_failed_jobs]
|
54
61
|
ARRAY_CONFIGS = [:queues]
|
@@ -59,6 +66,10 @@ module Delayed
|
|
59
66
|
@data = default
|
60
67
|
end
|
61
68
|
|
69
|
+
def control(value = nil)
|
70
|
+
puts "DEPRECATION WARNING: deprecated control setting is called from #{caller(1, 1).first}. Remove it from your config file."
|
71
|
+
end
|
72
|
+
|
62
73
|
SIMPLE_CONFIGS.each do |key|
|
63
74
|
define_method(key) do |value = nil|
|
64
75
|
if value
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Delayed
|
2
|
+
class Master
|
3
|
+
class Forker
|
4
|
+
def initialize(master)
|
5
|
+
@master = master
|
6
|
+
@config = master.config
|
7
|
+
end
|
8
|
+
|
9
|
+
def new_worker(worker)
|
10
|
+
@master.logger.info "forking #{worker.name}..."
|
11
|
+
fork_worker(worker)
|
12
|
+
@master.logger.info "forked #{worker.name} with pid #{worker.pid}"
|
13
|
+
|
14
|
+
@master.workers << worker
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def fork_worker(worker)
|
20
|
+
@config.run_callback(:before_fork, @master, worker)
|
21
|
+
worker.pid = fork do
|
22
|
+
worker.pid = Process.pid
|
23
|
+
worker.instance = create_instance(worker)
|
24
|
+
@config.run_callback(:after_fork, @master, worker)
|
25
|
+
$0 = worker.process_title
|
26
|
+
worker.instance.start
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_instance(worker)
|
31
|
+
require_relative 'worker_extension'
|
32
|
+
|
33
|
+
instance = Delayed::Worker.new(worker.setting.data)
|
34
|
+
[:max_run_time, :max_attempts, :destroy_failed_jobs].each do |key|
|
35
|
+
if (value = worker.setting.send(key))
|
36
|
+
Delayed::Worker.send("#{key}=", value)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
[:max_memory].each do |key|
|
40
|
+
if (value = worker.setting.send(key))
|
41
|
+
instance.send("#{key}=", value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
instance.master_logger = @master.logger
|
45
|
+
instance
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require_relative 'job_counter'
|
2
|
+
|
3
|
+
module Delayed
|
4
|
+
class Master
|
5
|
+
class JobChecker
|
6
|
+
def initialize(master)
|
7
|
+
@master = master
|
8
|
+
@config = master.config
|
9
|
+
@spec_names = target_spec_names
|
10
|
+
|
11
|
+
define_models
|
12
|
+
extend_after_fork_callback
|
13
|
+
end
|
14
|
+
|
15
|
+
def check
|
16
|
+
workers = []
|
17
|
+
mon = Monitor.new
|
18
|
+
|
19
|
+
threads = @spec_names.map do |spec_name|
|
20
|
+
Thread.new(spec_name) do |spec_name|
|
21
|
+
find_jobs_in_db(spec_name) do |setting|
|
22
|
+
mon.synchronize do
|
23
|
+
workers << Worker.new(index: @master.workers.size + workers.size, database: spec_name, setting: setting)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
threads.each(&:join)
|
30
|
+
|
31
|
+
workers
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def define_models
|
37
|
+
@spec_names.each do |spec_name|
|
38
|
+
klass = Class.new(Delayed::Job)
|
39
|
+
klass_name = "DelayedJob#{spec_name.capitalize}"
|
40
|
+
unless Delayed::Master.const_defined?(klass_name)
|
41
|
+
Delayed::Master.const_set(klass_name, klass)
|
42
|
+
Delayed::Master.const_get(klass_name).establish_connection(spec_name)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def model_for(spec_name)
|
48
|
+
Delayed::Master.const_get("DelayedJob#{spec_name.capitalize}")
|
49
|
+
end
|
50
|
+
|
51
|
+
def extend_after_fork_callback
|
52
|
+
prc = @config.after_fork
|
53
|
+
@config.after_fork do |master, worker|
|
54
|
+
prc.call(master, worker)
|
55
|
+
if worker.database && ActiveRecord::Base.connection_pool.spec.name != worker.database.to_s
|
56
|
+
ActiveRecord::Base.establish_connection(worker.database)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def target_spec_names
|
62
|
+
if @config.databases.nil? || @config.databases.empty?
|
63
|
+
load_spec_names.select { |spec_name| has_delayed_job_table?(spec_name) }
|
64
|
+
else
|
65
|
+
@config.databases
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def load_spec_names
|
70
|
+
if Rails::VERSION::MAJOR >= 6
|
71
|
+
configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
|
72
|
+
configs.reject(&:replica?).map { |c| c.spec_name.to_sym }
|
73
|
+
else
|
74
|
+
[Rails.env.to_sym]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def has_delayed_job_table?(spec_name)
|
79
|
+
ActiveRecord::Base.establish_connection(spec_name)
|
80
|
+
ActiveRecord::Base.connection.tables.include?('delayed_jobs')
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_jobs_in_db(spec_name)
|
84
|
+
counter = JobCounter.new(model_for(spec_name))
|
85
|
+
|
86
|
+
@config.worker_settings.each do |setting|
|
87
|
+
count = @master.workers.count { |worker| worker.setting.queues == setting.queues }
|
88
|
+
slot = setting.count - count
|
89
|
+
if slot > 0 && (job_count = counter.count(setting)) > 0
|
90
|
+
[slot, job_count].min.times do
|
91
|
+
yield setting
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -1,8 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
# JobCounter depends on delayed_job_active_record.
|
2
|
+
# See https://github.com/collectiveidea/delayed_job_active_record/blob/master/lib/delayed/backend/active_record.rb
|
3
|
+
module Delayed
|
4
|
+
class Master
|
5
|
+
class JobCounter
|
6
|
+
def initialize(klass)
|
7
|
+
@klass = klass
|
8
|
+
end
|
9
|
+
|
10
|
+
def count(setting)
|
11
|
+
jobs = ready_to_run(setting.max_run_time || Delayed::Worker::DEFAULT_MAX_RUN_TIME)
|
12
|
+
jobs.where!("priority >= ?", setting.min_priority) if setting.min_priority
|
13
|
+
jobs.where!("priority <= ?", setting.max_priority) if setting.max_priority
|
14
|
+
jobs.where!(queue: setting.queues) if setting.queues.any?
|
15
|
+
jobs.count
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def ready_to_run(max_run_time)
|
21
|
+
db_time_now = @klass.db_time_now
|
22
|
+
@klass.where("(run_at <= ? AND (locked_at IS NULL OR locked_at < ?)) AND failed_at IS NULL", db_time_now, db_time_now - max_run_time)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
8
26
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative 'forker'
|
2
|
+
require_relative 'job_checker' if defined?(Delayed::Backend::ActiveRecord)
|
3
|
+
|
4
|
+
module Delayed
|
5
|
+
class Master
|
6
|
+
class Monitoring
|
7
|
+
def initialize(master)
|
8
|
+
@master = master
|
9
|
+
@config = master.config
|
10
|
+
@forker = Forker.new(master)
|
11
|
+
@job_checker = JobChecker.new(master)
|
12
|
+
end
|
13
|
+
|
14
|
+
def monitor_while(&block)
|
15
|
+
loop do
|
16
|
+
break if block.call
|
17
|
+
monitor do
|
18
|
+
check_terminated
|
19
|
+
check_queued_jobs
|
20
|
+
end
|
21
|
+
sleep @config.monitor_wait.to_i
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def monitor
|
28
|
+
@config.run_callback(:before_monitor, @master)
|
29
|
+
yield
|
30
|
+
@config.run_callback(:after_monitor, @master)
|
31
|
+
rescue Exception => e
|
32
|
+
@master.logger.warn "#{e.class}: #{e.message} at #{__FILE__}: #{__LINE__}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_terminated
|
36
|
+
if (pid = terminated_pid)
|
37
|
+
@master.logger.debug "found terminated pid: #{pid}"
|
38
|
+
@master.workers.reject! { |worker| worker.pid == pid }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def terminated_pid
|
43
|
+
Process.waitpid(-1, Process::WNOHANG)
|
44
|
+
rescue Errno::ECHILD
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def check_queued_jobs
|
49
|
+
@master.logger.debug "checking jobs..."
|
50
|
+
|
51
|
+
new_workers = @job_checker.check
|
52
|
+
new_workers.each do |worker|
|
53
|
+
@master.logger.info "found jobs for #{worker.info}"
|
54
|
+
@forker.new_worker(worker)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'get_process_mem'
|
2
|
+
|
3
|
+
module Delayed
|
4
|
+
class Master
|
5
|
+
module Plugins
|
6
|
+
class MemoryChecker < Delayed::Plugin
|
7
|
+
callbacks do |lifecycle|
|
8
|
+
lifecycle.before(:perform) do |worker, job|
|
9
|
+
mem = GetProcessMem.new
|
10
|
+
worker.master_logger.info "performing #{job.name}, memory: #{mem.mb.to_i} MB"
|
11
|
+
end
|
12
|
+
lifecycle.after(:perform) do |worker, job|
|
13
|
+
mem = GetProcessMem.new
|
14
|
+
worker.master_logger.info "performed #{job.name}, memory: #{mem.mb.to_i} MB"
|
15
|
+
if worker.max_memory && mem.mb > worker.max_memory
|
16
|
+
worker.master_logger.info "shutting down worker #{Process.pid} because it consumes large memory..."
|
17
|
+
worker.stop
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Delayed
|
2
|
+
class Master
|
3
|
+
module Plugins
|
4
|
+
class SignalHandler < Delayed::Plugin
|
5
|
+
callbacks do |lifecycle|
|
6
|
+
lifecycle.before(:execute) do |worker|
|
7
|
+
worker.instance_eval do
|
8
|
+
trap(:USR1) do
|
9
|
+
Thread.new do
|
10
|
+
master_logger.info "reopening files..."
|
11
|
+
Delayed::Master::Util::FileReopener.reopen
|
12
|
+
master_logger.info "reopened"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
trap(:USR2) do
|
16
|
+
Thread.new do
|
17
|
+
$0 = "#{$0} [OLD]"
|
18
|
+
master_logger.info "shutting down worker #{Process.pid}..."
|
19
|
+
stop
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
lifecycle.after(:execute) do |worker|
|
25
|
+
worker.master_logger.info "shut down worker #{Process.pid}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Delayed
|
2
|
+
class Master
|
3
|
+
module Plugins
|
4
|
+
class StatusNotifier < Delayed::Plugin
|
5
|
+
callbacks do |lifecycle|
|
6
|
+
lifecycle.around(:perform) do |worker, job, &block|
|
7
|
+
title = $0
|
8
|
+
$0 = "#{title} [BUSY]"
|
9
|
+
ret = block.call
|
10
|
+
$0 = title
|
11
|
+
ret
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Delayed
|
2
|
+
class Master
|
3
|
+
class Signaler
|
4
|
+
def initialize(master)
|
5
|
+
@master = master
|
6
|
+
end
|
7
|
+
|
8
|
+
def register
|
9
|
+
signals = [[:TERM, :stop], [:INT, :stop], [:QUIT, :quit], [:USR1, :reopen_files], [:USR2, :restart]]
|
10
|
+
signals.each do |signal, method|
|
11
|
+
register_signal(signal, method)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def dispatch(signal)
|
16
|
+
@master.workers.each do |worker|
|
17
|
+
next unless worker.pid
|
18
|
+
dispatch_to(signal, worker.pid)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def register_signal(signal, method)
|
25
|
+
trap(signal) do
|
26
|
+
Thread.new do
|
27
|
+
@master.logger.info "received #{signal} signal"
|
28
|
+
@master.public_send(method)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def dispatch_to(signal, pid)
|
34
|
+
Process.kill(signal, pid)
|
35
|
+
@master.logger.info "sent #{signal} signal to worker #{pid}"
|
36
|
+
rescue
|
37
|
+
@master.logger.error "failed to send #{signal} signal to worker #{pid}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,18 +1,28 @@
|
|
1
1
|
module Delayed
|
2
2
|
class Master
|
3
3
|
class Worker
|
4
|
-
|
4
|
+
attr_accessor :index, :setting, :database
|
5
5
|
attr_accessor :pid, :instance
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
|
9
|
-
|
7
|
+
def initialize(attrs = {})
|
8
|
+
attrs.each do |k, v|
|
9
|
+
send("#{k}=", v)
|
10
|
+
end
|
10
11
|
end
|
11
12
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
def name
|
14
|
+
"worker[#{@setting.id}]"
|
15
|
+
end
|
16
|
+
|
17
|
+
def info
|
18
|
+
str = name
|
19
|
+
str << " @#{@database}" if @database
|
20
|
+
str << " (#{@setting.queues.join(', ')})" if @setting.queues.respond_to?(:join)
|
21
|
+
str
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_title
|
25
|
+
"delayed_job.#{@index}: #{info}"
|
16
26
|
end
|
17
27
|
end
|
18
28
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Delayed
|
2
|
+
class Worker
|
3
|
+
attr_accessor :master_logger, :max_memory
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
require_relative 'plugins/memory_checker'
|
8
|
+
require_relative 'plugins/signal_handler'
|
9
|
+
require_relative 'plugins/status_notifier'
|
10
|
+
|
11
|
+
[
|
12
|
+
Delayed::Master::Plugins::MemoryChecker,
|
13
|
+
Delayed::Master::Plugins::SignalHandler,
|
14
|
+
Delayed::Master::Plugins::StatusNotifier
|
15
|
+
].each do |plugin|
|
16
|
+
unless Delayed::Worker.plugins.include?(plugin)
|
17
|
+
Delayed::Worker.plugins << plugin
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'delayed/master'
|
@@ -18,9 +18,6 @@ add_worker do |worker|
|
|
18
18
|
# queue name for the worker
|
19
19
|
worker.queues %w(queue1)
|
20
20
|
|
21
|
-
# worker control (:static or :dynamic)
|
22
|
-
worker.control :static
|
23
|
-
|
24
21
|
# worker count
|
25
22
|
worker.count 1
|
26
23
|
|
@@ -40,7 +37,6 @@ end
|
|
40
37
|
# worker2
|
41
38
|
add_worker do |worker|
|
42
39
|
worker.queues %w(queue2)
|
43
|
-
worker.control :dynamic
|
44
40
|
worker.count 2
|
45
41
|
end
|
46
42
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delayed_job_master
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yoshikazu Kaneta
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: delayed_job
|
@@ -108,20 +108,6 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: delayed_job_mongoid
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '2.3'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '2.3'
|
125
111
|
- !ruby/object:Gem::Dependency
|
126
112
|
name: delayed_job_active_record
|
127
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -153,24 +139,26 @@ files:
|
|
153
139
|
- README.md
|
154
140
|
- Rakefile
|
155
141
|
- delayed_job_master.gemspec
|
156
|
-
- gemfiles/
|
157
|
-
- gemfiles/
|
142
|
+
- gemfiles/rails50.gemfile
|
143
|
+
- gemfiles/rails51.gemfile
|
144
|
+
- gemfiles/rails52.gemfile
|
145
|
+
- gemfiles/rails60.gemfile
|
158
146
|
- lib/delayed/master.rb
|
159
|
-
- lib/delayed/master/callback.rb
|
160
147
|
- lib/delayed/master/command.rb
|
161
148
|
- lib/delayed/master/config.rb
|
149
|
+
- lib/delayed/master/forker.rb
|
150
|
+
- lib/delayed/master/job_checker.rb
|
162
151
|
- lib/delayed/master/job_counter.rb
|
163
|
-
- lib/delayed/master/
|
164
|
-
- lib/delayed/master/
|
165
|
-
- lib/delayed/master/signal_handler.rb
|
152
|
+
- lib/delayed/master/monitoring.rb
|
153
|
+
- lib/delayed/master/plugins/memory_checker.rb
|
154
|
+
- lib/delayed/master/plugins/signal_handler.rb
|
155
|
+
- lib/delayed/master/plugins/status_notifier.rb
|
156
|
+
- lib/delayed/master/signaler.rb
|
166
157
|
- lib/delayed/master/util/file_reopener.rb
|
167
158
|
- lib/delayed/master/version.rb
|
168
159
|
- lib/delayed/master/worker.rb
|
169
|
-
- lib/delayed/master/
|
170
|
-
- lib/
|
171
|
-
- lib/delayed/worker/plugins/memory_checker.rb
|
172
|
-
- lib/delayed/worker/plugins/signal_handler.rb
|
173
|
-
- lib/delayed/worker/plugins/status_notifier.rb
|
160
|
+
- lib/delayed/master/worker_extension.rb
|
161
|
+
- lib/delayed_job_master.rb
|
174
162
|
- lib/generators/delayed_job_master/config_generator.rb
|
175
163
|
- lib/generators/delayed_job_master/templates/config.rb
|
176
164
|
- lib/generators/delayed_job_master/templates/script
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# JobCounter depends on delayed_job_active_record.
|
2
|
-
# See https://github.com/collectiveidea/delayed_job_active_record/blob/master/lib/delayed/backend/active_record.rb
|
3
|
-
module Delayed
|
4
|
-
class Master
|
5
|
-
class JobCounter
|
6
|
-
class << self
|
7
|
-
def count(setting)
|
8
|
-
jobs = ready_to_run(setting.max_run_time || Delayed::Worker::DEFAULT_MAX_RUN_TIME)
|
9
|
-
jobs.where!("priority >= ?", setting.min_priority) if setting.min_priority
|
10
|
-
jobs.where!("priority <= ?", setting.max_priority) if setting.max_priority
|
11
|
-
jobs.where!(queue: setting.queues) if setting.queues.any?
|
12
|
-
jobs.count
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def ready_to_run(max_run_time)
|
18
|
-
db_time_now = Delayed::Job.db_time_now
|
19
|
-
Delayed::Job.where("(run_at <= ? AND (locked_at IS NULL OR locked_at < ?)) AND failed_at IS NULL", db_time_now, db_time_now - max_run_time)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# JobCounter depends on delayed_job_mongoid.
|
2
|
-
# See https://github.com/collectiveidea/delayed_job_mongoid/blob/master/lib/delayed/backend/mongoid.rb
|
3
|
-
module Delayed
|
4
|
-
class Master
|
5
|
-
class JobCounter
|
6
|
-
class << self
|
7
|
-
def count(setting)
|
8
|
-
right_now = Delayed::Job.db_time_now
|
9
|
-
jobs = reservation_criteria(right_now, setting.max_run_time || Delayed::Worker::DEFAULT_MAX_RUN_TIME)
|
10
|
-
jobs = jobs.gte(priority: setting.min_priority.to_i) if setting.min_priority
|
11
|
-
jobs = jobs.lte(priority: setting.max_priority.to_i) if setting.max_priority
|
12
|
-
jobs = jobs.any_in(queue: setting.queues) if setting.queues.any?
|
13
|
-
jobs.count
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def reservation_criteria(right_now, max_run_time)
|
19
|
-
criteria = Delayed::Job.where(
|
20
|
-
run_at: { '$lte' => right_now },
|
21
|
-
failed_at: nil
|
22
|
-
).any_of(
|
23
|
-
{ locked_at: nil },
|
24
|
-
locked_at: { '$lt' => (right_now - max_run_time) }
|
25
|
-
)
|
26
|
-
|
27
|
-
criteria
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
module Delayed
|
2
|
-
class Master
|
3
|
-
class SignalHandler
|
4
|
-
def initialize(master)
|
5
|
-
@master = master
|
6
|
-
@logger = master.logger
|
7
|
-
@workers = master.workers
|
8
|
-
end
|
9
|
-
|
10
|
-
def register
|
11
|
-
%w(TERM INT QUIT USR1 USR2).each do |signal|
|
12
|
-
trap(signal) do
|
13
|
-
Thread.new do
|
14
|
-
@logger.info "received #{signal} signal"
|
15
|
-
case signal
|
16
|
-
when 'TERM', 'INT'
|
17
|
-
@master.stop
|
18
|
-
when 'QUIT'
|
19
|
-
@master.quit
|
20
|
-
when 'USR1'
|
21
|
-
@master.reopen_files
|
22
|
-
when 'USR2'
|
23
|
-
@master.restart
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def dispatch(signal)
|
31
|
-
@workers.each do |worker|
|
32
|
-
next unless worker.pid
|
33
|
-
dispatch_to(signal, worker.pid)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def dispatch_to(signal, pid)
|
40
|
-
Process.kill signal, pid
|
41
|
-
@logger.info "sent #{signal} signal to worker #{pid}"
|
42
|
-
rescue
|
43
|
-
@logger.error "failed to send #{signal} signal to worker #{pid}"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,141 +0,0 @@
|
|
1
|
-
module Delayed
|
2
|
-
class Master
|
3
|
-
class WorkerPool
|
4
|
-
def initialize(master, config)
|
5
|
-
@master = master
|
6
|
-
@config = config
|
7
|
-
|
8
|
-
@logger = master.logger
|
9
|
-
@workers = master.workers
|
10
|
-
|
11
|
-
@static_settings, @dynamic_settings = config.workers.partition { |conf| conf.control == :static }
|
12
|
-
@callback = Delayed::Master::Callback.new(config)
|
13
|
-
end
|
14
|
-
|
15
|
-
def init
|
16
|
-
@static_settings.each_with_index do |setting, i|
|
17
|
-
worker = Delayed::Master::Worker.new(i, setting)
|
18
|
-
@workers << worker
|
19
|
-
fork_worker(worker)
|
20
|
-
@logger.info "started worker #{worker.pid}"
|
21
|
-
end
|
22
|
-
|
23
|
-
@prepared = true
|
24
|
-
print_workers
|
25
|
-
end
|
26
|
-
|
27
|
-
def monitor_while(&block)
|
28
|
-
loop do
|
29
|
-
break if block.call
|
30
|
-
monitor do
|
31
|
-
check_pid
|
32
|
-
check_dynamic_worker
|
33
|
-
end
|
34
|
-
sleep @config.monitor_wait.to_i
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def prepared?
|
39
|
-
@prepared
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def fork_worker(worker)
|
45
|
-
@callback.run(:before_fork, @master, worker)
|
46
|
-
worker.pid = fork do
|
47
|
-
worker.pid = Process.pid
|
48
|
-
worker.instance = create_instance(worker)
|
49
|
-
@callback.run(:after_fork, @master, worker)
|
50
|
-
$0 = worker.title
|
51
|
-
worker.instance.start
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def create_instance(worker)
|
56
|
-
instance = Delayed::Worker.new(worker.setting.data)
|
57
|
-
[:max_run_time, :max_attempts, :destroy_failed_jobs].each do |key|
|
58
|
-
if (value = worker.setting.send(key))
|
59
|
-
Delayed::Worker.send("#{key}=", value)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
[:max_memory].each do |key|
|
63
|
-
if (value = worker.setting.send(key))
|
64
|
-
instance.send("#{key}=", value)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
instance.master_logger = @logger
|
68
|
-
instance
|
69
|
-
end
|
70
|
-
|
71
|
-
def monitor
|
72
|
-
@callback.run(:before_monitor, @master)
|
73
|
-
yield
|
74
|
-
@callback.run(:after_monitor, @master)
|
75
|
-
rescue Exception => e
|
76
|
-
@logger.warn "#{e.class}: #{e.message} at #{__FILE__}: #{__LINE__}"
|
77
|
-
end
|
78
|
-
|
79
|
-
def check_pid
|
80
|
-
pid = wait_pid
|
81
|
-
return unless pid
|
82
|
-
worker = @workers.detect { |worker| worker.pid == pid }
|
83
|
-
return unless worker
|
84
|
-
|
85
|
-
case worker.setting.control
|
86
|
-
when :static
|
87
|
-
fork_alt_worker(worker)
|
88
|
-
when :dynamic
|
89
|
-
@workers.delete(worker)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def wait_pid
|
94
|
-
Process.waitpid(-1, Process::WNOHANG)
|
95
|
-
rescue Errno::ECHILD
|
96
|
-
nil
|
97
|
-
end
|
98
|
-
|
99
|
-
def check_dynamic_worker
|
100
|
-
@dynamic_settings.each do |setting|
|
101
|
-
current_count = @workers.count { |worker| worker.setting.queues == setting.queues }
|
102
|
-
remaining_count = setting.count - current_count
|
103
|
-
if remaining_count > 0 && (job_count = count_job(setting)) > 0
|
104
|
-
[remaining_count, job_count].min.times do
|
105
|
-
fork_dynamic_worker(setting)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def count_job(setting)
|
112
|
-
Delayed::Master::JobCounter.count(setting)
|
113
|
-
end
|
114
|
-
|
115
|
-
def fork_dynamic_worker(setting)
|
116
|
-
worker = Delayed::Master::Worker.new(@workers.size, setting)
|
117
|
-
@workers << worker
|
118
|
-
|
119
|
-
@logger.info "forking dynamic worker..."
|
120
|
-
fork_worker(worker)
|
121
|
-
@logger.info "forked worker #{worker.pid}"
|
122
|
-
|
123
|
-
print_workers
|
124
|
-
end
|
125
|
-
|
126
|
-
def fork_alt_worker(worker)
|
127
|
-
@logger.info "worker #{worker.pid} seems to be killed, forking alternative worker..."
|
128
|
-
fork_worker(worker)
|
129
|
-
@logger.info "forked worker #{worker.pid}"
|
130
|
-
|
131
|
-
print_workers
|
132
|
-
end
|
133
|
-
|
134
|
-
def print_workers
|
135
|
-
@workers.each do |worker|
|
136
|
-
@logger.debug "#{worker.pid}: #{worker.title}"
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'get_process_mem'
|
2
|
-
|
3
|
-
module Delayed
|
4
|
-
module Plugins
|
5
|
-
class WorkerMemoryChecker < Delayed::Plugin
|
6
|
-
callbacks do |lifecycle|
|
7
|
-
lifecycle.after(:perform) do |worker, job|
|
8
|
-
next unless worker.max_memory
|
9
|
-
mem = GetProcessMem.new
|
10
|
-
if mem.mb > worker.max_memory
|
11
|
-
worker.master_logger.info "shutting down worker #{Process.pid} because it consumes large memory #{mem.mb.to_i} MB..."
|
12
|
-
worker.stop
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
Delayed::Worker.plugins << Delayed::Plugins::WorkerMemoryChecker
|
@@ -1,31 +0,0 @@
|
|
1
|
-
module Delayed
|
2
|
-
module Plugins
|
3
|
-
class SignalHandler < Delayed::Plugin
|
4
|
-
callbacks do |lifecycle|
|
5
|
-
lifecycle.before(:execute) do |worker|
|
6
|
-
worker.instance_eval do
|
7
|
-
trap('USR1') do
|
8
|
-
Thread.new do
|
9
|
-
master_logger.info "reopening files..."
|
10
|
-
Delayed::Master::Util::FileReopener.reopen
|
11
|
-
master_logger.info "reopened"
|
12
|
-
end
|
13
|
-
end
|
14
|
-
trap('USR2') do
|
15
|
-
Thread.new do
|
16
|
-
$0 = "#{$0} [OLD]"
|
17
|
-
master_logger.info "shutting down worker #{Process.pid}..."
|
18
|
-
stop
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
lifecycle.after(:execute) do |worker|
|
24
|
-
worker.master_logger.info "shut down worker #{Process.pid}"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
Delayed::Worker.plugins << Delayed::Plugins::SignalHandler
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Delayed
|
2
|
-
module Plugins
|
3
|
-
class StatusNotifier < Delayed::Plugin
|
4
|
-
callbacks do |lifecycle|
|
5
|
-
lifecycle.around(:perform) do |worker, job, &block|
|
6
|
-
title = $0
|
7
|
-
$0 = "#{title} [BUSY]"
|
8
|
-
ret = block.call
|
9
|
-
$0 = title
|
10
|
-
ret
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
Delayed::Worker.plugins << Delayed::Plugins::StatusNotifier
|