sidekiq-process_manager 1.0.4 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf2d4db525273b507522ba4ec10fb493bb9643bf5311ace3011aa8e1ddb789f9
4
- data.tar.gz: 285c66e121b7d64f41b270165596d91c43886866d8c6504c4ca8732d34c378d0
3
+ metadata.gz: 51d57e09aba69d64225948647f2f95625c668b44eb16e514b5ab4daf8e064e3c
4
+ data.tar.gz: 73d49c20ce5bf06a8d84a168d8e1afa2b04883ea1656d3d4e0ac96b66855140c
5
5
  SHA512:
6
- metadata.gz: d10fb3ce00499889150d64baed11dab070c31de8d385ad55ed5665e287034f1ffb04fc4d951ba38f924860d6be58292bc13fdf4fceb74bf06e3011485de3bbfc
7
- data.tar.gz: 1b94ddf8bee336e5600e5151fcac282f71648c88a6dabea9024464698ecdfffcdb80e02c22fb865415e3c033e1e38e3901b6e14331b634057b9283775af2ef44
6
+ metadata.gz: 2f35e8a6ccbbcbaa15fbbbd62658bd79e4337e675a0c4a776e8dc2c6457bb957ed5ba9880336532a68f54e5ea13890917d758d5f84a2395130c0c52a5166a7cc
7
+ data.tar.gz: dd4fa55ec09584a890e5125d93a64d15924b138583d23e91331a1818bec0c58dfa0cdd81ab58bfab37951a645ed41e149fa04996ec89d11c85c0362204c21be4
data/CHANGELOG.md CHANGED
@@ -4,25 +4,47 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [1.0.4] - 2021-05-20
7
+ ## 1.1.1
8
+
9
+ ### Added
10
+ - Guards to ensure signal processing thread doesn't die.
11
+
12
+ ## 1.1.0
13
+
14
+ ### Added
15
+ - Sidekiq 7 support.
16
+ - Max memory setting to automatically restart processes suffering from memory bloat.
17
+ - Use a notification pipe to handle signals (@KevinCarterDev)
18
+
19
+ ### Removed
20
+ - Sidekiq < 5.0 support.
21
+ - Ruby < 2.5 support.
22
+
23
+ ## 1.0.4
24
+
8
25
  ### Fixed
9
26
  - Set $0 to "sidekiq" in preforked process so instrumentation libraries detecting sidekiq server from the command line will work.
10
27
 
11
- ## [1.0.3] - 2021-05-19
28
+ ## 1.0.3
29
+
12
30
  ### Fixed
13
- - Restore bin dir to gem distribution
31
+ - Restore bin dir to gem distribution.
32
+
33
+ ## 1.0.2
14
34
 
15
- ## [1.0.2] - 2021-05-19
16
35
  ### Added
17
- - Support for sidekiq >= 6.1
36
+ - Support for sidekiq >= 6.1.
18
37
  - Set $0 to "sidekiq" so instrumentation libraries detecting sidekiq server from the command line will work.
19
38
 
20
39
  ### Changed
21
- - Minimum Ruby version 2.3
40
+ - Minimum Ruby version 2.3.
41
+
42
+ ## 1.0.1
22
43
 
23
- ## [1.0.1] - 2020-02-20
24
44
  ### Changed
25
45
  - Remove auto require of `sidekiq/cli` so `require: false` does not need to be specified in a Gemfile.
26
46
 
27
- ## [1.0.0] - 2019-11-27
28
- - Initial release
47
+ ## 1.0.0
48
+
49
+ ### Added
50
+ - Initial release.
data/README.md CHANGED
@@ -42,6 +42,10 @@ If your application can't be pre-forked, you can at least load the gem files and
42
42
 
43
43
  For a Rails application, you would normally want to preboot the `config/boot.rb` file.
44
44
 
45
+ ## Memory Bloat
46
+
47
+ You can also specify a maximum memory footprint that you want to allow for each child process. You can use this feature to automatically guard against poorly designed workers that bloat the Ruby memory heap. Note that you can also use an external process monitor to kill processes with memory bloat; the process manager will restart any process regardless of how it dies.
48
+
45
49
  ## Usage
46
50
 
47
51
  Install the gem in your sidekiq process and run it with `bundle exec sidekiq-process-manager` or, if you use [bundle binstubs](https://bundler.io/man/bundle-binstubs.1.html), `bin/sidekiq-process-manager`. Command line arguments are passed through to `sidekiq`. If you want to supply on of the `sidekiq-process_manager` specific options, those options should come first and the `sidekiq` options should appear after a `--` flag
@@ -93,6 +97,18 @@ or
93
97
  SIDEKIQ_PREBOOT=config/boot.rb SIDEKIQ_PROCESSES=4 bundle exec sidekiq-process-manager
94
98
  ```
95
99
 
100
+ You can set the maximum memory allowed per sidekiq process with the `--max-memory` argument or with the `SIDEKIQ_MAX_MEMORY` environment variable. You can suffix the value with "m" to specify megabytes or "g" to specify gigabytes.
101
+
102
+ ```bash
103
+ bundle exec sidekiq-process-manager --processes 4 --max-memory 2g
104
+ ```
105
+
106
+ or
107
+
108
+ ```bash
109
+ SIDEKIQ_MAX_MEMORY=2000m SIDEKIQ_PROCESSES=4 bundle exec sidekiq-process-manager
110
+ ```
111
+
96
112
  ## Alternatives
97
113
 
98
114
  Any process manager can be an alternative (service, update, systemd, monit, god, etc.).
@@ -103,4 +119,32 @@ The advantages this gem can provide are:
103
119
 
104
120
  2. Running in the foreground with output going to standard out instead of as daemon can integrate better into containerized environments.
105
121
 
106
- 3. Built with sidekiq in mind so signal passing is consistent.
122
+ 3. Built with sidekiq in mind so signal passing is consistent with signals used when running a simple sidekiq process.
123
+
124
+ ## Installation
125
+
126
+ Add this line to your application's Gemfile:
127
+
128
+ ```ruby
129
+ gem "sidekiq-process_manager", require: false
130
+ ```
131
+
132
+ Then execute:
133
+ ```bash
134
+ $ bundle
135
+ ```
136
+
137
+ Or install it yourself as:
138
+ ```bash
139
+ $ gem install sidekiq-process_manager
140
+ ```
141
+
142
+ ## Contributing
143
+
144
+ Open a pull request on [GitHub](https://github.com/bdurand/sidekiq-process_manager).
145
+
146
+ Please use the [standardrb](https://github.com/testdouble/standard) syntax and lint your code with `standardrb --fix` before submitting.
147
+
148
+ ## License
149
+
150
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.1
@@ -6,10 +6,27 @@ require_relative "../lib/sidekiq-process_manager"
6
6
 
7
7
  DEFAULT_PROCESS_COUNT = 1
8
8
 
9
+ def parse_max_memory(max_memory)
10
+ value = nil
11
+
12
+ matched = max_memory.to_s.match(/\A([\d]+(?:\.[\d]+)?)([mg])\z/i)
13
+ if matched
14
+ value = matched[1].to_f
15
+ if matched[2].downcase == 'm'
16
+ value *= 1024 * 1024
17
+ elsif matched[2].downcase == 'g'
18
+ value *= 1024 * 1024 * 1024
19
+ end
20
+ end
21
+
22
+ value
23
+ end
24
+
9
25
  options = {
10
26
  process_count: Integer(ENV.fetch('SIDEKIQ_PROCESSES', DEFAULT_PROCESS_COUNT)),
11
27
  prefork: !ENV.fetch("SIDEKIQ_PREFORK", "").empty?,
12
28
  preboot: ENV["SIDEKIQ_PREBOOT"],
29
+ max_memory: parse_max_memory(ENV["SIDEKIQ_MAX_MEMORY"]),
13
30
  mode: nil,
14
31
  }
15
32
 
@@ -28,6 +45,10 @@ parser = OptionParser.new do |opts|
28
45
  options[:preboot] = preboot
29
46
  end
30
47
 
48
+ opts.on('--max-memory MEMORY', "Max memory for each process (can also specify with SIDEKIQ_MAX_MEMORY); suffix with m or g to specify megabytes or gigabytes") do |max_memory|
49
+ options[:max_memory] = parse_max_memory(max_memory)
50
+ end
51
+
31
52
  opts.on('--testing', "Enable test mode") do |testing|
32
53
  options[:mode] = :testing if testing
33
54
  end
@@ -1,13 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sidekiq"
4
+ require "get_process_mem"
4
5
 
5
6
  module Sidekiq
6
7
  module ProcessManager
8
+ # Process manager for sidekiq. This class is responsible for starting and monitoring
9
+ # that the specified number of sidekiq processes are running. It will also forward
10
+ # signals sent to the main process to the child processes.
7
11
  class Manager
8
12
  attr_reader :cli
9
13
 
10
- def initialize(process_count: 1, prefork: false, preboot: nil, mode: nil, silent: false)
14
+ # Create a new process manager.
15
+ #
16
+ # @param process_count [Integer] The number of sidekiq processes to start.
17
+ # @param prefork [Boolean] If true, the process manager will load the application before forking.
18
+ # @param preboot [String] If set, the process manager will require the specified file before forking the child processes.
19
+ # @param mode [Symbol] If set to :testing, the process manager will use a mock CLI.
20
+ # @param silent [Boolean] If true, the process manager will not output any messages.
21
+ def initialize(process_count: 1, prefork: false, preboot: nil, max_memory: nil, mode: nil, silent: false)
11
22
  require "sidekiq/cli"
12
23
 
13
24
  # Get the number of processes to fork
@@ -16,19 +27,22 @@ module Sidekiq
16
27
 
17
28
  @prefork = (prefork && process_count > 1)
18
29
  @preboot = preboot if process_count > 1 && !prefork
30
+ @max_memory = ((max_memory.to_i > 0) ? max_memory.to_i : nil)
19
31
 
20
32
  if mode == :testing
21
33
  require_relative "../../../spec/support/mocks"
22
34
  @cli = MockSidekiqCLI.new(silent)
35
+ @memory_check_interval = 1
23
36
  else
24
37
  @cli = Sidekiq::CLI.instance
38
+ @memory_check_interval = 60
25
39
  end
26
40
 
27
41
  @silent = silent
28
42
  @pids = []
29
43
  @terminated_pids = []
30
44
  @started = false
31
- @monitor = Monitor.new
45
+ @mutex = Mutex.new
32
46
  end
33
47
 
34
48
  # Start the process manager. This method will start the specified number
@@ -38,6 +52,8 @@ module Sidekiq
38
52
  #
39
53
  # Child processes are manged by sending the signals you would normally send
40
54
  # to a sidekiq process to the process manager instead.
55
+ #
56
+ # @return [void]
41
57
  def start
42
58
  raise "Process manager already started" if started?
43
59
  @started = true
@@ -46,11 +62,26 @@ module Sidekiq
46
62
 
47
63
  master_pid = ::Process.pid
48
64
 
65
+ @signal_pipe_read, @signal_pipe_write = IO.pipe
66
+
67
+ @signal_thread = Thread.new do
68
+ Thread.current.name = "signal-handler"
69
+
70
+ while @signal_pipe_read.wait_readable
71
+ begin
72
+ signal = @signal_pipe_read.gets.strip
73
+ send_signal_to_children(signal.to_sym)
74
+ rescue => e
75
+ log_error("Error handling signal #{signal}: #{e.message}")
76
+ end
77
+ end
78
+ end
79
+
49
80
  # Trap signals that will be forwarded to child processes
50
81
  [:INT, :TERM, :USR1, :USR2, :TSTP, :TTIN].each do |signal|
51
82
  ::Signal.trap(signal) do
52
83
  if ::Process.pid == master_pid
53
- send_signal_to_children(signal)
84
+ @signal_pipe_write.puts(signal)
54
85
  end
55
86
  end
56
87
  end
@@ -58,25 +89,33 @@ module Sidekiq
58
89
  # Ensure that child processes receive the term signal when the master process exits.
59
90
  at_exit do
60
91
  if ::Process.pid == master_pid && @process_count > 0
61
- @pids.each do |pid|
62
- send_signal_to_children(:TERM)
63
- end
92
+ send_signal_to_children(:TERM)
64
93
  end
65
94
  end
66
95
 
96
+ GC.start
97
+ GC.compact if GC.respond_to?(:compact)
98
+ # I'm not sure why, but running GC operations blocks until we try to write some I/O.
99
+ File.write("/dev/null", "0")
100
+
67
101
  @process_count.times do
68
102
  start_child_process!
69
103
  end
70
104
 
105
+ start_memory_monitor if @max_memory
106
+
71
107
  log_info("Process manager started with pid #{::Process.pid}")
72
108
  monitor_child_processes
73
109
  log_info("Process manager #{::Process.pid} exiting")
74
110
  end
75
111
 
76
112
  # Helper to wait on the manager to wait on child processes to start up.
113
+ #
114
+ # @param timeout [Integer] The number of seconds to wait for child processes to start.
115
+ # @return [void]
77
116
  def wait(timeout = 5)
78
- start_time = Time.now
79
- while Time.now < start_time + timeout
117
+ timeout_time = monotonic_time + timeout
118
+ while monotonic_time <= timeout_time
80
119
  return if @pids.size == @process_count
81
120
  sleep(0.01)
82
121
  end
@@ -85,16 +124,25 @@ module Sidekiq
85
124
  end
86
125
 
87
126
  # Helper to gracefully stop all child processes.
127
+ #
128
+ # @return [void]
88
129
  def stop
130
+ stop_memory_monitor
89
131
  @process_count = 0
90
132
  send_signal_to_children(:TSTP)
91
133
  send_signal_to_children(:TERM)
92
134
  end
93
135
 
136
+ # Get all chile process pids.
137
+ #
138
+ # @return [Array<Integer>]
94
139
  def pids
95
- @pids.dup
140
+ @mutex.synchronize { @pids.dup }
96
141
  end
97
142
 
143
+ # Return true if the process manager has started.
144
+ #
145
+ # @return [Boolean]
98
146
  def started?
99
147
  @started
100
148
  end
@@ -103,9 +151,9 @@ module Sidekiq
103
151
 
104
152
  def log_info(message)
105
153
  return if @silent
106
- if $stdout.tty?
107
- $stdout.write("#{message}#{$/}")
108
- $stdout.flush
154
+ if $stderr.tty?
155
+ $stderr.write("#{message}#{$/}")
156
+ $stderr.flush
109
157
  else
110
158
  Sidekiq.logger.info(message)
111
159
  end
@@ -121,10 +169,27 @@ module Sidekiq
121
169
  end
122
170
  end
123
171
 
172
+ def monotonic_time
173
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
174
+ end
175
+
176
+ def sidekiq_options
177
+ if Sidekiq.respond_to?(:default_configuration)
178
+ Sidekiq.default_configuration
179
+ else
180
+ Sidekiq.options
181
+ end
182
+ end
183
+
124
184
  def load_sidekiq
125
185
  @cli.parse
126
- Sidekiq.options[:daemon] = false
127
- Sidekiq.options[:pidfile] = false
186
+
187
+ # Disable daemonization and pidfile creation for child processes (sidekiq < 6.0)
188
+ if Gem::Version.new(Sidekiq::VERSION) < Gem::Version.new("6.0")
189
+ sidekiq_options[:daemon] = false
190
+ sidekiq_options[:pidfile] = false
191
+ end
192
+
128
193
  if @prefork
129
194
  log_info("Pre-forking application")
130
195
  # Set $0 so instrumentation libraries detecting sidekiq from the command run will work properly.
@@ -148,26 +213,30 @@ module Sidekiq
148
213
  end
149
214
 
150
215
  def set_program_name!
151
- $PROGRAM_NAME = "sidekiq process manager #{Sidekiq.options[:tag]} [#{@pids.size} processes]"
216
+ $PROGRAM_NAME = "sidekiq process manager #{sidekiq_options[:tag]} [#{@pids.size} processes]"
152
217
  end
153
218
 
154
219
  def start_child_process!
155
- @pids << fork do
220
+ pid = fork do
156
221
  # Set $0 so instrumentation libraries detecting sidekiq from the command run will work properly.
157
222
  $0 = File.join(File.dirname($0), "sidekiq")
158
223
  @process_count = 0
159
224
  @pids.clear
225
+ @signal_thread.kill
226
+ @signal_pipe_read.close
227
+ @signal_pipe_write.close
160
228
  Sidekiq::ProcessManager.run_after_fork_hooks
161
229
  @cli.run
162
230
  end
163
- log_info("Forked sidekiq process with pid #{@pids.last}")
231
+ @mutex.synchronize { @pids << pid }
232
+ log_info("Forked sidekiq process with pid #{pid}")
164
233
  set_program_name!
165
234
  end
166
235
 
167
236
  def send_signal_to_children(signal)
168
237
  log_info("Process manager trapped signal #{signal}")
169
238
  @process_count = 0 if signal == :INT || signal == :TERM
170
- @pids.each do |pid|
239
+ pids.each do |pid|
171
240
  begin
172
241
  log_info("Sending signal #{signal} to sidekiq process #{pid}")
173
242
  ::Process.kill(signal, pid)
@@ -175,18 +244,81 @@ module Sidekiq
175
244
  log_warning("Error sending signal #{signal} to sidekiq process #{pid}: #{e.inspect}")
176
245
  end
177
246
  end
247
+ wait_for_children_to_exit(pids) if @process_count == 0
248
+ end
249
+
250
+ def start_memory_monitor
251
+ log_info("Starting memory monitor with max memory #{(@max_memory / (1024**2)).round}mb")
252
+
253
+ @memory_monitor = Thread.new do
254
+ Thread.current.name = "memory-monitor"
255
+ loop do
256
+ sleep(@memory_check_interval)
257
+
258
+ pids.each do |pid|
259
+ begin
260
+ memory = GetProcessMem.new(pid)
261
+ if memory.bytes > @max_memory
262
+ log_warning("Kill bloated sidekiq process #{pid}: #{memory.mb.round}mb used")
263
+ begin
264
+ ::Process.kill(:TERM, pid)
265
+ rescue Errno::ESRCH
266
+ # The process is already dead
267
+ end
268
+ break
269
+ end
270
+ rescue => e
271
+ log_warning("Error monitoring memory for sidekiq process #{pid}: #{e.inspect}")
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end
277
+
278
+ def stop_memory_monitor
279
+ if defined?(@memory_monitor) && @memory_monitor
280
+ @memory_monitor.kill
281
+ end
282
+ end
283
+
284
+ def wait_for_children_to_exit(pids)
285
+ timeout = monotonic_time + (sidekiq_options[:timeout] || 25).to_f
286
+ pids.each do |pid|
287
+ while monotonic_time < timeout
288
+ break unless process_alive?(pid)
289
+ sleep(0.01)
290
+ end
291
+ end
292
+
293
+ pids.each do |pid|
294
+ begin
295
+ ::Process.kill(:INT, pid) if process_alive?(pid)
296
+ rescue
297
+ # Ignore errors so we can continue to kill other processes.
298
+ end
299
+ end
300
+ end
301
+
302
+ def process_alive?(pid)
303
+ begin
304
+ ::Process.getpgid(pid)
305
+ true
306
+ rescue Errno::ESRCH
307
+ false
308
+ end
178
309
  end
179
310
 
180
311
  # Listen for child processes dying and restart if necessary.
181
312
  def monitor_child_processes
182
313
  loop do
183
314
  pid = ::Process.wait
184
- @pids.delete(pid)
315
+ @mutex.synchronize { @pids.delete(pid) }
316
+
185
317
  log_info("Sidekiq process #{pid} exited")
186
318
 
187
319
  # If there are not enough processes running, start a replacement one.
188
320
  if @process_count > @pids.size
189
- start_child_process! if @pids.size < @process_count
321
+ start_child_process!
190
322
  end
191
323
 
192
324
  set_program_name!
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sidekiq
4
4
  module ProcessManager
5
- VERSION = "1.0.4"
5
+ VERSION = File.read(File.expand_path("../../../VERSION", __dir__)).strip
6
6
  end
7
7
  end
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "lib/sidekiq/process_manager/version"
4
-
5
3
  Gem::Specification.new do |spec|
6
4
  spec.name = "sidekiq-process_manager"
7
- spec.version = Sidekiq::ProcessManager::VERSION
5
+ spec.version = File.read(File.expand_path("VERSION", __dir__)).strip
8
6
  spec.authors = ["Brian Durand"]
9
7
  spec.email = ["bbdurand@gmail.com"]
10
8
 
@@ -31,9 +29,10 @@ Gem::Specification.new do |spec|
31
29
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
32
30
  spec.require_paths = ["lib"]
33
31
 
34
- spec.required_ruby_version = ">= 2.3"
32
+ spec.required_ruby_version = ">= 2.5"
35
33
 
36
- spec.add_dependency "sidekiq", ">= 3.0"
34
+ spec.add_dependency "sidekiq", ">= 5.0"
35
+ spec.add_dependency "get_process_mem"
37
36
 
38
37
  spec.add_development_dependency "bundler"
39
38
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-process_manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Durand
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-20 00:00:00.000000000 Z
11
+ date: 2023-07-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq
@@ -16,14 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '3.0'
19
+ version: '5.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '3.0'
26
+ version: '5.0'
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'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -38,7 +52,7 @@ dependencies:
38
52
  - - ">="
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
41
- description:
55
+ description:
42
56
  email:
43
57
  - bbdurand@gmail.com
44
58
  executables:
@@ -49,6 +63,7 @@ files:
49
63
  - CHANGELOG.md
50
64
  - MIT_LICENSE.txt
51
65
  - README.md
66
+ - VERSION
52
67
  - bin/sidekiq-process-manager
53
68
  - lib/sidekiq-process_manager.rb
54
69
  - lib/sidekiq/process_manager.rb
@@ -59,7 +74,7 @@ homepage: https://github.com/bdurand/sidekiq-process_manager
59
74
  licenses:
60
75
  - MIT
61
76
  metadata: {}
62
- post_install_message:
77
+ post_install_message:
63
78
  rdoc_options: []
64
79
  require_paths:
65
80
  - lib
@@ -67,15 +82,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
67
82
  requirements:
68
83
  - - ">="
69
84
  - !ruby/object:Gem::Version
70
- version: '2.3'
85
+ version: '2.5'
71
86
  required_rubygems_version: !ruby/object:Gem::Requirement
72
87
  requirements:
73
88
  - - ">="
74
89
  - !ruby/object:Gem::Version
75
90
  version: '0'
76
91
  requirements: []
77
- rubygems_version: 3.0.3
78
- signing_key:
92
+ rubygems_version: 3.4.12
93
+ signing_key:
79
94
  specification_version: 4
80
95
  summary: Process manager for forking and monitoring multiple sidekiq processes.
81
96
  test_files: []