sidekiq-killswitch 1.1.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
2
  SHA256:
3
- metadata.gz: 19e742c53121e79d47f431622fb85caaa363db4e588465fcebbef34fd24b4c66
4
- data.tar.gz: '073755898e20e6190cc4578b1094b42a8880a09e7c7825be55a27ad9ff585165'
3
+ metadata.gz: d2c1e5555f67df5c51420ca3946edcf8786c622f7ee6d7c2ef238a3c2e42f639
4
+ data.tar.gz: 5607f5785571de88fae01a9abca4637bbeb35a2418db9cda87a29f0c6f5a1bcf
5
5
  SHA512:
6
- metadata.gz: bb06743e6f30e348cf7187ac93853edde5a26c06764f52b2fd273cf6635456582d093da42632a1129dc218adf078068e6fb504441b059a46de818bdc726bbc5b
7
- data.tar.gz: 071ba3570e2ded8313c637b39442e92f2c5d781751d49efc294057c2e272b73a2136dc017e810fccb99744c29f5cf18a73a29c485aad7a27cd94eae0c8b19b12
6
+ metadata.gz: 21e69f7d202a0c5f186d4f317bcb05ec8031522bd24c05bc7720be745e12f11280a5fd1f0902cf431dce750a63c9167409b36c5d4e92b9957fe916031f3a9699
7
+ data.tar.gz: 4399541da941edfef9a83eb555e80c1c5e98ffcb76160eaf487a06bc5415af4bcb4b5c4d0dcd299ad68d292509538f2b387b80a230fac56389b0431f0e3f1b31
data/CHANGES.md CHANGED
@@ -1,3 +1,9 @@
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
+
1
7
  ### 1.1.0 / 2024-07-01
2
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
3
9
 
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.1.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")
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.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuriy Naidyon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-01 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
@@ -96,7 +96,6 @@ files:
96
96
  - Rakefile
97
97
  - examples/killswitch-web-ui.png
98
98
  - examples/square-logo.png
99
- - lib/sidekiq/extensions/dead_set.rb
100
99
  - lib/sidekiq/killswitch.rb
101
100
  - lib/sidekiq/killswitch/config.rb
102
101
  - lib/sidekiq/killswitch/middleware/client.rb
@@ -106,7 +105,6 @@ files:
106
105
  - lib/sidekiq/killswitch/web.rb
107
106
  - sidekiq-killswitch.gemspec
108
107
  - spec/config_spec.rb
109
- - spec/dead_set_spec.rb
110
108
  - spec/killswitch_spec.rb
111
109
  - spec/middleware/client_spec.rb
112
110
  - spec/middleware/server_spec.rb
@@ -134,13 +132,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
132
  - !ruby/object:Gem::Version
135
133
  version: '0'
136
134
  requirements: []
137
- rubygems_version: 3.4.19
135
+ rubygems_version: 3.3.26
138
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