kubra-sidekiq-throttled 1.5.3 → 2.0.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: 0c8b57e15f1580876256ff151b057c188cea5816b035add8a9ee758031c54b79
4
- data.tar.gz: 0fdcbdf93cf808e475f7a01c41658edc08d7e9e256eb7581d793fb72a87a4cf0
3
+ metadata.gz: 98dc070784c42ddf555bbf84a5df49cf460efe2e5ccde93887498ca6295a5bc7
4
+ data.tar.gz: f2241d460e0879cb2153eac52ea4d06fd109ffd92a960d74f04fc75193d429db
5
5
  SHA512:
6
- metadata.gz: 4a59f04af06b3a5c97f331fe4963d0ab769e5496a04c47bb139290b1ee0b9f8b8e183bd0c5b32a1b03e1115bc04332a13cfb0cb496b51b8a21d8c503e84e95c4
7
- data.tar.gz: 7860e9a98c711808b662121e219991636225e39708cbc7e81045211d172723a8fe61474deba3a2116465363d715cc9237aa4a714d86ccbed6f6f5c1f33a07e65
6
+ metadata.gz: bdacff8274dcbda5d30fcc798935e25ea5cdf4e1533f5895dd29346d42aacdfdb14bf5d99ea1edd95907dfcd43a7c492f8bca14393b8044d9f0b45f78ad8c5bf
7
+ data.tar.gz: 69de366c927ffdc47d18cc74753b5c325821ad8ffafa9786b4ff2344502b75ea343f630914ac22908de7240499cf07d8e349c8c6a8e1f83ba0db88052b713803
data/README.adoc CHANGED
@@ -351,11 +351,9 @@ sidekiq_throttle(
351
351
 
352
352
  This library aims to support and is tested against the following Ruby versions:
353
353
 
354
- * Ruby 2.7.x
355
- * Ruby 3.0.x
356
- * Ruby 3.1.x
357
354
  * Ruby 3.2.x
358
355
  * Ruby 3.3.x
356
+ * Ruby 3.4.x
359
357
 
360
358
  If something doesn't work on one of these versions, it's a bug.
361
359
 
@@ -375,15 +373,11 @@ dropped.
375
373
 
376
374
  This library aims to support and work with following Sidekiq versions:
377
375
 
378
- * Sidekiq 7.0.x
379
- * Sidekiq 7.1.x
380
- * Sidekiq 7.2.x
376
+ * Sidekiq 8.0.x
381
377
 
382
378
  And the following Sidekiq Pro versions:
383
379
 
384
- * Sidekiq Pro 7.0.x
385
- * Sidekiq Pro 7.1.x
386
- * Sidekiq Pro 7.2.x
380
+ * Sidekiq Pro 8.0.x
387
381
 
388
382
  == Development
389
383
 
@@ -398,6 +392,7 @@ If you're working on Sidekiq-Pro support make sure that you have Sidekiq-Pro
398
392
  license set either in the global config, or in `BUNDLE_GEMS\__CONTRIBSYS__COM`
399
393
  environment variable.
400
394
 
395
+
401
396
  == Contributing
402
397
 
403
398
  * Fork sidekiq-throttled on GitHub
@@ -51,7 +51,7 @@ module Sidekiq
51
51
 
52
52
  # @!attribute [w] default_requeue_options
53
53
  def default_requeue_options=(options)
54
- requeue_with = options.delete(:with).intern || :enqueue
54
+ requeue_with = options.delete(:with)&.to_sym || :enqueue
55
55
 
56
56
  @default_requeue_options = options.merge({ with: requeue_with })
57
57
  end
@@ -88,8 +88,8 @@ module Sidekiq
88
88
  # @param [Hash] requeue What to do with jobs that are throttled
89
89
  # @see Registry.add
90
90
  # @return [void]
91
- def sidekiq_throttle(**kwargs)
92
- Registry.add(self, **kwargs)
91
+ def sidekiq_throttle(**)
92
+ Registry.add(self, **)
93
93
  end
94
94
 
95
95
  # Adds current worker to preconfigured throttling strategy. Allows
@@ -17,10 +17,10 @@ module Sidekiq
17
17
  #
18
18
  # @param (see Strategy#initialize)
19
19
  # @return [Strategy]
20
- def add(name, **kwargs)
20
+ def add(name, **)
21
21
  name = name.to_s
22
22
 
23
- @strategies[name] = Strategy.new(name, **kwargs)
23
+ @strategies[name] = Strategy.new(name, **)
24
24
  end
25
25
 
26
26
  # Adds alias for existing strategy.
@@ -14,7 +14,11 @@ module Sidekiq
14
14
  key = @base_key.dup
15
15
  return key unless @key_suffix
16
16
 
17
- key << ":#{@key_suffix.call(*job_args)}"
17
+ key << ":#{key_suffix(job_args)}"
18
+ end
19
+
20
+ def key_suffix(job_args)
21
+ @key_suffix.respond_to?(:call) ? @key_suffix.call(*job_args) : @key_suffix
18
22
  rescue StandardError => e
19
23
  Sidekiq.logger.error "Failed to get key suffix: #{e}"
20
24
  raise e
@@ -19,8 +19,8 @@ end
19
19
  -- can cause unnecessary delays in job processing. Underestimates are much
20
20
  -- safer as they only increase workload of sidekiq processors.
21
21
  local function est_current_backlog_size()
22
- local old_size = tonumber(redis.call("HGET", backlog_info_key, "size") or 0)
23
- local old_timestamp = tonumber(redis.call("HGET", backlog_info_key, "timestamp") or now)
22
+ local old_size = tonumber(redis.call("HGET", backlog_info_key, "size")) or 0
23
+ local old_timestamp = tonumber(redis.call("HGET", backlog_info_key, "timestamp")) or now
24
24
 
25
25
  local jobs_lost_since_old_timestamp = (now - old_timestamp) / lost_job_threshold * lmt
26
26
 
@@ -31,13 +31,18 @@ module Sidekiq
31
31
  # @param [#to_i] lost_job_threshold Seconds to wait before considering
32
32
  # a job lost or dead. Default: 900 or 3 * avg_job_duration
33
33
  # @param [Proc] key_suffix Dynamic key suffix generator.
34
+ # @param [#to_i] max_delay Maximum number of seconds to delay a job when it
35
+ # throttled. This prevents jobs from being schedule very far in the future
36
+ # when the backlog is large. Default: the smaller of 30 minutes or 10 * avg_job_duration
34
37
  # @deprecated @param [#to_i] ttl Obsolete alias for `lost_job_threshold`.
35
38
  # Default: 900 or 3 * avg_job_duration
36
- def initialize(strategy_key, limit:, avg_job_duration: nil, ttl: nil, lost_job_threshold: ttl, key_suffix: nil) # rubocop:disable Metrics/ParameterLists
39
+ def initialize(strategy_key, limit:, avg_job_duration: nil, ttl: nil, # rubocop:disable Metrics/ParameterLists
40
+ lost_job_threshold: ttl, key_suffix: nil, max_delay: nil)
37
41
  @base_key = "#{strategy_key}:concurrency.v2"
38
42
  @limit = limit
39
43
  @avg_job_duration, @lost_job_threshold = interp_duration_args(avg_job_duration, lost_job_threshold)
40
44
  @key_suffix = key_suffix
45
+ @max_delay = max_delay || [(10 * @avg_job_duration), 1_800].min
41
46
 
42
47
  raise(ArgumentError, "lost_job_threshold must be greater than avg_job_duration") if
43
48
  @lost_job_threshold <= @avg_job_duration
@@ -57,7 +62,7 @@ module Sidekiq
57
62
  keys = [key(job_args), backlog_info_key(job_args)]
58
63
  argv = [jid.to_s, job_limit, @lost_job_threshold, Time.now.to_f]
59
64
 
60
- Sidekiq.redis { |redis| (1 == SCRIPT.call(redis, keys: keys, argv: argv)) }
65
+ Sidekiq.redis { |redis| 1 == SCRIPT.call(redis, keys: keys, argv: argv) }
61
66
  end
62
67
 
63
68
  # @return [Float] How long, in seconds, before we'll next be able to take on jobs
@@ -65,7 +70,8 @@ module Sidekiq
65
70
  job_limit = limit(job_args)
66
71
  return 0.0 if !job_limit || count(*job_args) < job_limit
67
72
 
68
- estimated_backlog_size(job_args) * @avg_job_duration / limit(job_args)
73
+ (estimated_backlog_size(job_args) * @avg_job_duration / limit(job_args))
74
+ .then { |delay_sec| @max_delay * (1 - Math.exp(-delay_sec / @max_delay)) } # limit to max_delay
69
75
  end
70
76
 
71
77
  # @return [Integer] Current count of jobs
@@ -99,17 +105,13 @@ module Sidekiq
99
105
  old_size = (old_size_str || 0).to_f
100
106
  old_timestamp = (old_timestamp_str || Time.now).to_f
101
107
 
102
- nonneg(old_size - jobs_lost_since(old_timestamp, job_args))
108
+ (old_size - jobs_lost_since(old_timestamp, job_args)).clamp(0, Float::INFINITY)
103
109
  end
104
110
 
105
111
  def jobs_lost_since(timestamp, job_args)
106
112
  (Time.now.to_f - timestamp) / @lost_job_threshold * limit(job_args)
107
113
  end
108
114
 
109
- def nonneg(number)
110
- [number, 0].max
111
- end
112
-
113
115
  def interp_duration_args(avg_job_duration, lost_job_threshold)
114
116
  if avg_job_duration && lost_job_threshold
115
117
  [avg_job_duration.to_i, lost_job_threshold.to_i]
@@ -3,6 +3,6 @@
3
3
  module Sidekiq
4
4
  module Throttled
5
5
  # Gem version
6
- VERSION = "1.5.3"
6
+ VERSION = "2.0.0"
7
7
  end
8
8
  end
@@ -62,12 +62,13 @@ module Sidekiq
62
62
 
63
63
  # @return [String]
64
64
  def humanize_integer(int)
65
- digits = int.to_s.chars
66
- str = digits.shift(digits.count % 3).join
67
-
68
- str << " " << digits.shift(3).join while digits.count.positive?
69
-
70
- str.strip
65
+ int.to_s.chars
66
+ .reverse
67
+ .each_slice(3)
68
+ .map(&:reverse)
69
+ .reverse
70
+ .map(&:join)
71
+ .join(",")
71
72
  end
72
73
  end
73
74
  end
@@ -1,43 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # stdlib
4
3
  require "pathname"
5
4
 
6
- # 3rd party
7
5
  require "sidekiq"
8
6
  require "sidekiq/web"
9
7
 
10
- # internal
11
8
  require_relative "./registry"
12
9
  require_relative "./web/stats"
13
10
 
14
11
  module Sidekiq
15
12
  module Throttled
16
- # Provides Sidekiq tab to monitor and reset throttled stats.
17
13
  module Web
18
- VIEWS = Pathname.new(__dir__).join("web")
19
- THROTTLED_TPL = VIEWS.join("throttled.html.erb").read.freeze
14
+ ROOT = Pathname.new(__dir__).join("../../../web").expand_path.realpath.freeze
15
+ VIEWS = ROOT.join("views").freeze
20
16
 
21
- class << self
22
- # @api private
23
- def registered(app)
24
- register_throttled_tab app
17
+ def self.registered(app)
18
+ app.get("/throttled") do
19
+ erb :index, views: VIEWS
25
20
  end
26
21
 
27
- private
28
-
29
- def register_throttled_tab(app)
30
- app.get("/throttled") { erb THROTTLED_TPL.dup }
31
-
32
- app.post("/throttled/:id/reset") do
33
- Registry.get(params[:id], &:reset!)
34
- redirect "#{root_path}throttled"
35
- end
22
+ app.post("/throttled/:id/reset") do
23
+ Registry.get(route_params(:id), &:reset!)
24
+ redirect "#{root_path}throttled"
36
25
  end
37
26
  end
38
27
  end
39
28
  end
40
29
  end
41
30
 
42
- Sidekiq::Web.register Sidekiq::Throttled::Web
43
- Sidekiq::Web.tabs["Throttled"] = "throttled"
31
+ Sidekiq::Web.configure do |config|
32
+ config.register_extension(
33
+ Sidekiq::Throttled::Web,
34
+ name: "throttled",
35
+ tab: %w[Throttled],
36
+ index: %w[throttled],
37
+ root_dir: Sidekiq::Throttled::Web::ROOT.to_s
38
+ )
39
+ end
@@ -0,0 +1,41 @@
1
+ <section>
2
+ <header>
3
+ <h1>Throttled</h1>
4
+ </header>
5
+
6
+ <div class="table_container">
7
+ <table class="throttled">
8
+ <thead>
9
+ <tr>
10
+ <th>Name</th>
11
+ <th>Concurrency</th>
12
+ <th>Threshold</th>
13
+ <th>Actions</th>
14
+ </tr>
15
+ </thead>
16
+ <tbody>
17
+ <% Sidekiq::Throttled::Registry.each_with_static_keys do |name, strategy| %>
18
+ <tr>
19
+ <td style="vertical-align:middle;"><%= name %></td>
20
+ <td style="vertical-align:middle;text-align:center;">
21
+ <% strategy.concurrency.each do |concurrency| %>
22
+ <%= Sidekiq::Throttled::Web::Stats.new(concurrency).to_html %>
23
+ <% end %>
24
+ </td>
25
+ <td style="vertical-align:middle;text-align:center;">
26
+ <% strategy.threshold.each do |threshold| %>
27
+ <%= Sidekiq::Throttled::Web::Stats.new(threshold).to_html %>
28
+ <% end %>
29
+ </td>
30
+ <td style="vertical-align:middle;text-align:center;">
31
+ <form action="<%= root_path %>throttled/<%= CGI.escape name %>/reset" method="post">
32
+ <%= csrf_tag %>
33
+ <button class="btn btn-danger" type="submit">Reset</button>
34
+ </form>
35
+ </td>
36
+ </tr>
37
+ <% end %>
38
+ </tbody>
39
+ </table>
40
+ </div>
41
+ </section>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kubra-sidekiq-throttled
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.3
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Williams
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2025-03-25 00:00:00.000000000 Z
12
+ date: 2025-10-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: concurrent-ruby
@@ -45,18 +45,17 @@ dependencies:
45
45
  requirements:
46
46
  - - ">="
47
47
  - !ruby/object:Gem::Version
48
- version: '6.5'
48
+ version: '8.0'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - ">="
54
54
  - !ruby/object:Gem::Version
55
- version: '6.5'
55
+ version: '8.0'
56
56
  description:
57
57
  email:
58
- - peter.williams@kubra.com
59
- - alexey@zapparov.com
58
+ - pezra@barelyenough.org
60
59
  executables: []
61
60
  extensions: []
62
61
  extra_rdoc_files: []
@@ -85,16 +84,16 @@ files:
85
84
  - lib/sidekiq/throttled/version.rb
86
85
  - lib/sidekiq/throttled/web.rb
87
86
  - lib/sidekiq/throttled/web/stats.rb
88
- - lib/sidekiq/throttled/web/throttled.html.erb
89
87
  - lib/sidekiq/throttled/worker.rb
90
- homepage: https://github.com/pezra/sidekiq-throttled
88
+ - web/views/index.erb
89
+ homepage: https://github.com/ixti/sidekiq-throttled
91
90
  licenses:
92
91
  - MIT
93
92
  metadata:
94
- homepage_uri: https://github.com/pezra/sidekiq-throttled
95
- source_code_uri: https://github.com/pezra/sidekiq-throttled/tree/v1.5.3
96
- bug_tracker_uri: https://github.com/pezra/sidekiq-throttled/issues
97
- changelog_uri: https://github.com/pezra/sidekiq-throttled/blob/v1.5.3/CHANGES.md
93
+ homepage_uri: https://github.com/ixti/sidekiq-throttled
94
+ source_code_uri: https://github.com/ixti/sidekiq-throttled/tree/v2.0.0
95
+ bug_tracker_uri: https://github.com/ixti/sidekiq-throttled/issues
96
+ changelog_uri: https://github.com/ixti/sidekiq-throttled/blob/v2.0.0/CHANGES.md
98
97
  rubygems_mfa_required: 'true'
99
98
  post_install_message:
100
99
  rdoc_options: []
@@ -104,16 +103,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
104
103
  requirements:
105
104
  - - ">="
106
105
  - !ruby/object:Gem::Version
107
- version: '2.7'
106
+ version: '3.2'
108
107
  required_rubygems_version: !ruby/object:Gem::Requirement
109
108
  requirements:
110
109
  - - ">="
111
110
  - !ruby/object:Gem::Version
112
111
  version: '0'
113
112
  requirements: []
114
- rubygems_version: 3.1.6
113
+ rubygems_version: 3.4.19
115
114
  signing_key:
116
115
  specification_version: 4
117
- summary: Concurrency and rate-limit throttling for Sidekiq (w/ high volume throttling
118
- improvements) )
116
+ summary: Concurrency and rate-limit throttling for Sidekiq (with improvements)
119
117
  test_files: []
@@ -1,35 +0,0 @@
1
- <h3>Throttled</h3>
2
-
3
- <div class="table_container">
4
- <table class="table table-hover table-bordered table-striped table-white">
5
- <thead>
6
- <tr>
7
- <th>Name</th>
8
- <th style="text-align:center;">Concurrency</th>
9
- <th style="text-align:center;">Threshold</th>
10
- <th style="text-align:center;">Actions</th>
11
- </tr>
12
- </thead>
13
- <% Sidekiq::Throttled::Registry.each_with_static_keys do |name, strategy| %>
14
- <tr>
15
- <td style="vertical-align:middle;"><%= name %></td>
16
- <td style="vertical-align:middle;text-align:center;">
17
- <% strategy.concurrency.each do |concurrency| %>
18
- <%= Sidekiq::Throttled::Web::Stats.new(concurrency).to_html %>
19
- <% end %>
20
- </td>
21
- <td style="vertical-align:middle;text-align:center;">
22
- <% strategy.threshold.each do |threshold| %>
23
- <%= Sidekiq::Throttled::Web::Stats.new(threshold).to_html %>
24
- <% end %>
25
- </td>
26
- <td style="vertical-align:middle;text-align:center;">
27
- <form action="<%= root_path %>throttled/<%= CGI.escape name %>/reset" method="post">
28
- <%= csrf_tag %>
29
- <button class="btn btn-danger" type="submit">Reset</button>
30
- </form>
31
- </td>
32
- </tr>
33
- <% end %>
34
- </table>
35
- </div>