sidekiq-killswitch 1.0.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 16a6f2e7ff8e7ad601d13c63b6572a6609b39344
4
- data.tar.gz: d7857259bdadd1b46545977bae4a313966990218
2
+ SHA256:
3
+ metadata.gz: d2c1e5555f67df5c51420ca3946edcf8786c622f7ee6d7c2ef238a3c2e42f639
4
+ data.tar.gz: 5607f5785571de88fae01a9abca4637bbeb35a2418db9cda87a29f0c6f5a1bcf
5
5
  SHA512:
6
- metadata.gz: f05064da2b923e14c65119dd6507060a462014dc8d6a52de31d1ef39794358912d6a91fe5d2028aa7974f50ee84ad5940a1ca3a6d7b4a303cc8de9bbe445c1c7
7
- data.tar.gz: e6fe7320e902ff8e884e26d0e21861cfa27c9b3a3882cde4b58d28514663a4a437af5dc95e96f597a75c8adfc0e746717e85424b18cf2b6274d2c3ded4e7e5b4
6
+ metadata.gz: 21e69f7d202a0c5f186d4f317bcb05ec8031522bd24c05bc7720be745e12f11280a5fd1f0902cf431dce750a63c9167409b36c5d4e92b9957fe916031f3a9699
7
+ data.tar.gz: 4399541da941edfef9a83eb555e80c1c5e98ffcb76160eaf487a06bc5415af4bcb4b5c4d0dcd299ad68d292509538f2b387b80a230fac56389b0431f0e3f1b31
data/CHANGES.md CHANGED
@@ -1,3 +1,12 @@
1
+ ### 1.1.1 / 2024-08-27
2
+ * Support for `redis-client` client (used by Sidekiq 7)
3
+ * Remove `Sidekiq::DeadSet#kill` extension.
4
+ * `module Sidekiq::Killswitch; end` replaced with `module Sidekiq; module Killswitch; ...`.
5
+ * We're now using `warn` instead of `info` to log worker blackholed/dead_queued messaged.
6
+
7
+ ### 1.1.0 / 2024-07-01
8
+ * Add in warning banner to Sidekiq UI so users are linked to docs and informed of the difference of sending jobs to blackhole vs. dead queue
9
+
1
10
  ### 1.0.0 / 2018-02-20
2
11
  * `1.0.0.pre2` released as `1.0.0`.
3
12
 
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,25 @@
1
+ # Contributing
2
+
3
+ We welcome contributions. For small changes, open a pull request. If you'd like to discuss an improvement or fix before writing the code, please open a Github issue.
4
+
5
+ ## Reporting Issues
6
+
7
+ Please report issues in Github issues.
8
+
9
+ # Table of Contents
10
+
11
+ - [Contributing](#contributing)
12
+ - [Reporting Issues](#reporting-issues)
13
+ - [Table of Contents](#table-of-contents)
14
+ - [Technical Details](#technical-details)
15
+ - [Local Development](#local-development)
16
+
17
+
18
+ ## Technical Details
19
+ ### Local Development
20
+ Clone the repo and open in your IDE of choice.
21
+
22
+ ### Releasing a new version
23
+ Tag the branch with the appropriate version number (ex v1.2.0) with `git tag v1.2.0`.
24
+
25
+ Push to the remote repo with `git push origin --tags`
data/Gemfile CHANGED
@@ -2,3 +2,5 @@
2
2
  source 'http://rubygems.org'
3
3
 
4
4
  gemspec
5
+
6
+ gem 'pry-byebug'
@@ -1,36 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Sidekiq::Killswitch
4
- class Config
5
- attr_accessor :web_ui_worker_validator
6
- attr_writer :logger
3
+ module Sidekiq
4
+ module Killswitch
5
+ class Config
6
+ attr_accessor :web_ui_worker_validator
7
+ attr_writer :logger
7
8
 
8
- def initialize
9
- self.web_ui_worker_validator = ->(worker_name) { !worker_name.nil? && worker_name != '' }
10
- end
9
+ def initialize
10
+ self.web_ui_worker_validator = ->(worker_name) { !worker_name.nil? && worker_name != '' }
11
+ end
11
12
 
12
- def logger
13
- @logger ||= Sidekiq.logger
14
- end
13
+ def logger
14
+ @logger ||= Sidekiq.logger
15
+ end
15
16
 
16
- def validate_worker_class_in_web
17
- self.web_ui_worker_validator = proc do |worker_name|
18
- begin
19
- constantize(worker_name).include?(Sidekiq::Worker)
20
- rescue NameError
21
- false
17
+ def validate_worker_class_in_web
18
+ self.web_ui_worker_validator = proc do |worker_name|
19
+ begin
20
+ constantize(worker_name).include?(Sidekiq::Worker)
21
+ rescue NameError
22
+ false
23
+ end
22
24
  end
23
25
  end
24
- end
25
26
 
26
- private
27
+ private
27
28
 
28
- def constantize(str)
29
- names = str.split('::')
30
- names.shift if names.empty? || names.first.empty?
29
+ def constantize(str)
30
+ names = str.split('::')
31
+ names.shift if names.empty? || names.first.empty?
31
32
 
32
- names.inject(Object) do |constant, name|
33
- constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
33
+ names.inject(Object) do |constant, name|
34
+ constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
35
+ end
34
36
  end
35
37
  end
36
38
  end
@@ -8,7 +8,7 @@ module Sidekiq
8
8
  worker_name = Sidekiq::Killswitch.class_to_str(worker_class)
9
9
 
10
10
  if Killswitch.blackhole_worker?(worker_name)
11
- Killswitch.logger.info "#{worker_name} is currently disabled. Job #{job} was not executed."
11
+ Killswitch.logger.warn "#{worker_name} is currently disabled. Job #{job} was not executed."
12
12
  false
13
13
  else
14
14
  yield
@@ -4,14 +4,16 @@ module Sidekiq
4
4
  module Killswitch
5
5
  module Middleware
6
6
  class Server
7
+ include Sidekiq::ServerMiddleware
8
+
7
9
  def call(worker, job, _queue)
8
10
  serialized_job = Sidekiq.dump_json(job)
9
11
 
10
12
  if Killswitch.dead_queue_worker?(worker.class)
11
13
  DeadSet.new.kill(serialized_job)
12
- Killswitch.logger.info "#{worker.class.name} marked as dead queue worker. Job #{serialized_job} was killed."
14
+ Killswitch.logger.warn "#{worker.class.name} marked as dead queue worker. Job #{serialized_job} was killed."
13
15
  elsif Killswitch.blackhole_worker?(worker.class)
14
- Killswitch.logger.info "#{worker.class.name} is currently disabled. Job #{serialized_job} was not executed."
16
+ Killswitch.logger.warn "#{worker.class.name} is currently disabled. Job #{serialized_job} was not executed."
15
17
  else
16
18
  yield
17
19
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sidekiq
4
4
  module Killswitch
5
- VERSION = '1.0.0'
5
+ VERSION = '1.1.1'
6
6
  end
7
7
  end
@@ -1,92 +1,100 @@
1
1
  # frozen_string_literal: true
2
- require 'sidekiq/extensions/dead_set'
3
2
  require 'sidekiq/killswitch/config'
4
3
  require 'sidekiq/killswitch/middleware/server'
5
4
  require 'sidekiq/killswitch/middleware/client'
6
5
 
7
- module Sidekiq::Killswitch
8
- BLACKHOLE_WORKERS_KEY_NAME = 'sidekiq.disabled-workers'
9
- DEAD_QUEUE_WORKERS_KEY_NAME = 'sidekiq.dead-queue-workers'
6
+ module Sidekiq
7
+ module Killswitch
8
+ BLACKHOLE_WORKERS_KEY_NAME = 'sidekiq.disabled-workers'
9
+ DEAD_QUEUE_WORKERS_KEY_NAME = 'sidekiq.dead-queue-workers'
10
10
 
11
- class << self
12
- def config
13
- @config ||= Config.new
14
- end
11
+ class << self
12
+ def config
13
+ @config ||= Config.new
14
+ end
15
15
 
16
- def configure(&block)
17
- yield config
18
- end
16
+ def configure(&block)
17
+ yield config
18
+ end
19
19
 
20
- def logger
21
- config.logger
22
- end
20
+ def logger
21
+ config.logger
22
+ end
23
23
 
24
- def redis_pool(&block)
25
- Sidekiq.redis(&block)
26
- end
24
+ def redis_pool(&block)
25
+ Sidekiq.redis(&block)
26
+ end
27
27
 
28
- def blackhole_add_worker(worker_name)
29
- worker_name = class_to_str(worker_name)
28
+ def blackhole_add_worker(worker_name)
29
+ worker_name = class_to_str(worker_name)
30
30
 
31
- redis_pool do |redis|
32
- redis.hset(BLACKHOLE_WORKERS_KEY_NAME, worker_name, Time.now)
31
+ redis_pool do |redis|
32
+ redis.hset(BLACKHOLE_WORKERS_KEY_NAME, worker_name, Time.now.to_s)
33
+ end
34
+ logger.warn "#{worker_name} added to blackhole workers"
33
35
  end
34
- logger.warn "#{worker_name} added to blackhole workers"
35
- end
36
36
 
37
- def blackhole_remove_worker(worker_name)
38
- worker_name = class_to_str(worker_name)
37
+ def blackhole_remove_worker(worker_name)
38
+ worker_name = class_to_str(worker_name)
39
39
 
40
- redis_pool do |redis|
41
- redis.hdel(BLACKHOLE_WORKERS_KEY_NAME, worker_name)
40
+ redis_pool do |redis|
41
+ redis.hdel(BLACKHOLE_WORKERS_KEY_NAME, worker_name)
42
+ end
43
+ logger.warn "#{worker_name} removed from blackhole workers"
42
44
  end
43
- logger.warn "#{worker_name} removed from blackhole workers"
44
- end
45
45
 
46
- def blackhole_worker?(worker_name)
47
- redis_pool do |redis|
48
- redis.hexists(BLACKHOLE_WORKERS_KEY_NAME, class_to_str(worker_name))
46
+ def blackhole_worker?(worker_name)
47
+ redis_pool do |redis|
48
+ is_true_compat(redis.hexists(BLACKHOLE_WORKERS_KEY_NAME, class_to_str(worker_name)))
49
+ end
49
50
  end
50
- end
51
51
 
52
- def blackhole_workers
53
- redis_pool do |redis|
54
- redis.hgetall(BLACKHOLE_WORKERS_KEY_NAME)
52
+ def blackhole_workers
53
+ redis_pool do |redis|
54
+ redis.hgetall(BLACKHOLE_WORKERS_KEY_NAME)
55
+ end
55
56
  end
56
- end
57
57
 
58
- def dead_queue_add_worker(worker_name)
59
- worker_name = class_to_str(worker_name)
58
+ def dead_queue_add_worker(worker_name)
59
+ worker_name = class_to_str(worker_name)
60
60
 
61
- redis_pool do |redis|
62
- redis.hset(DEAD_QUEUE_WORKERS_KEY_NAME, worker_name, Time.now)
61
+ redis_pool do |redis|
62
+ redis.hset(DEAD_QUEUE_WORKERS_KEY_NAME, worker_name, Time.now.to_s)
63
+ end
64
+ logger.warn "#{worker_name} added to dead queue workers"
63
65
  end
64
- logger.warn "#{worker_name} added to dead queue workers"
65
- end
66
66
 
67
- def dead_queue_remove_worker(worker_name)
68
- worker_name = class_to_str(worker_name)
67
+ def dead_queue_remove_worker(worker_name)
68
+ worker_name = class_to_str(worker_name)
69
69
 
70
- redis_pool do |redis|
71
- redis.hdel(DEAD_QUEUE_WORKERS_KEY_NAME, worker_name)
70
+ redis_pool do |redis|
71
+ redis.hdel(DEAD_QUEUE_WORKERS_KEY_NAME, worker_name)
72
+ end
73
+ logger.warn "#{worker_name} removed from dead queue workers"
72
74
  end
73
- logger.warn "#{worker_name} removed from dead queue workers"
74
- end
75
75
 
76
- def dead_queue_worker?(worker_name)
77
- redis_pool do |redis|
78
- redis.hexists(DEAD_QUEUE_WORKERS_KEY_NAME, class_to_str(worker_name))
76
+ def dead_queue_worker?(worker_name)
77
+ redis_pool do |redis|
78
+ is_true_compat(redis.hexists(DEAD_QUEUE_WORKERS_KEY_NAME, class_to_str(worker_name)))
79
+ end
79
80
  end
80
- end
81
81
 
82
- def dead_queue_workers
83
- redis_pool do |redis|
84
- redis.hgetall(DEAD_QUEUE_WORKERS_KEY_NAME)
82
+ def dead_queue_workers
83
+ redis_pool do |redis|
84
+ redis.hgetall(DEAD_QUEUE_WORKERS_KEY_NAME)
85
+ end
86
+ end
87
+
88
+ def class_to_str(class_or_string)
89
+ class_or_string.is_a?(String) ? class_or_string : class_or_string.name
85
90
  end
86
- end
87
91
 
88
- def class_to_str(class_or_string)
89
- class_or_string.is_a?(String) ? class_or_string : class_or_string.name
92
+ private
93
+
94
+ def is_true_compat(redis_result_value)
95
+ # Compatibility method for `redis` and `redis-client`.
96
+ redis_result_value == 1 || redis_result_value == true
97
+ end
90
98
  end
91
99
  end
92
100
  end
@@ -65,7 +65,7 @@ RSpec.describe Sidekiq::Killswitch do
65
65
  Sidekiq::Killswitch.blackhole_remove_worker(worker_name)
66
66
 
67
67
  Sidekiq::Killswitch.redis_pool do |redis|
68
- expect(redis.hexists(Sidekiq::Killswitch::BLACKHOLE_WORKERS_KEY_NAME, worker_name)).to be_falsey
68
+ expect(redis.hexists(Sidekiq::Killswitch::BLACKHOLE_WORKERS_KEY_NAME, worker_name)).to eq(0)
69
69
  end
70
70
  end
71
71
  end
@@ -113,7 +113,7 @@ RSpec.describe Sidekiq::Killswitch do
113
113
  Sidekiq::Killswitch.dead_queue_remove_worker(worker_name)
114
114
 
115
115
  Sidekiq::Killswitch.redis_pool do |redis|
116
- expect(redis.hexists(Sidekiq::Killswitch::DEAD_QUEUE_WORKERS_KEY_NAME, worker_name)).to be_falsey
116
+ expect(redis.hexists(Sidekiq::Killswitch::DEAD_QUEUE_WORKERS_KEY_NAME, worker_name)).to eq(0)
117
117
  end
118
118
  end
119
119
  end
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
  require 'rspec'
3
3
  require 'logger'
4
+ require 'rack/test'
4
5
  require 'sidekiq/testing'
5
6
  require 'sidekiq/killswitch'
6
7
 
7
8
  ENV['RACK_ENV'] = 'test' # Disable CSRF protection for Sidekiq Web app
9
+ $TESTING = true
8
10
 
9
11
  Sidekiq::Killswitch.configure do |config|
10
12
  config.logger = Logger.new('/dev/null')
data/spec/web_spec.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require 'spec_helper'
3
- require 'rack/test'
4
3
  require 'rspec-html-matchers'
5
4
  require 'sidekiq/killswitch/web'
6
5
 
@@ -10,6 +9,12 @@ RSpec.describe Sidekiq::Killswitch::Web do
10
9
 
11
10
  let(:app) { Sidekiq::Web }
12
11
 
12
+ before do
13
+ # Hash is not a real session, but it seems to provide a session-compatible interface,
14
+ # and we're only using #[] and #delete methods.
15
+ env "rack.session", {}
16
+ end
17
+
13
18
  def expect_redirect_to_root_page(response)
14
19
  expect(response.status).to be(302)
15
20
  expect(response.headers['Location']).to eq("http://#{rack_mock_session.default_host}/kill-switches")
@@ -4,6 +4,16 @@
4
4
 
5
5
  <h3><%= t('blackholed_workers')%></h3>
6
6
 
7
+ <div class="alert alert-danger" role="alert">
8
+ <p>Blackholing workers drops instances of these workers when these instances are pushed to the Sidekiq queue or processed (<a
9
+ href="https://github.com/square/sidekiq-killswitch?tab=readme-ov-file#how-does-it-work" class="alert-link">docs</a>).</p>
10
+
11
+ <p>Only blackhole workers that you don't want to retry later! (e.g. cron jobs or jobs you don't care about) You should be able to reach a state
12
+ of correctness without retrying these workers.</p>
13
+
14
+ <p>Use <%= t('dead_queue_workers') %> if you want to retry these jobs later from the dead queue.</p>
15
+ </div>
16
+
7
17
  <form action="<%= root_path %>kill-switches/blackhole_add" method="post" style="margin-bottom: 4px;">
8
18
  <%= csrf_tag %>
9
19
  <input name="worker_name" />
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-killswitch
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuriy Naidyon
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-20 00:00:00.000000000 Z
11
+ date: 2024-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq
@@ -80,7 +80,7 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 0.49.1
83
- description:
83
+ description:
84
84
  email: yurokle@gmail.com
85
85
  executables: []
86
86
  extensions: []
@@ -89,13 +89,13 @@ files:
89
89
  - ".gitignore"
90
90
  - ".rubocop.yml"
91
91
  - CHANGES.md
92
+ - CONTRIBUTING.md
92
93
  - Gemfile
93
94
  - LICENSE
94
95
  - README.md
95
96
  - Rakefile
96
97
  - examples/killswitch-web-ui.png
97
98
  - examples/square-logo.png
98
- - lib/sidekiq/extensions/dead_set.rb
99
99
  - lib/sidekiq/killswitch.rb
100
100
  - lib/sidekiq/killswitch/config.rb
101
101
  - lib/sidekiq/killswitch/middleware/client.rb
@@ -105,7 +105,6 @@ files:
105
105
  - lib/sidekiq/killswitch/web.rb
106
106
  - sidekiq-killswitch.gemspec
107
107
  - spec/config_spec.rb
108
- - spec/dead_set_spec.rb
109
108
  - spec/killswitch_spec.rb
110
109
  - spec/middleware/client_spec.rb
111
110
  - spec/middleware/server_spec.rb
@@ -118,7 +117,7 @@ licenses:
118
117
  - Apache-2.0
119
118
  metadata:
120
119
  allowed_push_host: https://rubygems.org
121
- post_install_message:
120
+ post_install_message:
122
121
  rdoc_options: []
123
122
  require_paths:
124
123
  - lib
@@ -133,14 +132,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
132
  - !ruby/object:Gem::Version
134
133
  version: '0'
135
134
  requirements: []
136
- rubyforge_project:
137
- rubygems_version: 2.6.12
138
- signing_key:
135
+ rubygems_version: 3.3.26
136
+ signing_key:
139
137
  specification_version: 4
140
138
  summary: Cross-host Sidekiq worker killswitches
141
139
  test_files:
142
140
  - spec/config_spec.rb
143
- - spec/dead_set_spec.rb
144
141
  - spec/killswitch_spec.rb
145
142
  - spec/middleware/client_spec.rb
146
143
  - spec/middleware/server_spec.rb
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'sidekiq/api'
3
-
4
- # this is a monkey-patch!
5
- # TODO: delete this patch after we explicitly depend on Sidekiq version that
6
- # includes this PR: https://github.com/mperham/sidekiq/pull/3573
7
-
8
- module Sidekiq
9
- module DeadSetKill
10
- def kill(message)
11
- now = Time.now.to_f
12
- Sidekiq.redis do |conn|
13
- conn.multi do
14
- conn.zadd(name, now.to_f.to_s, message)
15
- conn.zremrangebyscore(name, '-inf', now - self.class.timeout)
16
- conn.zremrangebyrank(name, 0, - self.class.max_jobs)
17
- end
18
- end
19
- end
20
- end
21
- end
22
-
23
- Sidekiq::DeadSet.include(Sidekiq::DeadSetKill) unless Sidekiq::DeadSet.method_defined?(:kill)
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'spec_helper'
3
-
4
- RSpec.describe Sidekiq::DeadSet do
5
- let(:dead_set) { Sidekiq::DeadSet.new }
6
-
7
- describe '#kill' do
8
- it 'should put passed serialized job to the "dead" sorted set' do
9
- serialized_job = Sidekiq.dump_json(jid: '123123', class: 'SomeWorker', args: [])
10
- dead_set.kill(serialized_job)
11
-
12
- expect(dead_set.find_job('123123').value).to eq(serialized_job)
13
- end
14
-
15
- it 'should remove dead jobs older than Sidekiq::DeadSet.timeout' do
16
- allow(Sidekiq::DeadSet).to receive(:timeout).and_return(10)
17
- time_now = Time.now
18
-
19
- stub_time_now(time_now - 11)
20
- dead_set.kill(Sidekiq.dump_json({jid: '000103', class: 'MyWorker3', args: []})) # the oldest
21
-
22
- stub_time_now(time_now - 9)
23
- dead_set.kill(Sidekiq.dump_json({jid: '000102', class: 'MyWorker2', args: []}))
24
-
25
- stub_time_now(time_now)
26
- dead_set.kill(Sidekiq.dump_json({jid: '000101', class: 'MyWorker1', args: []}))
27
-
28
- stub_time_now(time_now)
29
-
30
- expect(dead_set.find_job('000103')).to be_falsey
31
- expect(dead_set.find_job('000102')).to be_truthy
32
- expect(dead_set.find_job('000101')).to be_truthy
33
- end
34
-
35
- it 'should remove all but last Sidekiq::DeadSet.max_jobs-1 jobs' do
36
- allow(Sidekiq::DeadSet).to receive(:max_jobs).and_return(3)
37
-
38
- dead_set.kill(Sidekiq.dump_json({jid: '000101', class: 'MyWorker1', args: []}))
39
- dead_set.kill(Sidekiq.dump_json({jid: '000102', class: 'MyWorker2', args: []}))
40
- dead_set.kill(Sidekiq.dump_json({jid: '000103', class: 'MyWorker3', args: []}))
41
-
42
- expect(dead_set.find_job('000101')).to be_falsey
43
- expect(dead_set.find_job('000102')).to be_truthy
44
- expect(dead_set.find_job('000103')).to be_truthy
45
- end
46
- end
47
- end