sidekiq-max-jobs 0.0.2
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 +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: []
|