sidekiq-max-jobs 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +38 -0
- data/.rubocop_todo.yml +36 -0
- data/Gemfile +5 -0
- data/README.md +78 -0
- data/Rakefile +9 -0
- data/VERSION +1 -0
- data/lib/sidekiq/middleware/server/max_jobs.rb +88 -0
- data/sidekiq-max-jobs.gemspec +36 -0
- data/spec/.keep +0 -0
- data/spec/spec_helper.rb +4 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ae8b389a6961487e91003baca5d13e15237f3cff0732cd789a57ead24781deba
|
4
|
+
data.tar.gz: 3aaedb46b7abb7f78cb0794bb759561ea632b208787ae8218a9a75d8ac511856
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5e40e4105fededa4a8b4b3513673bd3fcbbdb83e6ae9c0cf064cfa30bc32d66ccca61f9dbcedbbce91ccd4c40312af6a5dc90bcc057f74e663d72c2cc47b848c
|
7
|
+
data.tar.gz: 632290222af419e9ddb70e86bb58adb20e1a4bd5123079f816d81fd03ad676e5c27939852b6c11378a09a733c451550cb78735ceb4ad49efebbe8197f06f3f01
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
|
3
|
+
require: 'rubocop-rspec'
|
4
|
+
|
5
|
+
AllCops:
|
6
|
+
Exclude:
|
7
|
+
- 'bin/**/*'
|
8
|
+
- 'config/**/*'
|
9
|
+
- 'db/**/*'
|
10
|
+
- 'vendor/**/*'
|
11
|
+
|
12
|
+
Layout/DotPosition:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Layout/SpaceInLambdaLiteral:
|
16
|
+
EnforcedStyle: require_space
|
17
|
+
|
18
|
+
Metrics/BlockLength:
|
19
|
+
Exclude:
|
20
|
+
- 'spec/**/*'
|
21
|
+
|
22
|
+
Metrics/LineLength:
|
23
|
+
Max: 120
|
24
|
+
|
25
|
+
Style/Documentation:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Style/DoubleNegation:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
Style/NegatedIf:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
Style/RegexpLiteral:
|
35
|
+
AllowInnerSlashes: true
|
36
|
+
|
37
|
+
Style/SymbolArray:
|
38
|
+
EnforcedStyle: brackets
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2020-06-09 22:11:35 -0500 using RuboCop version 0.85.1.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 1
|
10
|
+
Lint/RescueException:
|
11
|
+
Exclude:
|
12
|
+
- 'lib/sidekiq/middleware/server/max_jobs.rb'
|
13
|
+
|
14
|
+
# Offense count: 1
|
15
|
+
# Configuration parameters: IgnoredMethods.
|
16
|
+
Metrics/AbcSize:
|
17
|
+
Max: 19
|
18
|
+
|
19
|
+
# Offense count: 1
|
20
|
+
# Configuration parameters: CountComments, ExcludedMethods.
|
21
|
+
# ExcludedMethods: refine
|
22
|
+
Metrics/BlockLength:
|
23
|
+
Max: 26
|
24
|
+
|
25
|
+
# Offense count: 1
|
26
|
+
# Configuration parameters: CountComments, ExcludedMethods.
|
27
|
+
Metrics/MethodLength:
|
28
|
+
Max: 17
|
29
|
+
|
30
|
+
# Offense count: 1
|
31
|
+
# Cop supports --auto-correct.
|
32
|
+
# Configuration parameters: EnforcedStyle.
|
33
|
+
# SupportedStyles: use_perl_names, use_english_names
|
34
|
+
Style/SpecialGlobalVars:
|
35
|
+
Exclude:
|
36
|
+
- 'sidekiq-max-jobs.gemspec'
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
sidekiq-max-jobs
|
2
|
+
================
|
3
|
+
|
4
|
+
A [Sidekiq](https://sidekiq.org/) server middleware. Requires `sidekiq >= 4.0.0`
|
5
|
+
|
6
|
+
This gem provides the ability to configure the maximum number of jobs a `Worker`
|
7
|
+
will process before terminating. For an environment running _Kubernetes_ this is
|
8
|
+
a perfect addition because once the affected pod dies it will automatically be
|
9
|
+
restarted resetting memory, database-connections, etc. with minimal interruption
|
10
|
+
to processing throughput.
|
11
|
+
|
12
|
+
Origin Story
|
13
|
+
------------
|
14
|
+
|
15
|
+
While working on a project for [HappyFunCorp](https://happyfuncorp.com/) we were
|
16
|
+
dealing with unmanageable memory growth on our primary DB. We did all the
|
17
|
+
regular things for _Sidekiq_ such as disabling prepared statements, running with
|
18
|
+
uncached queries, etc. to no avail. After lots of Googling, like any dilligent
|
19
|
+
developer does, we hit a dead-end. A number of people had seen this issue and
|
20
|
+
reported it, yet there was no real guidance aside from "restart your workers
|
21
|
+
periodically to free resources." We saw that this worked, however rather than
|
22
|
+
setting up a CRON we decided to implement a middleware that gave each `Worker`
|
23
|
+
the reigns at controlling its own fate. What started as a work-around, turned
|
24
|
+
out to actually be a pretty good solution vs. switching to a different
|
25
|
+
background-job processing framework. Given that others are facing the same / a
|
26
|
+
similar issue, we wanted to give it back to the open-source community.
|
27
|
+
|
28
|
+
Install & Quick Start
|
29
|
+
---------------------
|
30
|
+
|
31
|
+
To install:
|
32
|
+
```bash
|
33
|
+
$ gem install sidekiq-max-jobs
|
34
|
+
```
|
35
|
+
|
36
|
+
If you're using [Bundler](https://bundler.io/) to manage your dependencies you
|
37
|
+
should add the following to your Gemfile:
|
38
|
+
```ruby
|
39
|
+
gem 'sidekiq-max-jobs'
|
40
|
+
```
|
41
|
+
|
42
|
+
Next, add the middleware to your `sidekiq` initializer (typically: config/initializers/sidekiq.rb)
|
43
|
+
```ruby
|
44
|
+
require 'sidekiq/middleware/server/max_jobs'
|
45
|
+
Sidekiq.configure_server do |config|
|
46
|
+
config.server_middleware do |chain|
|
47
|
+
chain.add Sidekiq::Middleware::Server::MaxJobs
|
48
|
+
end
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
If everything above is successful the next time you start your worker you will
|
53
|
+
see a message like the following:
|
54
|
+
```bash
|
55
|
+
2020-06-10T00:23:31.789Z pid=73703 tid=oxifk6l13 INFO: Max-Jobs middleware enabled, shutting down pid: 73703 after: 100 job(s)
|
56
|
+
```
|
57
|
+
|
58
|
+
Configuration Options
|
59
|
+
---------------------
|
60
|
+
|
61
|
+
Above we covered how to get started, but that's only the beginning. There are a
|
62
|
+
few configuration options available to you to customize the middleware's
|
63
|
+
behavior (currently only configurable via the environment):
|
64
|
+
|
65
|
+
* `MAX_JOBS`: The number of jobs to process before terminating (default: `100`)
|
66
|
+
* `MAX_JOBS_JITTER`: Used as the upper-bound for calculating a random number
|
67
|
+
between 1 and the value specified. This value is added to the `MAX_JOBS` value,
|
68
|
+
mentioned above, to decrease the likelihood that all of your `Worker(s)`
|
69
|
+
restart at / around the same time (default: `1`)
|
70
|
+
|
71
|
+
Contributing
|
72
|
+
------------
|
73
|
+
|
74
|
+
1. Fork it (http://github.com/jzaleski/sidekiq-max-jobs/fork)
|
75
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
76
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
77
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
78
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.2
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module Middleware
|
5
|
+
module Server
|
6
|
+
class MaxJobs
|
7
|
+
VERSION = File.read(
|
8
|
+
File.join(
|
9
|
+
File.dirname(__FILE__),
|
10
|
+
'..',
|
11
|
+
'..',
|
12
|
+
'..',
|
13
|
+
'..',
|
14
|
+
'VERSION'
|
15
|
+
)
|
16
|
+
).strip
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def counter
|
20
|
+
@counter ||= 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def increment_counter!
|
24
|
+
@counter = counter.next
|
25
|
+
end
|
26
|
+
|
27
|
+
def log_info(message)
|
28
|
+
::Sidekiq.logger.info(message) if defined?(::Sidekiq.logger)
|
29
|
+
end
|
30
|
+
|
31
|
+
def log_initialization!
|
32
|
+
log_info("Max-Jobs middleware enabled, shutting down pid: #{pid} after: #{max_jobs_with_jitter} job(s)")
|
33
|
+
end
|
34
|
+
|
35
|
+
def max_jobs
|
36
|
+
@max_jobs ||= (ENV['MAX_JOBS'] || 100).to_i
|
37
|
+
end
|
38
|
+
|
39
|
+
def max_jobs_jitter
|
40
|
+
@max_jobs_jitter ||= rand((ENV['MAX_JOBS_JITTER'] || 1).to_i)
|
41
|
+
end
|
42
|
+
|
43
|
+
def max_jobs_with_jitter
|
44
|
+
@max_jobs_with_jitter ||= max_jobs + max_jobs_jitter
|
45
|
+
end
|
46
|
+
|
47
|
+
def mutex
|
48
|
+
@mutex ||= ::Mutex.new
|
49
|
+
end
|
50
|
+
|
51
|
+
def pid
|
52
|
+
@pid ||= ::Process.pid
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def call(
|
57
|
+
_, # worker-instance
|
58
|
+
_, # item
|
59
|
+
_ # queue
|
60
|
+
)
|
61
|
+
exception_raised = false
|
62
|
+
begin
|
63
|
+
yield
|
64
|
+
rescue Exception
|
65
|
+
# Set the `exception_raised` boolean to `true` so that the
|
66
|
+
# job-counter *is not* incremented in the `ensure` block
|
67
|
+
exception_raised = true
|
68
|
+
# Re-raise the `Exception` so that _Sidekiq_ can deal w/ it
|
69
|
+
raise
|
70
|
+
ensure
|
71
|
+
if !exception_raised
|
72
|
+
self.class.mutex.synchronize do
|
73
|
+
self.class.increment_counter!
|
74
|
+
|
75
|
+
if self.class.counter == self.class.max_jobs_with_jitter
|
76
|
+
self.class.log_info("Max-Jobs quota met, shutting down pid: #{self.class.pid}")
|
77
|
+
::Process.kill('TERM', self.class.pid)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
::Sidekiq::Middleware::Server::MaxJobs.log_initialization!
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
LIB_DIR = File.expand_path(__dir__, 'lib')
|
4
|
+
$LOAD_PATH.unshift(LIB_DIR) unless $LOAD_PATH.include?(LIB_DIR)
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'sidekiq-max-jobs'
|
8
|
+
s.version = File.read(File.join(File.dirname(__FILE__), 'VERSION')).strip
|
9
|
+
s.date = Time.now.strftime('%Y-%m-%d')
|
10
|
+
s.authors = ['Jonathan W. Zaleski']
|
11
|
+
s.email = ['JonathanZaleski@gmail.com']
|
12
|
+
s.summary = <<~SUMMARY
|
13
|
+
A simple plugin used to control the maximum number of jobs for a Sidekiq
|
14
|
+
worker to process before terminating
|
15
|
+
SUMMARY
|
16
|
+
s.description = <<~DESCRIPTION
|
17
|
+
This gem provides the ability to configure the maximum number of jobs a
|
18
|
+
Sidekiq worker will process before terminating. For an environment running
|
19
|
+
Kubernetes this is a perfect addition because once the affected pod dies it
|
20
|
+
will automatically be restarted [gracefully] resetting memory,
|
21
|
+
database-connections, etc. with minimal interruption to throughput
|
22
|
+
DESCRIPTION
|
23
|
+
s.homepage = 'http://github.com/jzaleski/sidekiq-max-jobs'
|
24
|
+
s.license = 'MIT'
|
25
|
+
|
26
|
+
s.files = `git ls-files`.split($/)
|
27
|
+
s.require_paths = %w[lib]
|
28
|
+
|
29
|
+
s.add_dependency('sidekiq', '>= 4.0.0', '< 7.0.0')
|
30
|
+
|
31
|
+
s.add_development_dependency('pry', '~> 0.13.0')
|
32
|
+
s.add_development_dependency('rake', '~> 13.0.0')
|
33
|
+
s.add_development_dependency('rspec', '~> 3.9.0')
|
34
|
+
s.add_development_dependency('rubocop', '~> 0.85.0')
|
35
|
+
s.add_development_dependency('rubocop-rspec', '~> 1.39.0')
|
36
|
+
end
|
data/spec/.keep
ADDED
File without changes
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sidekiq-max-jobs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jonathan W. Zaleski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-06-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sidekiq
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.0.0
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 7.0.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 4.0.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 7.0.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: pry
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 0.13.0
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.13.0
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 13.0.0
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 13.0.0
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rspec
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 3.9.0
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 3.9.0
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rubocop
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 0.85.0
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 0.85.0
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rubocop-rspec
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 1.39.0
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 1.39.0
|
103
|
+
description: |
|
104
|
+
This gem provides the ability to configure the maximum number of jobs a
|
105
|
+
Sidekiq worker will process before terminating. For an environment running
|
106
|
+
Kubernetes this is a perfect addition because once the affected pod dies it
|
107
|
+
will automatically be restarted [gracefully] resetting memory,
|
108
|
+
database-connections, etc. with minimal interruption to throughput
|
109
|
+
email:
|
110
|
+
- JonathanZaleski@gmail.com
|
111
|
+
executables: []
|
112
|
+
extensions: []
|
113
|
+
extra_rdoc_files: []
|
114
|
+
files:
|
115
|
+
- ".gitignore"
|
116
|
+
- ".rubocop.yml"
|
117
|
+
- ".rubocop_todo.yml"
|
118
|
+
- Gemfile
|
119
|
+
- README.md
|
120
|
+
- Rakefile
|
121
|
+
- VERSION
|
122
|
+
- lib/sidekiq/middleware/server/max_jobs.rb
|
123
|
+
- sidekiq-max-jobs.gemspec
|
124
|
+
- spec/.keep
|
125
|
+
- spec/spec_helper.rb
|
126
|
+
homepage: http://github.com/jzaleski/sidekiq-max-jobs
|
127
|
+
licenses:
|
128
|
+
- MIT
|
129
|
+
metadata: {}
|
130
|
+
post_install_message:
|
131
|
+
rdoc_options: []
|
132
|
+
require_paths:
|
133
|
+
- lib
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
requirements: []
|
145
|
+
rubygems_version: 3.0.3
|
146
|
+
signing_key:
|
147
|
+
specification_version: 4
|
148
|
+
summary: A simple plugin used to control the maximum number of jobs for a Sidekiq
|
149
|
+
worker to process before terminating
|
150
|
+
test_files: []
|