sidekiq-process_manager 1.0.4 → 1.1.0

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: d03e818fac3487ed18d2763f536b2e97fa526b8d7dd066b1a066407727ff0dba
4
+ data.tar.gz: 1e3e1837c3367a22ced37e111ad6e7d1e9988bcdd7baaf1d07b0b3083857650e
5
5
  SHA512:
6
- metadata.gz: d10fb3ce00499889150d64baed11dab070c31de8d385ad55ed5665e287034f1ffb04fc4d951ba38f924860d6be58292bc13fdf4fceb74bf06e3011485de3bbfc
7
- data.tar.gz: 1b94ddf8bee336e5600e5151fcac282f71648c88a6dabea9024464698ecdfffcdb80e02c22fb865415e3c033e1e38e3901b6e14331b634057b9283775af2ef44
6
+ metadata.gz: feae2e03df3bc77c6590a1da6bcb5079761184a6e5d37f05c81451b7f52783c8b496dbbdfa71b793a8adfe7eaf8bde293e185ec1ecc1b0a90a802cab772a30dc
7
+ data.tar.gz: 988f0f484e747142c8b725e80717bc0edefee68d9600595352f27a2c343535bc2c6e56ecde5c10dccf56c775b1210f9e50f3a542ed2095fd2e0041fedbfcff93
data/CHANGELOG.md CHANGED
@@ -4,25 +4,42 @@ 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.1.0] - Unreleased
8
+
9
+ ### Added
10
+ - Sidekiq 7 support.
11
+ - Max memory setting to automatically restart processes suffering from memory bloat.
12
+ - Use a notification pipe to handle signals (@KevinCarterDev)
13
+
14
+ ### Removed
15
+ - Sidekiq < 5.0 support.
16
+ - Ruby < 2.5 support.
17
+
7
18
  ## [1.0.4] - 2021-05-20
19
+
8
20
  ### Fixed
9
21
  - Set $0 to "sidekiq" in preforked process so instrumentation libraries detecting sidekiq server from the command line will work.
10
22
 
11
23
  ## [1.0.3] - 2021-05-19
24
+
12
25
  ### Fixed
13
- - Restore bin dir to gem distribution
26
+ - Restore bin dir to gem distribution.
14
27
 
15
28
  ## [1.0.2] - 2021-05-19
29
+
16
30
  ### Added
17
- - Support for sidekiq >= 6.1
31
+ - Support for sidekiq >= 6.1.
18
32
  - Set $0 to "sidekiq" so instrumentation libraries detecting sidekiq server from the command line will work.
19
33
 
20
34
  ### Changed
21
- - Minimum Ruby version 2.3
35
+ - Minimum Ruby version 2.3.
22
36
 
23
37
  ## [1.0.1] - 2020-02-20
38
+
24
39
  ### Changed
25
40
  - Remove auto require of `sidekiq/cli` so `require: false` does not need to be specified in a Gemfile.
26
41
 
27
42
  ## [1.0.0] - 2019-11-27
28
- - Initial release
43
+
44
+ ### Added
45
+ - 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.0
@@ -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,37 +62,56 @@ module Sidekiq
46
62
 
47
63
  master_pid = ::Process.pid
48
64
 
65
+ @signal_pipe_read, @signal_pipe_write = IO.pipe
66
+
49
67
  # Trap signals that will be forwarded to child processes
50
68
  [:INT, :TERM, :USR1, :USR2, :TSTP, :TTIN].each do |signal|
51
69
  ::Signal.trap(signal) do
52
70
  if ::Process.pid == master_pid
53
- send_signal_to_children(signal)
71
+ @signal_pipe_write.puts(signal)
54
72
  end
55
73
  end
56
74
  end
57
75
 
76
+ @signal_thread = Thread.new do
77
+ Thread.current.name = "signal-handler"
78
+
79
+ while @signal_pipe_read.wait_readable
80
+ signal = @signal_pipe_read.gets.strip
81
+ send_signal_to_children(signal.to_sym)
82
+ end
83
+ end
84
+
58
85
  # Ensure that child processes receive the term signal when the master process exits.
59
86
  at_exit do
60
87
  if ::Process.pid == master_pid && @process_count > 0
61
- @pids.each do |pid|
62
- send_signal_to_children(:TERM)
63
- end
88
+ send_signal_to_children(:TERM)
64
89
  end
65
90
  end
66
91
 
92
+ GC.start
93
+ GC.compact if GC.respond_to?(:compact)
94
+ # I'm not sure why, but running GC operations blocks until we try to write some I/O.
95
+ File.write("/dev/null", "0")
96
+
67
97
  @process_count.times do
68
98
  start_child_process!
69
99
  end
70
100
 
101
+ start_memory_monitor if @max_memory
102
+
71
103
  log_info("Process manager started with pid #{::Process.pid}")
72
104
  monitor_child_processes
73
105
  log_info("Process manager #{::Process.pid} exiting")
74
106
  end
75
107
 
76
108
  # Helper to wait on the manager to wait on child processes to start up.
109
+ #
110
+ # @param timeout [Integer] The number of seconds to wait for child processes to start.
111
+ # @return [void]
77
112
  def wait(timeout = 5)
78
- start_time = Time.now
79
- while Time.now < start_time + timeout
113
+ timeout_time = monotonic_time + timeout
114
+ while monotonic_time <= timeout_time
80
115
  return if @pids.size == @process_count
81
116
  sleep(0.01)
82
117
  end
@@ -85,27 +120,45 @@ module Sidekiq
85
120
  end
86
121
 
87
122
  # Helper to gracefully stop all child processes.
123
+ #
124
+ # @return [void]
88
125
  def stop
126
+ stop_memory_monitor
89
127
  @process_count = 0
90
128
  send_signal_to_children(:TSTP)
91
129
  send_signal_to_children(:TERM)
92
130
  end
93
131
 
132
+ # Get all chile process pids.
133
+ #
134
+ # @return [Array<Integer>]
94
135
  def pids
95
- @pids.dup
136
+ @mutex.synchronize { @pids.dup }
96
137
  end
97
138
 
139
+ # Return true if the process manager has started.
140
+ #
141
+ # @return [Boolean]
98
142
  def started?
99
143
  @started
100
144
  end
101
145
 
146
+ # Kill a child process by sending the TERM signal to it.
147
+ #
148
+ # @param pid [Integer] The pid of the child process to kill.
149
+ # @return [void]
150
+ # @api private
151
+ def kill(pid)
152
+ ::Process.kill(:TERM, pid)
153
+ end
154
+
102
155
  private
103
156
 
104
157
  def log_info(message)
105
158
  return if @silent
106
- if $stdout.tty?
107
- $stdout.write("#{message}#{$/}")
108
- $stdout.flush
159
+ if $stderr.tty?
160
+ $stderr.write("#{message}#{$/}")
161
+ $stderr.flush
109
162
  else
110
163
  Sidekiq.logger.info(message)
111
164
  end
@@ -121,10 +174,27 @@ module Sidekiq
121
174
  end
122
175
  end
123
176
 
177
+ def monotonic_time
178
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
179
+ end
180
+
181
+ def sidekiq_options
182
+ if Sidekiq.respond_to?(:default_configuration)
183
+ Sidekiq.default_configuration
184
+ else
185
+ Sidekiq.options
186
+ end
187
+ end
188
+
124
189
  def load_sidekiq
125
190
  @cli.parse
126
- Sidekiq.options[:daemon] = false
127
- Sidekiq.options[:pidfile] = false
191
+
192
+ # Disable daemonization and pidfile creation for child processes (sidekiq < 6.0)
193
+ if Gem::Version.new(Sidekiq::VERSION) < Gem::Version.new("6.0")
194
+ sidekiq_options[:daemon] = false
195
+ sidekiq_options[:pidfile] = false
196
+ end
197
+
128
198
  if @prefork
129
199
  log_info("Pre-forking application")
130
200
  # Set $0 so instrumentation libraries detecting sidekiq from the command run will work properly.
@@ -148,26 +218,30 @@ module Sidekiq
148
218
  end
149
219
 
150
220
  def set_program_name!
151
- $PROGRAM_NAME = "sidekiq process manager #{Sidekiq.options[:tag]} [#{@pids.size} processes]"
221
+ $PROGRAM_NAME = "sidekiq process manager #{sidekiq_options[:tag]} [#{@pids.size} processes]"
152
222
  end
153
223
 
154
224
  def start_child_process!
155
- @pids << fork do
225
+ pid = fork do
156
226
  # Set $0 so instrumentation libraries detecting sidekiq from the command run will work properly.
157
227
  $0 = File.join(File.dirname($0), "sidekiq")
158
228
  @process_count = 0
159
229
  @pids.clear
230
+ @signal_thread.kill
231
+ @signal_pipe_read.close
232
+ @signal_pipe_write.close
160
233
  Sidekiq::ProcessManager.run_after_fork_hooks
161
234
  @cli.run
162
235
  end
163
- log_info("Forked sidekiq process with pid #{@pids.last}")
236
+ @mutex.synchronize { @pids << pid }
237
+ log_info("Forked sidekiq process with pid #{pid}")
164
238
  set_program_name!
165
239
  end
166
240
 
167
241
  def send_signal_to_children(signal)
168
242
  log_info("Process manager trapped signal #{signal}")
169
243
  @process_count = 0 if signal == :INT || signal == :TERM
170
- @pids.each do |pid|
244
+ pids.each do |pid|
171
245
  begin
172
246
  log_info("Sending signal #{signal} to sidekiq process #{pid}")
173
247
  ::Process.kill(signal, pid)
@@ -175,18 +249,73 @@ module Sidekiq
175
249
  log_warning("Error sending signal #{signal} to sidekiq process #{pid}: #{e.inspect}")
176
250
  end
177
251
  end
252
+ wait_for_children_to_exit(pids) if @process_count == 0
253
+ end
254
+
255
+ def start_memory_monitor
256
+ log_info("Starting memory monitor with max memory #{(@max_memory / (1024**2)).round}mb")
257
+
258
+ @memory_monitor = Thread.new do
259
+ Thread.current.name = "memory-monitor"
260
+ loop do
261
+ sleep(@memory_check_interval)
262
+
263
+ pids.each do |pid|
264
+ begin
265
+ memory = GetProcessMem.new(pid)
266
+ if memory.bytes > @max_memory
267
+ log_warning("Kill bloated sidekiq process #{pid}: #{memory.mb.round}mb used")
268
+ kill(pid)
269
+ break
270
+ end
271
+ rescue => e
272
+ log_warning("Error monitoring memory for sidekiq process #{pid}: #{e.inspect}")
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
278
+
279
+ def stop_memory_monitor
280
+ if defined?(@memory_monitor) && @memory_monitor
281
+ @memory_monitor.kill
282
+ end
283
+ end
284
+
285
+ def wait_for_children_to_exit(pids)
286
+ timeout = monotonic_time + (sidekiq_options[:timeout] || 25).to_f
287
+ pids.each do |pid|
288
+ while monotonic_time < timeout
289
+ break unless process_alive?(pid)
290
+ sleep(0.01)
291
+ end
292
+ end
293
+
294
+ pids.each do |pid|
295
+ ::Process.kill(:INT, pid) if process_alive?(pid)
296
+ end
297
+ end
298
+
299
+ def process_alive?(pid)
300
+ begin
301
+ ::Process.getpgid(pid)
302
+ true
303
+ rescue Errno::ESRCH
304
+ false
305
+ end
178
306
  end
179
307
 
180
308
  # Listen for child processes dying and restart if necessary.
181
309
  def monitor_child_processes
182
310
  loop do
183
311
  pid = ::Process.wait
184
- @pids.delete(pid)
312
+ @mutex.synchronize { @pids.delete(pid) }
313
+
185
314
  log_info("Sidekiq process #{pid} exited")
186
315
 
187
316
  # If there are not enough processes running, start a replacement one.
188
317
  if @process_count > @pids.size
189
- start_child_process! if @pids.size < @process_count
318
+ start_child_process!
190
319
  end
191
320
 
192
321
  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.0
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: []