delayed_job_master 1.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 +7 -0
- data/.gitignore +19 -0
- data/.rspec +4 -0
- data/.travis.yml +16 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +158 -0
- data/Rakefile +6 -0
- data/delayed_job_master.gemspec +34 -0
- data/gemfiles/active_record.gemfile +5 -0
- data/gemfiles/mongoid.gemfile +5 -0
- data/lib/delayed/master.rb +115 -0
- data/lib/delayed/master/callback.rb +13 -0
- data/lib/delayed/master/command.rb +32 -0
- data/lib/delayed/master/dsl.rb +60 -0
- data/lib/delayed/master/job_counter.rb +8 -0
- data/lib/delayed/master/job_counter/active_record.rb +24 -0
- data/lib/delayed/master/job_counter/mongoid.rb +32 -0
- data/lib/delayed/master/signal_handler.rb +43 -0
- data/lib/delayed/master/version.rb +5 -0
- data/lib/delayed/master/worker_info.rb +19 -0
- data/lib/delayed/master/worker_pool.rb +128 -0
- data/lib/delayed/util/file_reopener.rb +16 -0
- data/lib/delayed/worker/extension.rb +9 -0
- data/lib/delayed/worker/plugins/memory_checker.rb +16 -0
- data/lib/delayed/worker/plugins/signal_handler.rb +27 -0
- data/lib/delayed/worker/plugins/status_notifier.rb +13 -0
- data/lib/generators/delayed_job_master_generator.rb +14 -0
- data/lib/generators/templates/config.rb +52 -0
- data/lib/generators/templates/script +5 -0
- metadata +200 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 97335497d66ae19f719fdfd655d184507e985b42
|
4
|
+
data.tar.gz: d723ca1ed73489e737cf2653e4685ab0644a4a26
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 56e8b33e80dee3d82b370f3275cc638fee882a458260c5304977c9379fcb6523aec185231038e0629fefb090c78d960ed4fa28e674e4b1a38d702b0492dacce0
|
7
|
+
data.tar.gz: 4b26b8ffbe453683d07809afc5a968dcb115429b0c41a1b2593da12996e5ba1d814e65126d9afa63ad63ae52d353322be2f02385ce4e630e7ff849e549d522be
|
data/.gitignore
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/.project
|
4
|
+
/Gemfile.lock
|
5
|
+
/gemfiles/.bundle
|
6
|
+
/gemfiles/vendor
|
7
|
+
/gemfiles/*gemfile.lock
|
8
|
+
/_yardoc/
|
9
|
+
/bin/
|
10
|
+
/coverage/
|
11
|
+
/doc/
|
12
|
+
/pkg/
|
13
|
+
/log/
|
14
|
+
/tmp/
|
15
|
+
/spec/reports/
|
16
|
+
/spec/**/db/*.sqlite3
|
17
|
+
/spec/**/log/*.log
|
18
|
+
/spec/**/tmp/pids/*.pid
|
19
|
+
/vendor/
|
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
language: ruby
|
2
|
+
services:
|
3
|
+
- mongodb
|
4
|
+
rvm:
|
5
|
+
- 2.3.4
|
6
|
+
- 2.4.1
|
7
|
+
gemfile:
|
8
|
+
- gemfiles/active_record.gemfile
|
9
|
+
- gemfiles/mongoid.gemfile
|
10
|
+
before_install:
|
11
|
+
- gem install bundler -v 1.15.3
|
12
|
+
before_script:
|
13
|
+
- cd spec/dummy
|
14
|
+
- bundle exec rake db:migrate RAILS_ENV=test
|
15
|
+
- cd ../..
|
16
|
+
script: bundle exec rspec
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at kaneta@sitebridge.co.jp. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Yoshikazu Kaneta
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
# DelayedJobMaster
|
2
|
+
|
3
|
+
A simple delayed_job master process to control multiple workers.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
* Preload application and fork workers fastly.
|
8
|
+
* Monitor workers and fork new workers if necessary.
|
9
|
+
* Restart workers with memory limitation.
|
10
|
+
* Trap USR1 signal to reopen log files.
|
11
|
+
* Trap USR2 signal to restart master and workers.
|
12
|
+
* Auto-scale worker processes.
|
13
|
+
|
14
|
+
## Dependencies
|
15
|
+
|
16
|
+
* ruby 2.3+
|
17
|
+
* delayed_job 4.1
|
18
|
+
|
19
|
+
Supported delayed_job backends:
|
20
|
+
|
21
|
+
* delayed_job_active_record 4.1
|
22
|
+
* delayed_job_mongoid 2.3
|
23
|
+
|
24
|
+
## Installation
|
25
|
+
|
26
|
+
Add this line to your application's Gemfile:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem 'delayed_job_master', require: false
|
30
|
+
```
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
|
34
|
+
$ bundle
|
35
|
+
|
36
|
+
Create default files:
|
37
|
+
|
38
|
+
$ rails generate delayed_job_master
|
39
|
+
|
40
|
+
This command creates `bin/delayed_job_master` and `config/delayed_job_master.rb`.
|
41
|
+
|
42
|
+
## Configuration
|
43
|
+
|
44
|
+
Edit `config/delayed_job_master.rb`:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# working directory
|
48
|
+
working_directory Dir.pwd
|
49
|
+
|
50
|
+
# monitor wait time in second
|
51
|
+
monitor_wait 5
|
52
|
+
|
53
|
+
# path to pid file
|
54
|
+
pid_file "#{Dir.pwd}/tmp/pids/delayed_job_master.pid"
|
55
|
+
|
56
|
+
# path to log file
|
57
|
+
log_file "#{Dir.pwd}/log/delayed_job_master.log"
|
58
|
+
|
59
|
+
# log level
|
60
|
+
log_level :info
|
61
|
+
|
62
|
+
# worker1
|
63
|
+
add_worker do |worker|
|
64
|
+
# queue name for the worker
|
65
|
+
worker.queues %w(queue1)
|
66
|
+
|
67
|
+
# worker control (:static or :dynamic)
|
68
|
+
worker.control :static
|
69
|
+
|
70
|
+
# worker count
|
71
|
+
worker.count 1
|
72
|
+
|
73
|
+
# max memory in MB
|
74
|
+
worker.max_memory 300
|
75
|
+
|
76
|
+
# configs below are same as delayed_job, see https://github.com/collectiveidea/delayed_job
|
77
|
+
# worker.sleep_delay 5
|
78
|
+
# worker.read_ahead 5
|
79
|
+
# worker.max_attempts 25
|
80
|
+
# worker.max_run_time 4.hours
|
81
|
+
# worker.min_priority 1
|
82
|
+
# worker.max_priority 10
|
83
|
+
end
|
84
|
+
|
85
|
+
# worker2
|
86
|
+
add_worker do |worker|
|
87
|
+
worker.queues %w(queue2)
|
88
|
+
worker.control :dynamic
|
89
|
+
worker.count 2
|
90
|
+
end
|
91
|
+
|
92
|
+
before_fork do |master, worker_info|
|
93
|
+
Delayed::Worker.before_fork if defined?(Delayed::Worker)
|
94
|
+
end
|
95
|
+
|
96
|
+
after_fork do |master, worker_info|
|
97
|
+
Delayed::Worker.after_fork if defined?(Delayed::Worker)
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
## Usage
|
102
|
+
|
103
|
+
Start master:
|
104
|
+
|
105
|
+
$ RAILS_ENV=production bin/delayed_job_master -c config/delayed_job_master.rb -D
|
106
|
+
|
107
|
+
Command line options:
|
108
|
+
|
109
|
+
* -c, --config: Specify configuration file.
|
110
|
+
* -D, --daemon: Start master as a daemon.
|
111
|
+
|
112
|
+
Stop master and workers gracefully:
|
113
|
+
|
114
|
+
$ kill -TERM `cat tmp/pids/delayed_job_master.pid`
|
115
|
+
|
116
|
+
Stop master and workers forcefully:
|
117
|
+
|
118
|
+
$ kill -QUIT `cat tmp/pids/delayed_job_master.pid`
|
119
|
+
|
120
|
+
Reopen log files:
|
121
|
+
|
122
|
+
$ kill -USR1 `cat tmp/pids/delayed_job_master.pid`
|
123
|
+
|
124
|
+
Restart gracefully:
|
125
|
+
|
126
|
+
$ kill -USR2 `cat tmp/pids/delayed_job_master.pid`
|
127
|
+
|
128
|
+
Workers handle each signal as follows:
|
129
|
+
|
130
|
+
* TERM: Workers stop after finishing current jobs.
|
131
|
+
* QUIT: Workers are killed immediately.
|
132
|
+
* USR1: Workers reopen log files.
|
133
|
+
* USR2: New workers start, old workers stop after finishing current jobs.
|
134
|
+
|
135
|
+
## Worker status
|
136
|
+
|
137
|
+
`ps` command shows worker status as follows:
|
138
|
+
|
139
|
+
```
|
140
|
+
$ ps aux
|
141
|
+
... delayed_job.0 (queue1) [BUSY] # BUSY process is currently proceeding some jobs
|
142
|
+
```
|
143
|
+
|
144
|
+
After graceful restart, you may find OLD process.
|
145
|
+
|
146
|
+
```
|
147
|
+
$ ps aux
|
148
|
+
... delayed_job.0 (queue1) [BUSY] [OLD] # OLD process will stop after finishing current jobs.
|
149
|
+
```
|
150
|
+
|
151
|
+
## Contributing
|
152
|
+
|
153
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/kanety/delayed_job_master. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
154
|
+
|
155
|
+
## License
|
156
|
+
|
157
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
158
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'delayed/master/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "delayed_job_master"
|
8
|
+
spec.version = Delayed::Master::VERSION
|
9
|
+
spec.authors = ["Yoshikazu Kaneta"]
|
10
|
+
spec.email = ["kaneta@sitebridge.co.jp"]
|
11
|
+
|
12
|
+
spec.summary = %q{A simple delayed_job master process to control multiple workers}
|
13
|
+
spec.description = %q{A simple delayed_job master process to control multiple workers}
|
14
|
+
spec.homepage = "https://github.com/kanety/delayed_job_master"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.required_ruby_version = '>= 2.3'
|
23
|
+
|
24
|
+
spec.add_dependency "delayed_job", ">= 4.1"
|
25
|
+
spec.add_dependency "get_process_mem"
|
26
|
+
|
27
|
+
spec.add_development_dependency "rails"
|
28
|
+
spec.add_development_dependency "rake"
|
29
|
+
spec.add_development_dependency "rspec-rails"
|
30
|
+
spec.add_development_dependency "simplecov"
|
31
|
+
spec.add_development_dependency "sqlite3"
|
32
|
+
spec.add_development_dependency "delayed_job_mongoid", ">= 2.3"
|
33
|
+
spec.add_development_dependency "delayed_job_active_record", ">= 4.1"
|
34
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'logger'
|
3
|
+
require 'ostruct'
|
4
|
+
require_relative 'util/file_reopener'
|
5
|
+
require_relative 'master/version'
|
6
|
+
require_relative 'master/command'
|
7
|
+
require_relative 'master/callback'
|
8
|
+
require_relative 'master/worker_info'
|
9
|
+
require_relative 'master/worker_pool'
|
10
|
+
require_relative 'master/signal_handler'
|
11
|
+
|
12
|
+
module Delayed
|
13
|
+
class Master
|
14
|
+
attr_reader :config, :logger, :worker_infos
|
15
|
+
|
16
|
+
def initialize(argv)
|
17
|
+
config = Command.new(argv).config
|
18
|
+
@config = OpenStruct.new(config).freeze
|
19
|
+
@logger = setup_logger(@config.log_file, @config.log_level)
|
20
|
+
@worker_infos = []
|
21
|
+
|
22
|
+
@signal_handler = SignalHandler.new(self)
|
23
|
+
@worker_pool = WorkerPool.new(self, config)
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
load_app
|
28
|
+
show_worker_configs
|
29
|
+
daemonize if @config.daemon
|
30
|
+
|
31
|
+
create_pid_file
|
32
|
+
@logger.info "started master #{Process.pid}"
|
33
|
+
|
34
|
+
@signal_handler.register
|
35
|
+
@worker_pool.init
|
36
|
+
@worker_pool.monitor_while { stop? }
|
37
|
+
|
38
|
+
remove_pid_file
|
39
|
+
@logger.info "shut down master"
|
40
|
+
end
|
41
|
+
|
42
|
+
def load_app
|
43
|
+
require File.join(@config.working_directory, 'config', 'environment')
|
44
|
+
require_relative 'master/job_counter'
|
45
|
+
require_relative 'worker/extension'
|
46
|
+
end
|
47
|
+
|
48
|
+
def prepared?
|
49
|
+
@worker_pool.prepared?
|
50
|
+
end
|
51
|
+
|
52
|
+
def quit
|
53
|
+
kill_workers
|
54
|
+
@stop = true
|
55
|
+
end
|
56
|
+
|
57
|
+
def stop
|
58
|
+
@signal_handler.dispatch('TERM')
|
59
|
+
@stop = true
|
60
|
+
end
|
61
|
+
|
62
|
+
def stop?
|
63
|
+
@stop
|
64
|
+
end
|
65
|
+
|
66
|
+
def reopen_files
|
67
|
+
@signal_handler.dispatch('USR1')
|
68
|
+
@logger.info "reopening files..."
|
69
|
+
Delayed::Util::FileReopener.reopen
|
70
|
+
@logger.info "reopened"
|
71
|
+
end
|
72
|
+
|
73
|
+
def restart
|
74
|
+
@signal_handler.dispatch('USR2')
|
75
|
+
@logger.info "restarting master..."
|
76
|
+
exec(*([$0] + ARGV))
|
77
|
+
end
|
78
|
+
|
79
|
+
def kill_workers
|
80
|
+
@signal_handler.dispatch('KILL')
|
81
|
+
end
|
82
|
+
|
83
|
+
def wait_workers
|
84
|
+
Process.waitall
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def setup_logger(log_file, log_level)
|
90
|
+
FileUtils.mkdir_p(File.dirname(log_file))
|
91
|
+
logger = Logger.new(log_file)
|
92
|
+
logger.level = log_level
|
93
|
+
logger
|
94
|
+
end
|
95
|
+
|
96
|
+
def create_pid_file
|
97
|
+
FileUtils.mkdir_p(File.dirname(@config.pid_file))
|
98
|
+
File.write(@config.pid_file, Process.pid)
|
99
|
+
end
|
100
|
+
|
101
|
+
def remove_pid_file
|
102
|
+
File.delete(@config.pid_file) if File.exist?(@config.pid_file)
|
103
|
+
end
|
104
|
+
|
105
|
+
def daemonize
|
106
|
+
Process.daemon(true)
|
107
|
+
end
|
108
|
+
|
109
|
+
def show_worker_configs
|
110
|
+
@config.worker_configs.each do |config|
|
111
|
+
puts "#{config[:count]} worker for '#{config[:queues].join(',')}' under #{config[:control]} control"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Delayed
|
2
|
+
class Master
|
3
|
+
class Callback
|
4
|
+
def initialize(config = {})
|
5
|
+
@config = config.select { |k, _| [:before_fork, :after_fork].include?(k) }
|
6
|
+
end
|
7
|
+
|
8
|
+
def run(name, *args)
|
9
|
+
@config[name].call(*args) if @config[name]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require_relative 'dsl'
|
3
|
+
|
4
|
+
module Delayed
|
5
|
+
class Master
|
6
|
+
class Command
|
7
|
+
attr_reader :config
|
8
|
+
|
9
|
+
def initialize(args)
|
10
|
+
@config = {}
|
11
|
+
|
12
|
+
OptionParser.new { |opt|
|
13
|
+
opt.banner = <<-EOS
|
14
|
+
#{File.basename($PROGRAM_NAME)} #{Delayed::Master::VERSION}
|
15
|
+
Usage: #{File.basename($PROGRAM_NAME)} [options]
|
16
|
+
EOS
|
17
|
+
|
18
|
+
opt.on('-h', '--help', '-v', '--version', 'Show this message') do |boolean|
|
19
|
+
puts opt
|
20
|
+
exit
|
21
|
+
end
|
22
|
+
opt.on('-D', '--daemon', 'Start master as a daemon') do |boolean|
|
23
|
+
@config[:daemon] = boolean
|
24
|
+
end
|
25
|
+
opt.on('-c', '--config=FILE', 'Specify config file') do |file|
|
26
|
+
@config.merge!(DSL.new(file).config)
|
27
|
+
end
|
28
|
+
}.parse(args)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Delayed
|
2
|
+
class Master
|
3
|
+
class DSL
|
4
|
+
SIMPLE_CONFIGS = [:working_directory, :log_file, :log_level, :pid_file, :monitor_wait]
|
5
|
+
CALLBACK_CONFIGS = [:before_fork, :after_fork]
|
6
|
+
|
7
|
+
attr_reader :config
|
8
|
+
|
9
|
+
def initialize(config_file)
|
10
|
+
@config = { worker_configs: [] }
|
11
|
+
instance_eval(File.read(config_file))
|
12
|
+
@config
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_worker
|
16
|
+
setting = WorkerSetting.new(control: :static, count: 1)
|
17
|
+
yield setting
|
18
|
+
setting.config[:exit_on_complete] = true if setting.config[:control] == :dynamic
|
19
|
+
@config[:worker_configs] << setting.config
|
20
|
+
end
|
21
|
+
|
22
|
+
SIMPLE_CONFIGS.each do |key|
|
23
|
+
define_method(key) do |value|
|
24
|
+
@config[key] = value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
CALLBACK_CONFIGS.each do |key|
|
29
|
+
define_method(key) do |&block|
|
30
|
+
@config[key] = block
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class WorkerSetting
|
35
|
+
SIMPLE_CONFIGS = [:control, :count, :max_memory,
|
36
|
+
:min_priority, :max_priority, :sleep_delay, :read_ahead, :exit_on_complete,
|
37
|
+
:max_attempts, :max_run_time]
|
38
|
+
ARRAY_CONFIGS = [:queues]
|
39
|
+
|
40
|
+
attr_reader :config
|
41
|
+
|
42
|
+
def initialize(default = {})
|
43
|
+
@config = default
|
44
|
+
end
|
45
|
+
|
46
|
+
SIMPLE_CONFIGS.each do |key|
|
47
|
+
define_method(key) do |value|
|
48
|
+
@config[key] = value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
ARRAY_CONFIGS.each do |key|
|
53
|
+
define_method(key) do |value|
|
54
|
+
@config[key] = Array(value)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
case Delayed::Worker.backend.to_s
|
2
|
+
when 'Delayed::Backend::ActiveRecord::Job'
|
3
|
+
require_relative 'job_counter/active_record'
|
4
|
+
when 'Delayed::Backend::Mongoid::Job'
|
5
|
+
require_relative 'job_counter/mongoid'
|
6
|
+
else
|
7
|
+
raise "Unsupported backend: #{Delayed::Worker.backend}"
|
8
|
+
end
|
@@ -0,0 +1,24 @@
|
|
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(config)
|
8
|
+
jobs = ready_to_run(config[:max_run_time] || Delayed::Worker::DEFAULT_MAX_RUN_TIME)
|
9
|
+
jobs.where!("priority >= ?", config[:min_priority]) if config[:min_priority]
|
10
|
+
jobs.where!("priority <= ?", config[:max_priority]) if config[:max_priority]
|
11
|
+
jobs.where!(queue: config[:queues]) if config[: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
|
@@ -0,0 +1,32 @@
|
|
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(config)
|
8
|
+
right_now = Delayed::Job.db_time_now
|
9
|
+
jobs = reservation_criteria(right_now, config[:max_run_time] || Delayed::Worker::DEFAULT_MAX_RUN_TIME)
|
10
|
+
jobs = jobs.gte(priority: config[:min_priority].to_i) if config[:min_priority]
|
11
|
+
jobs = jobs.lte(priority: config[:max_priority].to_i) if config[:max_priority]
|
12
|
+
jobs = jobs.any_in(queue: config[:queues]) if config[: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
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Delayed
|
2
|
+
class Master
|
3
|
+
class SignalHandler
|
4
|
+
def initialize(master)
|
5
|
+
@master = master
|
6
|
+
@logger = master.logger
|
7
|
+
@worker_infos = master.worker_infos
|
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
|
+
@worker_infos.each do |worker_info|
|
32
|
+
next unless worker_info.pid
|
33
|
+
begin
|
34
|
+
Process.kill signal, worker_info.pid
|
35
|
+
@logger.info "sent #{signal} signal to worker #{worker_info.pid}"
|
36
|
+
rescue
|
37
|
+
@logger.error "failed to send #{signal} signal to worker #{worker_info.pid}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Delayed
|
2
|
+
class Master
|
3
|
+
class WorkerInfo
|
4
|
+
attr_reader :index, :config
|
5
|
+
attr_accessor :pid
|
6
|
+
|
7
|
+
def initialize(index, config = {})
|
8
|
+
@index = index
|
9
|
+
@config = config
|
10
|
+
end
|
11
|
+
|
12
|
+
def title
|
13
|
+
titles = ["delayed_job.#{@index}"]
|
14
|
+
titles << "(#{@config[:queues].join(',')})" if @config[:queues]
|
15
|
+
titles.join(' ')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Delayed
|
2
|
+
class Master
|
3
|
+
class WorkerPool
|
4
|
+
def initialize(master, config = {})
|
5
|
+
@master = master
|
6
|
+
@logger = master.logger
|
7
|
+
@worker_infos = master.worker_infos
|
8
|
+
|
9
|
+
@config = OpenStruct.new(config).freeze
|
10
|
+
@static_worker_configs, @dynamic_worker_configs = @config.worker_configs.partition { |wc| wc[:control] == :static }
|
11
|
+
|
12
|
+
@callback = Delayed::Master::Callback.new(config)
|
13
|
+
end
|
14
|
+
|
15
|
+
def init
|
16
|
+
@static_worker_configs.each_with_index do |config, i|
|
17
|
+
worker_info = Delayed::Master::WorkerInfo.new(i, config)
|
18
|
+
@worker_infos << worker_info
|
19
|
+
fork_worker(worker_info)
|
20
|
+
@logger.info "started worker #{worker_info.pid}"
|
21
|
+
end
|
22
|
+
|
23
|
+
@prepared = true
|
24
|
+
debug_worker_infos
|
25
|
+
end
|
26
|
+
|
27
|
+
def monitor_while(&block)
|
28
|
+
loop do
|
29
|
+
break if block.call
|
30
|
+
check_pid
|
31
|
+
check_dynamic_worker
|
32
|
+
sleep @config.monitor_wait.to_i
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def prepared?
|
37
|
+
@prepared
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def fork_worker(worker_info)
|
43
|
+
@callback.run(:before_fork, @master, worker_info)
|
44
|
+
worker_info.pid = fork do
|
45
|
+
@callback.run(:after_fork, @master, worker_info)
|
46
|
+
$0 = worker_info.title
|
47
|
+
worker = create_new_worker(worker_info)
|
48
|
+
worker.start
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def create_new_worker(worker_info)
|
53
|
+
worker = Delayed::Worker.new(worker_info.config)
|
54
|
+
[:max_run_time, :max_attempts].each do |key|
|
55
|
+
value = worker_info.config[key]
|
56
|
+
Delayed::Worker.send("#{key}=", value) if value
|
57
|
+
end
|
58
|
+
[:max_memory].each do |key|
|
59
|
+
value = worker_info.config[key]
|
60
|
+
worker.send("#{key}=", value) if value
|
61
|
+
end
|
62
|
+
worker.master_logger = @logger
|
63
|
+
worker
|
64
|
+
end
|
65
|
+
|
66
|
+
def check_pid
|
67
|
+
pid = wait_pid
|
68
|
+
return unless pid
|
69
|
+
worker_info = @worker_infos.detect { |wi| wi.pid == pid }
|
70
|
+
return unless worker_info
|
71
|
+
|
72
|
+
case worker_info.config[:control]
|
73
|
+
when :static
|
74
|
+
fork_alt_worker(worker_info)
|
75
|
+
when :dynamic
|
76
|
+
@worker_infos.delete(worker_info)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def wait_pid
|
81
|
+
Process.waitpid(-1, Process::WNOHANG)
|
82
|
+
rescue Errno::ECHILD
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
|
86
|
+
def check_dynamic_worker
|
87
|
+
@dynamic_worker_configs.each do |worker_config|
|
88
|
+
current_count = @worker_infos.count { |wi| wi.config[:queues] == worker_config[:queues] }
|
89
|
+
remaining_count = worker_config[:count] - current_count
|
90
|
+
if remaining_count > 0 && (job_count = count_job_for_worker(worker_config)) > 0
|
91
|
+
[remaining_count, job_count].min.times do
|
92
|
+
fork_dynamic_worker(worker_config)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def count_job_for_worker(worker_config)
|
99
|
+
Delayed::Master::JobCounter.count(worker_config)
|
100
|
+
end
|
101
|
+
|
102
|
+
def fork_dynamic_worker(worker_config)
|
103
|
+
worker_info = Delayed::Master::WorkerInfo.new(@worker_infos.size, worker_config)
|
104
|
+
@worker_infos << worker_info
|
105
|
+
|
106
|
+
@logger.info "forking dynamic worker..."
|
107
|
+
fork_worker(worker_info)
|
108
|
+
@logger.info "forked worker #{worker_info.pid}"
|
109
|
+
|
110
|
+
debug_worker_infos
|
111
|
+
end
|
112
|
+
|
113
|
+
def fork_alt_worker(worker_info)
|
114
|
+
@logger.info "worker #{worker_info.pid} seems to be killed, forking alternative worker..."
|
115
|
+
fork_worker(worker_info)
|
116
|
+
@logger.info "forked worker #{worker_info.pid}"
|
117
|
+
|
118
|
+
debug_worker_infos
|
119
|
+
end
|
120
|
+
|
121
|
+
def debug_worker_infos
|
122
|
+
@worker_infos.each do |worker_info|
|
123
|
+
@logger.debug "#{worker_info.pid}: #{worker_info.title}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Delayed
|
2
|
+
class Util
|
3
|
+
class FileReopener
|
4
|
+
class << self
|
5
|
+
def reopen
|
6
|
+
ObjectSpace.each_object(File) do |file|
|
7
|
+
next if file.closed? || !file.sync
|
8
|
+
file.reopen file.path
|
9
|
+
file.sync = true
|
10
|
+
file.flush
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'get_process_mem'
|
2
|
+
|
3
|
+
class Delayed::Plugins::WorkerMemoryChecker < Delayed::Plugin
|
4
|
+
callbacks do |lifecycle|
|
5
|
+
lifecycle.after(:perform) do |worker, job|
|
6
|
+
next unless worker.max_memory
|
7
|
+
mem = GetProcessMem.new
|
8
|
+
if mem.mb > worker.max_memory
|
9
|
+
worker.master_logger.info "shutting down worker #{Process.pid} because it consumes large memory #{mem.mb.to_i} MB..."
|
10
|
+
worker.stop
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Delayed::Worker.plugins << Delayed::Plugins::WorkerMemoryChecker
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Delayed::Plugins::SignalHandler < Delayed::Plugin
|
2
|
+
callbacks do |lifecycle|
|
3
|
+
lifecycle.before(:execute) do |worker|
|
4
|
+
worker.instance_eval do
|
5
|
+
trap('USR1') do
|
6
|
+
Thread.new do
|
7
|
+
master_logger.info "reopening files..."
|
8
|
+
Delayed::Util::FileReopener.reopen
|
9
|
+
master_logger.info "reopened"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
trap('USR2') do
|
13
|
+
Thread.new do
|
14
|
+
$0 = "#{$0} [OLD]"
|
15
|
+
master_logger.info "shutting down worker #{Process.pid}..."
|
16
|
+
stop
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
lifecycle.after(:execute) do |worker|
|
22
|
+
worker.master_logger.info "shut down worker #{Process.pid}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Delayed::Worker.plugins << Delayed::Plugins::SignalHandler
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Delayed::Plugins::StatusNotifier < Delayed::Plugin
|
2
|
+
callbacks do |lifecycle|
|
3
|
+
lifecycle.around(:perform) do |worker, job, &block|
|
4
|
+
title = $0
|
5
|
+
$0 = "#{title} [BUSY]"
|
6
|
+
ret = block.call
|
7
|
+
$0 = title
|
8
|
+
ret
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
Delayed::Worker.plugins << Delayed::Plugins::StatusNotifier
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class DelayedJobMasterGenerator < Rails::Generators::Base
|
4
|
+
source_root File.join(File.dirname(__FILE__), 'templates')
|
5
|
+
|
6
|
+
def create_script_file
|
7
|
+
template 'script', 'bin/delayed_job_master'
|
8
|
+
chmod 'bin/delayed_job_master', 0o755
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_config_file
|
12
|
+
template 'config.rb', 'config/delayed_job_master.rb'
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# working directory
|
2
|
+
working_directory Dir.pwd
|
3
|
+
|
4
|
+
# monitor wait time in second
|
5
|
+
monitor_wait 5
|
6
|
+
|
7
|
+
# path to pid file
|
8
|
+
pid_file "#{Dir.pwd}/tmp/pids/delayed_job_master.pid"
|
9
|
+
|
10
|
+
# path to log file
|
11
|
+
log_file "#{Dir.pwd}/log/delayed_job_master.log"
|
12
|
+
|
13
|
+
# log level
|
14
|
+
log_level :info
|
15
|
+
|
16
|
+
# worker1
|
17
|
+
add_worker do |worker|
|
18
|
+
# queue name for the worker
|
19
|
+
worker.queues %w(queue1)
|
20
|
+
|
21
|
+
# worker control (:static or :dynamic)
|
22
|
+
worker.control :static
|
23
|
+
|
24
|
+
# worker count
|
25
|
+
worker.count 1
|
26
|
+
|
27
|
+
# max memory in MB
|
28
|
+
worker.max_memory 300
|
29
|
+
|
30
|
+
# configs below are same as delayed_job, see https://github.com/collectiveidea/delayed_job
|
31
|
+
# worker.sleep_delay 5
|
32
|
+
# worker.read_ahead 5
|
33
|
+
# worker.max_attempts 25
|
34
|
+
# worker.max_run_time 4.hours
|
35
|
+
# worker.min_priority 1
|
36
|
+
# worker.max_priority 10
|
37
|
+
end
|
38
|
+
|
39
|
+
# worker2
|
40
|
+
add_worker do |worker|
|
41
|
+
worker.queues %w(queue2)
|
42
|
+
worker.control :dynamic
|
43
|
+
worker.count 2
|
44
|
+
end
|
45
|
+
|
46
|
+
before_fork do |master, worker_info|
|
47
|
+
Delayed::Worker.before_fork if defined?(Delayed::Worker)
|
48
|
+
end
|
49
|
+
|
50
|
+
after_fork do |master, worker_info|
|
51
|
+
Delayed::Worker.after_fork if defined?(Delayed::Worker)
|
52
|
+
end
|
metadata
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: delayed_job_master
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yoshikazu Kaneta
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-11-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: delayed_job
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: get_process_mem
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-rails
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sqlite3
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
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
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: delayed_job_active_record
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '4.1'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '4.1'
|
139
|
+
description: A simple delayed_job master process to control multiple workers
|
140
|
+
email:
|
141
|
+
- kaneta@sitebridge.co.jp
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- ".gitignore"
|
147
|
+
- ".rspec"
|
148
|
+
- ".travis.yml"
|
149
|
+
- CODE_OF_CONDUCT.md
|
150
|
+
- Gemfile
|
151
|
+
- LICENSE.txt
|
152
|
+
- README.md
|
153
|
+
- Rakefile
|
154
|
+
- delayed_job_master.gemspec
|
155
|
+
- gemfiles/active_record.gemfile
|
156
|
+
- gemfiles/mongoid.gemfile
|
157
|
+
- lib/delayed/master.rb
|
158
|
+
- lib/delayed/master/callback.rb
|
159
|
+
- lib/delayed/master/command.rb
|
160
|
+
- lib/delayed/master/dsl.rb
|
161
|
+
- lib/delayed/master/job_counter.rb
|
162
|
+
- lib/delayed/master/job_counter/active_record.rb
|
163
|
+
- lib/delayed/master/job_counter/mongoid.rb
|
164
|
+
- lib/delayed/master/signal_handler.rb
|
165
|
+
- lib/delayed/master/version.rb
|
166
|
+
- lib/delayed/master/worker_info.rb
|
167
|
+
- lib/delayed/master/worker_pool.rb
|
168
|
+
- lib/delayed/util/file_reopener.rb
|
169
|
+
- lib/delayed/worker/extension.rb
|
170
|
+
- lib/delayed/worker/plugins/memory_checker.rb
|
171
|
+
- lib/delayed/worker/plugins/signal_handler.rb
|
172
|
+
- lib/delayed/worker/plugins/status_notifier.rb
|
173
|
+
- lib/generators/delayed_job_master_generator.rb
|
174
|
+
- lib/generators/templates/config.rb
|
175
|
+
- lib/generators/templates/script
|
176
|
+
homepage: https://github.com/kanety/delayed_job_master
|
177
|
+
licenses:
|
178
|
+
- MIT
|
179
|
+
metadata: {}
|
180
|
+
post_install_message:
|
181
|
+
rdoc_options: []
|
182
|
+
require_paths:
|
183
|
+
- lib
|
184
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - ">="
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: '2.3'
|
189
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
190
|
+
requirements:
|
191
|
+
- - ">="
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: '0'
|
194
|
+
requirements: []
|
195
|
+
rubyforge_project:
|
196
|
+
rubygems_version: 2.5.2.1
|
197
|
+
signing_key:
|
198
|
+
specification_version: 4
|
199
|
+
summary: A simple delayed_job master process to control multiple workers
|
200
|
+
test_files: []
|