sidekiq-max-jobs 0.0.5 → 0.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9bf624b8b84bbfd78039b22826e440c7218c9850207a55a1a24bfc046a0812a4
4
- data.tar.gz: 364ca8c57ac52151636f7c9a0feb7ffe18d758fd95772fb692b0802fb634bef6
3
+ metadata.gz: 9e52017db21742cf50ed432d2773add32e29a8119341a1d9dc757b9817a3aee9
4
+ data.tar.gz: 3c5609487bb19a7de4c24cbf389e5faf6a54819a61c7fef368562c74bbc22f02
5
5
  SHA512:
6
- metadata.gz: 12548204b50a4f9d3dfec6470d3488b03fc672c7c41c12e6554ed350004ce63b42d322043c21dfc023fbd6fc819a724c5a167d62494a0615be0c8d4e71bfda19
7
- data.tar.gz: 7e704fde12c9f64b200ac001f6824821ec6e0698235d4493e01a30ff49f27e3742373ae38f0ef91c013b5ee08986bf619cb6c2922eff44c79b3898444b4c3984
6
+ metadata.gz: 78b270607d7c393cd62fb73e111332197177310856b43a7ec4496ae69beb08ead4edd8ccdf15ecbb99fa8fe3892bb2a150aac315ee657cbe9070b29058a3ba2c
7
+ data.tar.gz: fb7cb69631840c6082d91333a6b387e1abd2a1173ab71948908b11e0b4a064b7c8a4915bdd2893495581b52b89d351b82ab2146b665e4f21ac5db38aa04dc132
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2020-06-17 22:45:10 -0500 using RuboCop version 0.85.1.
3
+ # on 2020-06-21 09:12:56 -0500 using RuboCop version 0.85.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -14,7 +14,7 @@ Lint/RescueException:
14
14
  # Offense count: 1
15
15
  # Configuration parameters: IgnoredMethods.
16
16
  Metrics/AbcSize:
17
- Max: 27
17
+ Max: 31
18
18
 
19
19
  # Offense count: 1
20
20
  # Configuration parameters: CountComments, ExcludedMethods.
@@ -25,17 +25,22 @@ Metrics/BlockLength:
25
25
  # Offense count: 1
26
26
  # Configuration parameters: CountComments.
27
27
  Metrics/ClassLength:
28
- Max: 152
28
+ Max: 193
29
29
 
30
30
  # Offense count: 1
31
31
  # Configuration parameters: IgnoredMethods.
32
32
  Metrics/CyclomaticComplexity:
33
- Max: 7
33
+ Max: 10
34
34
 
35
35
  # Offense count: 1
36
36
  # Configuration parameters: CountComments, ExcludedMethods.
37
37
  Metrics/MethodLength:
38
- Max: 24
38
+ Max: 28
39
+
40
+ # Offense count: 1
41
+ # Configuration parameters: IgnoredMethods.
42
+ Metrics/PerceivedComplexity:
43
+ Max: 10
39
44
 
40
45
  # Offense count: 1
41
46
  # Cop supports --auto-correct.
data/README.md CHANGED
@@ -52,7 +52,7 @@ end
52
52
  If everything above is successful the next time you start your worker you will
53
53
  see a message like the following:
54
54
  ```bash
55
- 2020-06-10T00:23:31.789Z pid=73703 tid=oxifk6l13 INFO: Max-Jobs middleware enabled, shutting down pid: 73703 when max-jobs quota is reached
55
+ 2020-06-10T00:23:31.789Z pid=73703 tid=oxifk6l13 INFO: Max-Jobs middleware enabled, shutting down pid: 73703 when quota is reached
56
56
  ```
57
57
 
58
58
  Configuration Options
@@ -62,30 +62,36 @@ Above we covered how to get started, but that's only the beginning. There are a
62
62
  few configuration options available to you to customize the middleware's
63
63
  behavior (currently only configurable via the environment):
64
64
 
65
- * `MAX_JOBS`: The number of jobs to process before terminating (default: `100`)
65
+ * `MAX_JOBS`: The number of jobs to process before terminating (default: `500`)
66
66
  * `MAX_JOBS_JITTER`: Used as the upper-bound for calculating a random number
67
67
  between 1 and the value specified. This value is added to the `MAX_JOBS` value,
68
68
  mentioned above, to decrease the likelihood that all of your `Worker(s)`
69
- restart at / around the same time (default: `rand(1)`)
69
+ restart at / around the same time (default: `rand(-1)`)
70
70
  * `MAX_JOBS_<QUEUE>`: The number of jobs to process for a specific queue before
71
- terminating (default: `(ENV['MAX_JOBS'] || 100).to_i`)
71
+ terminating (default: `-1`)
72
72
  * `MAX_JOBS_JITTER_<QUEUE>`: Used as the upper-bound for calculating a random
73
73
  number between 1 and the value specified. This value is added to the
74
74
  `MAX_JOBS_<QUEUE>` value, mentioned above, to decreased the likelihood that all
75
75
  of your `Worker(s)` restart at / around the same time (default:
76
- `rand((ENV['MAX_JOBS_JITTER'] || 1).to_i)`)
76
+ `rand(-1)`)
77
+ * `MAX_JOBS_RUNTIME`: The total time in seconds to run before terminating
78
+ (default: `-1`)
79
+ * `MAX_JOBS_RUNTIME_JITTER`: Used as the upper-bound for calculating a random
80
+ number between 1 and the value specified. This value is added to the
81
+ `MAX_JOBS_RUNTIME` value, mentioned above, to decrease the likelihood that all
82
+ of your `Worker(s)` restart at / around the same time (default: `rand(-1)`)
77
83
 
78
84
  Important Note
79
85
  --------------
80
86
 
81
- When determining if the max-job quota has been reached the total jobs processed
82
- is checked first, followed by the jobs processed for the current queue. If your
83
- `Worker(s)` are handling multiple queues it is generally recommended that you
84
- set the total value to the same value as your highest queue value (e.g. if you
85
- had `MAX_JOBS_FOO=100` and `MAX_JOBS_BAR=200` it probably makes sense to set
86
- `MAX_JOBS=200`, if not a little bit lower). Setting the right limits ultimately
87
- depends on the intensity / resource needs of the work being performed. The same
88
- rule of thumb applies to `MAX_JOBS_JITTER` as well.
87
+ When determining if the max-job quota has been reached the runtime is checked
88
+ first, followed by the total jobs processed, followed by the jobs processed for
89
+ the current queue. If your `Worker(s)` are handling multiple queues it is
90
+ recommended that you set the total value to the same value as your highest queue
91
+ value (e.g. if you had `MAX_JOBS_FOO=100` and `MAX_JOBS_BAR=200` it probably
92
+ makes sense to set `MAX_JOBS=200`, if not a little bit lower). Setting the right
93
+ limits ultimately depends on the intensity / resource needs of the work being
94
+ performed. The same rule of thumb applies to `MAX_JOBS_JITTER` as well.
89
95
 
90
96
  Contributing
91
97
  ------------
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.5
1
+ 0.1.0
@@ -4,6 +4,8 @@ module Sidekiq
4
4
  module Middleware
5
5
  module Server
6
6
  class MaxJobs
7
+ # Version
8
+
7
9
  VERSION = File.read(
8
10
  File.join(
9
11
  File.dirname(__FILE__),
@@ -16,143 +18,185 @@ module Sidekiq
16
18
  ).strip
17
19
 
18
20
  class << self
21
+ # Constant(s)
22
+
23
+ COUNTER_FOR_QUEUE_KEY_TEMPLATE = 'COUNTER_%s'
24
+ COUNTER_KEY = 'COUNTER'
25
+ LOG_INITIALIZATION_TEMPLATE = \
26
+ 'Max-Jobs middleware enabled, shutting down pid: %d when quota is reached'
27
+ LOG_MAX_JOBS_QUOTA_MET_FOR_QUEUE_TEMPLATE = \
28
+ 'Max-Jobs queue quota met for: "%s", shutting down pid: %d'
29
+ LOG_MAX_JOBS_QUOTA_MET_TEMPLATE = \
30
+ 'Max-Jobs total quota met, shutting down pid: %d'
31
+ LOG_MAX_JOBS_RUNTIME_QUOTA_MET_TEMPLATE = \
32
+ 'Max-Jobs runtime quota met, shutting down pid: %d'
33
+ MAX_JOBS_FOR_QUEUE_KEY_TEMPLATE = 'MAX_JOBS_%s'
34
+ MAX_JOBS_JITTER_FOR_QUEUE_KEY_TEMPLATE = 'MAX_JOBS_JITTER_%s'
35
+ MAX_JOBS_JITTER_KEY = 'MAX_JOBS_JITTER'
36
+ MAX_JOBS_KEY = 'MAX_JOBS'
37
+ MAX_JOBS_RUNTIME_JITTER_KEY = 'MAX_JOBS_RUNTIME_JITTER'
38
+ MAX_JOBS_RUNTIME_WITH_JITTER_KEY = 'MAX_JOBS_RUNTIME_WITH_JITTER'
39
+ MAX_JOBS_RUNTIME_KEY = 'MAX_JOBS_RUNTIME'
40
+ MAX_JOBS_WITH_JITTER_FOR_QUEUE_KEY_TEMPLATE = \
41
+ 'MAX_JOBS_WITH_JITTER_%s'
42
+ MAX_JOBS_WITH_JITTER_KEY = 'MAX_JOBS_WITH_JITTER'
43
+ MUTEX_KEY = 'MUTEX'
44
+ PID_KEY = 'PID'
45
+ START_TIME_KEY = 'START_TIME'
46
+ TERM = 'TERM'
47
+ TERMINATING_KEY = 'TERMINATING'
48
+
49
+ # Default(s)
50
+
51
+ DEFAULT_MAX_JOBS = 500
52
+ DEFAULT_MAX_JOBS_FOR_QUEUE = -1
53
+ DEFAULT_MAX_JOBS_JITTER = -1
54
+ DEFAULT_MAX_JOBS_JITTER_FOR_QUEUE = -1
55
+ DEFAULT_MAX_JOBS_RUNTIME = -1
56
+ DEFAULT_MAX_JOBS_RUNTIME_JITTER = -1
57
+
58
+ # Helper Method(s)
59
+
19
60
  def cache
20
61
  @cache ||= {}
21
62
  end
22
63
 
23
64
  def counter
24
- key = counter_key
65
+ key = COUNTER_KEY
25
66
  cache[key] ||= 0
26
67
  end
27
68
 
28
69
  def counter_for_queue(queue)
29
- key = counter_for_queue_key(queue)
70
+ key = format(COUNTER_FOR_QUEUE_KEY_TEMPLATE, queue.upcase)
30
71
  cache[key] ||= 0
31
72
  end
32
73
 
33
- def counter_for_queue_key(queue)
34
- "COUNTER_#{queue.upcase}"
35
- end
36
-
37
- def counter_key
38
- 'COUNTER'
39
- end
40
-
41
- def default_max_jobs
42
- 100
43
- end
44
-
45
- def default_max_jobs_jitter
46
- 1
47
- end
48
-
49
74
  def increment_counter!
50
- key = counter_key
75
+ key = COUNTER_KEY
51
76
  cache[key] = (cache[key] || 0).next
52
77
  end
53
78
 
54
79
  def increment_counter_for_queue!(queue)
55
- key = counter_for_queue_key(queue)
80
+ key = format(COUNTER_FOR_QUEUE_KEY_TEMPLATE, queue.upcase)
56
81
  cache[key] = (cache[key] || 0).next
57
82
  end
58
83
 
59
84
  def log_info(message)
60
- ::Sidekiq.logger.info(message) if defined?(::Sidekiq.logger)
85
+ logger_defined = defined?(::Sidekiq.logger)
86
+ logger_defined ? ::Sidekiq.logger.info(message) : puts(message)
61
87
  end
62
88
 
63
89
  def log_initialization!
64
- log_info("Max-Jobs middleware enabled, shutting down pid: #{pid} when max-jobs quota is reached")
90
+ message = format(LOG_INITIALIZATION_TEMPLATE, pid)
91
+ log_info(message)
65
92
  end
66
93
 
67
- def max_jobs
68
- key = max_jobs_key
69
- cache[key] ||= (ENV[key] || default_max_jobs).to_i
94
+ def log_max_jobs_quota_met!
95
+ message = format(LOG_MAX_JOBS_QUOTA_MET_TEMPLATE, pid)
96
+ log_info(message)
70
97
  end
71
98
 
72
- def max_jobs_for_queue(queue)
73
- key = max_jobs_for_queue_key(queue)
74
- cache[key] ||= (
75
- ENV[key] ||
76
- ENV[max_jobs_key] ||
77
- default_max_jobs
78
- ).to_i
99
+ def log_max_jobs_quota_met_for_queue!(queue)
100
+ message = format(
101
+ LOG_MAX_JOBS_QUOTA_MET_FOR_QUEUE_TEMPLATE,
102
+ queue,
103
+ pid
104
+ )
105
+ log_info(message)
106
+ end
107
+
108
+ def log_max_jobs_runtime_quota_met!
109
+ message = format(LOG_MAX_JOBS_RUNTIME_QUOTA_MET_TEMPLATE, pid)
110
+ log_info(message)
79
111
  end
80
112
 
81
- def max_jobs_for_queue_key(queue)
82
- "MAX_JOBS_#{queue.upcase}"
113
+ def max_jobs
114
+ key = MAX_JOBS_KEY
115
+ cache[key] ||= (ENV[key] || DEFAULT_MAX_JOBS).to_i
116
+ end
117
+
118
+ def max_jobs_for_queue(queue)
119
+ key = format(MAX_JOBS_FOR_QUEUE_KEY_TEMPLATE, queue.upcase)
120
+ cache[key] ||= (ENV[key] || DEFAULT_MAX_JOBS_FOR_QUEUE).to_i
83
121
  end
84
122
 
85
123
  def max_jobs_jitter
86
- key = max_jobs_jitter_key
87
- cache[key] ||= rand((ENV[key] || default_max_jobs_jitter).to_i)
124
+ key = MAX_JOBS_JITTER_KEY
125
+ cache[key] ||= rand((ENV[key] || DEFAULT_MAX_JOBS_JITTER).to_i)
88
126
  end
89
127
 
90
128
  def max_jobs_jitter_for_queue(queue)
91
- key = max_jobs_jitter_for_queue_key(queue)
92
- cache[key] ||= rand(
93
- (
94
- ENV[key] ||
95
- ENV[max_jobs_jitter_key] ||
96
- default_max_jobs_jitter
97
- ).to_i
98
- )
129
+ key = format(MAX_JOBS_JITTER_FOR_QUEUE_KEY_TEMPLATE, queue.upcase)
130
+ cache[key] ||= \
131
+ rand((ENV[key] || DEFAULT_MAX_JOBS_JITTER_FOR_QUEUE).to_i)
99
132
  end
100
133
 
101
- def max_jobs_jitter_for_queue_key(queue)
102
- "MAX_JOBS_JITTER_#{queue.upcase}"
134
+ def max_jobs_quota_met?
135
+ quota = max_jobs_with_jitter
136
+ quota.positive? ? counter == quota : false
137
+ end
138
+
139
+ def max_jobs_quota_met_for_queue?(queue)
140
+ quota = max_jobs_with_jitter_for_queue(queue)
141
+ quota.positive? ? counter_for_queue(queue) == quota : false
142
+ end
143
+
144
+ def max_jobs_runtime
145
+ key = MAX_JOBS_RUNTIME_KEY
146
+ cache[key] ||= (ENV[key] || DEFAULT_MAX_JOBS_RUNTIME).to_i
147
+ end
148
+
149
+ def max_jobs_runtime_jitter
150
+ key = MAX_JOBS_RUNTIME_JITTER_KEY
151
+ cache[key] ||= \
152
+ rand((ENV[key] || DEFAULT_MAX_JOBS_RUNTIME_JITTER).to_i)
103
153
  end
104
154
 
105
- def max_jobs_jitter_key
106
- 'MAX_JOBS_JITTER'
155
+ def max_jobs_runtime_quota_met?
156
+ quota = max_jobs_runtime_with_jitter
157
+ quota.positive? ? (::Time.now.to_i - start_time) >= quota : false
107
158
  end
108
159
 
109
- def max_jobs_key
110
- 'MAX_JOBS'
160
+ def max_jobs_runtime_with_jitter
161
+ key = MAX_JOBS_RUNTIME_WITH_JITTER_KEY
162
+ cache[key] ||= (max_jobs_runtime + max_jobs_runtime_jitter)
111
163
  end
112
164
 
113
165
  def max_jobs_with_jitter
114
- key = max_jobs_with_jitter_key
166
+ key = MAX_JOBS_WITH_JITTER_KEY
115
167
  cache[key] ||= (max_jobs + max_jobs_jitter)
116
168
  end
117
169
 
118
170
  def max_jobs_with_jitter_for_queue(queue)
119
- key = max_jobs_with_jitter_for_queue_key(queue)
171
+ key = \
172
+ format(MAX_JOBS_WITH_JITTER_FOR_QUEUE_KEY_TEMPLATE, queue.upcase)
120
173
  cache[key] ||= \
121
174
  (max_jobs_for_queue(queue) + max_jobs_jitter_for_queue(queue))
122
175
  end
123
176
 
124
- def max_jobs_with_jitter_for_queue_key(queue)
125
- "MAX_JOBS_WITH_JITTER_#{queue.upcase}"
126
- end
127
-
128
- def max_jobs_with_jitter_key
129
- 'MAX_JOBS_WITH_JITTER'
130
- end
131
-
132
177
  def mutex
133
- key = mutex_key
178
+ key = MUTEX_KEY
134
179
  cache[key] ||= ::Mutex.new
135
180
  end
136
181
 
137
- def mutex_key
138
- 'MUTEX'
139
- end
140
-
141
182
  def pid
142
- key = pid_key
183
+ key = PID_KEY
143
184
  cache[key] ||= ::Process.pid
144
185
  end
145
186
 
146
- def pid_key
147
- 'PID'
187
+ def start_time
188
+ key = START_TIME_KEY
189
+ cache[key] ||= ::Time.now.to_i
148
190
  end
149
191
 
150
- def quota_met?
151
- counter == max_jobs_with_jitter
192
+ def terminate!
193
+ key = TERMINATING_KEY
194
+ cache[key] = true && ::Process.kill(TERM, pid)
152
195
  end
153
196
 
154
- def quota_met_for_queue?(queue)
155
- counter_for_queue(queue) == max_jobs_with_jitter_for_queue(queue)
197
+ def terminating?
198
+ key = TERMINATING_KEY
199
+ cache[key] == true
156
200
  end
157
201
  end
158
202
 
@@ -165,36 +209,44 @@ module Sidekiq
165
209
  begin
166
210
  yield
167
211
  rescue Exception
168
- # Set the `exception_raised` boolean to `true` so that the
169
- # job-counter *is not* incremented in the `ensure` block
212
+ # Set the `exception_raised` boolean to `true` so that the counter
213
+ # *is not* incremented in the `ensure` block
170
214
  exception_raised = true
171
215
  # Re-raise the `Exception` so that _Sidekiq_ can deal w/ it
172
216
  raise
173
217
  ensure
174
- if !exception_raised
218
+ if !exception_raised && !self.class.terminating?
175
219
  self.class.mutex.synchronize do
220
+ # Controls whether or not the process will be TERMinated at the
221
+ # end of the block
176
222
  terminate = false
177
223
 
224
+ # First check if the runtime quota has been met
225
+ if self.class.max_jobs_runtime_quota_met?
226
+ self.class.log_max_jobs_runtime_quota_met!
227
+ terminate = true
228
+ end
229
+
178
230
  # Increment the total counter
179
231
  self.class.increment_counter!
180
232
 
181
- # First check if the total quota has been met
182
- if self.class.quota_met?
183
- self.class.log_info("Max-Jobs quota met, shutting down pid: #{self.class.pid}")
233
+ # Next, check if the total quota has been met
234
+ if !terminate && self.class.max_jobs_quota_met?
235
+ self.class.log_max_jobs_quota_met!
184
236
  terminate = true
185
237
  end
186
238
 
187
239
  # Increment the queue specific counter
188
240
  self.class.increment_counter_for_queue!(queue)
189
241
 
190
- # Now check if the queue specific quota has been met
191
- if !terminate && self.class.quota_met_for_queue?(queue)
192
- self.class.log_info(%(Max-Jobs quota met for queue: "#{queue}", shutting down pid: #{self.class.pid}))
242
+ # Last[ly], check if the queue quota has been met
243
+ if !terminate && self.class.max_jobs_quota_met_for_queue?(queue)
244
+ self.class.log_max_jobs_quota_met_for_queue!(queue)
193
245
  terminate = true
194
246
  end
195
247
 
196
- # If applicable, TERMinate the `Process`
197
- ::Process.kill('TERM', self.class.pid) if terminate
248
+ # If applicable, terminate
249
+ self.class.terminate! if terminate
198
250
  end
199
251
  end
200
252
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-max-jobs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan W. Zaleski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-18 00:00:00.000000000 Z
11
+ date: 2020-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq