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 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: []