Skip to content

Commit

Permalink
feat(digest): allow modern algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
mhenrixon committed Jul 25, 2024
1 parent bdca185 commit 1506b61
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 26 deletions.
1 change: 1 addition & 0 deletions .github/workflows/rspec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ jobs:
- sidekiq_7.0
- sidekiq_7.1
- sidekiq_7.2
- sidekiq_7.3

steps:
- uses: actions/checkout@v4
Expand Down
4 changes: 4 additions & 0 deletions Appraisals
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ end
appraise "sidekiq-7.2" do
gem "sidekiq", "~> 7.2.0"
end

appraise "sidekiq-7.3" do
gem "sidekiq", "~> 7.3.0"
end
35 changes: 24 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Want to show me some ❤️ for the hard work I do on this gem? You can use the
- [sidekiq-global_id](#sidekiq-global_id)
- [sidekiq-status](#sidekiq-status)
- [Global Configuration](#global-configuration)
- [digest_algorithm](#digest_algorithm)
- [debug_lua](#debug_lua)
- [lock_timeout](#lock_timeout)
- [lock_ttl](#lock_ttl)
Expand Down Expand Up @@ -734,17 +735,29 @@ Configure SidekiqUniqueJobs in an initializer or the sidekiq initializer on appl

```ruby
SidekiqUniqueJobs.configure do |config|
config.logger = Sidekiq.logger # default, change at your own discretion
config.logger_enabled = true # default, disable for test environments
config.debug_lua = false # Turn on when debugging
config.lock_info = false # Turn on when debugging
config.lock_ttl = 600 # Expire locks after 10 minutes
config.lock_timeout = nil # turn off lock timeout
config.max_history = 0 # Turn on when debugging
config.reaper = :ruby # :ruby, :lua or :none/nil
config.reaper_count = 1000 # Stop reaping after this many keys
config.reaper_interval = 600 # Reap orphans every 10 minutes
config.reaper_timeout = 150 # Timeout reaper after 2.5 minutes
config.logger = Sidekiq.logger # default, change at your own discretion
config.logger_enabled = true # default, disable for test environments
config.debug_lua = false # Turn on when debugging
config.lock_info = false # Turn on when debugging
config.lock_ttl = 600 # Expire locks after 10 minutes
config.lock_timeout = nil # turn off lock timeout
config.max_history = 0 # Turn on when debugging
config.reaper = :ruby # :ruby, :lua or :none/nil
config.reaper_count = 1000 # Stop reaping after this many keys
config.reaper_interval = 600 # Reap orphans every 10 minutes
config.reaper_timeout = 150 # Timeout reaper after 2.5 minutes
config.digest_algorithm = :modern # Timeout reaper after 2.5 minutes
end
```
#### digest_algorithm

For backwards compatibility this one is set to `:legacy` by the default. If you happen to run into issues with FIPS being enabled on your redis server you might want to set this to `:modern`.

See: https://github.com/mhenrixon/sidekiq-unique-jobs/issues/848 for explanation

```ruby
SidekiqUniqueJobs.configure do |config|
config.digest_algorithm = :modern # Timeout reaper after 2.5 minutes
end
```

Expand Down
2 changes: 1 addition & 1 deletion gemfiles/sidekiq_7.1.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ gem "sinatra"
gem "timecop"
gem "toxiproxy"
gem "yard"
gem "sidekiq", "~> 7.0.0"
gem "sidekiq", "~> 7.1.0"

platforms :mri do
gem "concurrent-ruby-ext"
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/sidekiq_7.2.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ gem "sinatra"
gem "timecop"
gem "toxiproxy"
gem "yard"
gem "sidekiq", "~> 7.0.0"
gem "sidekiq", "~> 7.2.0"

platforms :mri do
gem "concurrent-ruby-ext"
Expand Down
28 changes: 28 additions & 0 deletions gemfiles/sidekiq_7.3.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This file was generated by Appraisal

source "https://rubygems.org"

gem "appraisal"
gem "faraday-retry"
gem "gem-release"
gem "github-markup"
gem "rack-test"
gem "rake", "13.0.3"
gem "reek", ">= 5.3"
gem "rspec"
gem "rspec-benchmark"
gem "rspec-html-matchers"
gem "rspec-its"
gem "rubocop-mhenrixon"
gem "simplecov-sublime", ">= 0.21.2", require: false
gem "sinatra"
gem "timecop"
gem "toxiproxy"
gem "yard"
gem "sidekiq", "~> 7.3.0"

platforms :mri do
gem "concurrent-ruby-ext"
end

gemspec path: "../"
22 changes: 19 additions & 3 deletions lib/sidekiq_unique_jobs/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ module SidekiqUniqueJobs
:reaper_resurrector_enabled,
:lock_info,
:raise_on_config_error,
:current_redis_version)
:current_redis_version,
:digest_algorithm)

#
# Shared class for dealing with gem configuration
Expand Down Expand Up @@ -118,11 +119,9 @@ class Config < ThreadSafeConfig
#
# @return [3600] check if reaper is dead each 3600 seconds
REAPER_RESURRECTOR_INTERVAL = 3600

#
# @return [false] enable reaper resurrector
REAPER_RESURRECTOR_ENABLED = false

#
# @return [false] while useful it also adds overhead so disable lock_info by default
USE_LOCK_INFO = false
Expand All @@ -132,6 +131,9 @@ class Config < ThreadSafeConfig
#
# @return [0.0.0] default redis version is only to avoid NoMethodError on nil
REDIS_VERSION = "0.0.0"
#
# @return [:legacy] default digest algorithm :modern or :legacy
DIGEST_ALGORITHM = :legacy

#
# Returns a default configuration
Expand Down Expand Up @@ -198,6 +200,7 @@ def self.default # rubocop:disable Metrics/MethodLength
USE_LOCK_INFO,
RAISE_ON_CONFIG_ERROR,
REDIS_VERSION,
DIGEST_ALGORITHM,
)
end

Expand Down Expand Up @@ -304,6 +307,19 @@ def add_strategy(name, klass)
self.strategies = new_strategies
end

#
# Sets digest_algorithm to either :modern or :legacy
#
# @param [Symbol] value
#
# @return [Symbol] the new value
#
def digest_algorithm=(value)
raise ArgumentError, "Invalid digest algorithm: #{value} (should be :modern or :legacy)" unless %i[modern legacy].include?(value)

super(value)
end

#
# The current version of redis
#
Expand Down
7 changes: 6 additions & 1 deletion lib/sidekiq_unique_jobs/lock_digest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ def lock_digest
# Creates a namespaced unique digest based on the {#digestable_hash} and the {#lock_prefix}
# @return [String] a unique digest
def create_digest
digest = OpenSSL::Digest::MD5.hexdigest(dump_json(digestable_hash.sort))
if (SidekiqUniqueJobs.config.digest_algorithm == :legacy)
digest = OpenSSL::Digest::MD5.hexdigest(dump_json(digestable_hash.sort))
else
digest = OpenSSL::Digest.new("SHA3-256", dump_json(digestable_hash.sort)).hexdigest
end

"#{lock_prefix}:#{digest}"
end

Expand Down
40 changes: 31 additions & 9 deletions spec/sidekiq_unique_jobs/lock_digest_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
let(:digest_two) { described_class.new(another_item) }

context "with the same unique args" do
let(:another_item) { item }
let(:another_item) { item.dup }

it "equals to lock_digest for that item" do
expect(lock_digest).to eq(digest_two.lock_digest)
Expand All @@ -52,18 +52,40 @@
end
end

context "when digest is a proc" do
let(:job_class) { MyUniqueJobWithFilterProc }
let(:args) { [1, 2, { "type" => "it" }] }
context "when digest_algorithm is :legacy" do
context "when digest is a proc" do
let(:job_class) { MyUniqueJobWithFilterProc }
let(:args) { [1, 2, { "type" => "it" }] }

it_behaves_like "unique digest"
it_behaves_like "unique digest"
end

context "when unique_args is a symbol" do
let(:job_class) { MyUniqueJobWithFilterMethod }
let(:args) { [1, 2, { "type" => "it" }] }

it_behaves_like "unique digest"
end
end

context "when unique_args is a symbol" do
let(:job_class) { MyUniqueJobWithFilterMethod }
let(:args) { [1, 2, { "type" => "it" }] }
context "when digest_algorithm is :modern" do
around do |example|
SidekiqUniqueJobs.use_config(digest_algorithm: :modern, &example)
end

context "when digest is a proc" do
let(:job_class) { MyUniqueJobWithFilterProc }
let(:args) { [1, 2, { "type" => "it" }] }

it_behaves_like "unique digest"
end

it_behaves_like "unique digest"
context "when unique_args is a symbol" do
let(:job_class) { MyUniqueJobWithFilterMethod }
let(:args) { [1, 2, { "type" => "it" }] }

it_behaves_like "unique digest"
end
end
end

Expand Down

0 comments on commit 1506b61

Please sign in to comment.