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 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/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+
3
+ .ruby-version
4
+
5
+ Gemfile.lock
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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Link the `[r]spec` task to the `test` task
4
+ desc 'Run `rspec`'
5
+ task test: :spec
6
+
7
+ # Set the default task
8
+ desc 'Run the default `Task`'
9
+ task default: :test
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
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Configure `rspec`
4
+ RSpec.configure { |config| config.fail_if_no_examples = true }
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: []