Skip to content

Commit

Permalink
Add ability to set a custom runtime lock key for `:until_and_while_ex…
Browse files Browse the repository at this point in the history
…ecuting` strategy
  • Loading branch information
sharshenov committed Oct 22, 2021
1 parent 7c83723 commit 9ce214f
Show file tree
Hide file tree
Showing 12 changed files with 347 additions and 17 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased](https://github.com/veeqo/activejob-uniqueness/compare/v0.2.1...HEAD)

### Added
- [#31](https://github.com/veeqo/activejob-uniqueness/pull/31) Add ability to set a custom runtime lock key for `:until_and_while_executing` strategy

## [0.2.1](https://github.com/veeqo/activejob-uniqueness/compare/v0.2.0...v0.2.1) - 2021-08-24

### Added
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ class MyJob < ActiveJob::Base
def lock_key
'qux' # completely custom lock key
end

def runtime_lock_key
'quux' # completely custom runtime lock key for :until_and_while_executing
end
end
```

Expand Down
8 changes: 4 additions & 4 deletions lib/active_job/uniqueness/active_job_patch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def unlock!(*arguments)
end

def lock_strategy
@lock_strategy ||= lock_strategy_class.new(**lock_options.merge(lock_key: lock_key, job: self))
@lock_strategy ||= lock_strategy_class.new(job: self)
end

# Override in your job class if you want to customize arguments set for a digest.
Expand All @@ -64,11 +64,11 @@ def lock_key_arguments
end

# Override lock_key method in your job class if you want to build completely custom lock key.
delegate :lock_key, to: :lock_key_generator
delegate :lock_key, :runtime_lock_key, to: :lock_key_generator

def lock_key_generator
ActiveJob::Uniqueness::LockKey.new job_class_name: self.class.name,
arguments: lock_key_arguments
@lock_key_generator ||= ActiveJob::Uniqueness::LockKey.new job_class_name: self.class.name,
arguments: lock_key_arguments
end
end

Expand Down
8 changes: 8 additions & 0 deletions lib/active_job/uniqueness/lock_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ def lock_key
].join(':')
end

# used only by :until_and_while_executing strategy
def runtime_lock_key
[
lock_key,
'runtime'
].join(':')
end

def wildcard_key
[
lock_prefix,
Expand Down
8 changes: 4 additions & 4 deletions lib/active_job/uniqueness/strategies/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ class Base

attr_reader :lock_key, :lock_ttl, :on_conflict, :job

def initialize(lock_key:, lock_ttl: nil, on_conflict: nil, job: nil)
@lock_key = lock_key
@lock_ttl = (lock_ttl || config.lock_ttl).to_i * 1000 # ms
@on_conflict = on_conflict || config.on_conflict
def initialize(job:)
@lock_key = job.lock_key
@lock_ttl = (job.lock_options[:lock_ttl] || config.lock_ttl).to_i * 1000 # ms
@on_conflict = job.lock_options[:on_conflict] || config.on_conflict
@job = job
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ module Strategies
class UntilAndWhileExecuting < Base
include LockingOnEnqueue

attr_reader :runtime_lock_ttl, :on_runtime_conflict
attr_reader :runtime_lock_key, :runtime_lock_ttl, :on_runtime_conflict

def initialize(runtime_lock_ttl: nil, on_runtime_conflict: nil, **params)
super(**params)
@runtime_lock_ttl = runtime_lock_ttl.present? ? runtime_lock_ttl.to_i * 1000 : lock_ttl
@on_runtime_conflict = on_runtime_conflict || on_conflict
def initialize(job:)
super
@runtime_lock_key = job.runtime_lock_key

runtime_lock_ttl_option = job.lock_options[:runtime_lock_ttl]
@runtime_lock_ttl = runtime_lock_ttl_option.present? ? runtime_lock_ttl_option.to_i * 1000 : lock_ttl

@on_runtime_conflict = job.lock_options[:on_runtime_conflict] || on_conflict
end

def before_perform
Expand All @@ -33,10 +37,6 @@ def around_perform(block)
ensure
unlock(resource: runtime_lock_key, event: :runtime_unlock) unless @job_aborted
end

def runtime_lock_key
[lock_key, 'runtime'].join(':')
end
end
end
end
Expand Down
43 changes: 43 additions & 0 deletions spec/active_job/uniqueness/lock_key/runtime_lock_key_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

describe ActiveJob::Uniqueness::LockKey, '#runtime_lock_key' do
subject { lock_key.runtime_lock_key }

let(:lock_key) { described_class.new(job_class_name: job_class_name, arguments: arguments) }
let(:job_class_name) { 'FooBarJob' }
let(:arguments) { ['baz'] }

context 'when default configuration is used' do
it { is_expected.to eq 'activejob_uniqueness:foo_bar_job:143654a5f0a059a178924baf9b815ea6:runtime' }
end

context 'when job class has namespace' do
let(:job_class_name) { 'Foo::BarJob' }

it { is_expected.to eq 'activejob_uniqueness:foo/bar_job:143654a5f0a059a178924baf9b815ea6:runtime' }
end

context 'when custom lock_prefix is set' do
before { allow(ActiveJob::Uniqueness.config).to receive(:lock_prefix).and_return('custom') }

it { is_expected.to eq 'custom:foo_bar_job:143654a5f0a059a178924baf9b815ea6:runtime' }
end

context 'when custom digest_method is set' do
before { allow(ActiveJob::Uniqueness.config).to receive(:digest_method).and_return(OpenSSL::Digest::SHA1) }

it { is_expected.to eq 'activejob_uniqueness:foo_bar_job:c8246148dacbed08f65913be488195317569f8dd:runtime' }
end

context 'when nil arguments given' do
let(:arguments) { nil }

it { is_expected.to eq 'activejob_uniqueness:foo_bar_job:no_arguments:runtime' }
end

context 'when [] arguments given' do
let(:arguments) { [] }

it { is_expected.to eq 'activejob_uniqueness:foo_bar_job:no_arguments:runtime' }
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,96 @@ def perform(number1, number2)
end
end
end

describe 'lock keys' do
let(:job) { job_class.new(2, 1) }

describe 'on enqueuing' do
before { job.lock_strategy.before_enqueue }

context 'when the job has no custom #lock_key defined' do
let(:job_class) do
stub_active_job_class do
unique :until_and_while_executing

def perform(number1, number2)
number1 / number2
end
end
end

it 'locks the job with the default lock key' do
expect(locks.size).to eq 1
expect(locks.first).to match(/\Aactivejob_uniqueness:my_job:[^:]+\z/)
end
end

context 'when the job has a custom #lock_key defined' do
let(:job_class) do
stub_active_job_class do
unique :until_and_while_executing

def perform(number1, number2)
number1 / number2
end

def lock_key
'activejob_uniqueness:whatever'
end
end
end

it 'locks the job with the custom runtime lock key' do
expect(locks.size).to eq 1
expect(locks.first).to eq 'activejob_uniqueness:whatever'
end
end
end

describe 'while executing' do
before { job.lock_strategy.before_perform }

context 'when the job has no custom #runtime_lock_key defined' do
let(:job_class) do
stub_active_job_class do
unique :until_and_while_executing

def perform(number1, number2)
number1 / number2
end
end
end

it 'locks the job with the default runtime lock key' do
job.lock_strategy.around_perform lambda {
expect(locks.size).to eq 1
expect(locks.first).to match(/\Aactivejob_uniqueness:my_job:[^:]+:runtime\z/)
}
end
end

context 'when the job has a custom #runtime_lock_key defined' do
let(:job_class) do
stub_active_job_class do
unique :until_and_while_executing

def perform(number1, number2)
number1 / number2
end

def runtime_lock_key
'activejob_uniqueness:whatever'
end
end
end

it 'locks the job with the custom runtime lock key' do
job.lock_strategy.around_perform lambda {
expect(locks.size).to eq 1
expect(locks.first).to eq 'activejob_uniqueness:whatever'
}
end
end
end
end
end
44 changes: 44 additions & 0 deletions spec/active_job/uniqueness/strategies/until_executed_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,48 @@ def perform(number1, number2)
end
end
end

describe 'lock key' do
let(:job) { job_class.new(2, 1) }

before { job.lock_strategy.before_enqueue }

context 'when the job has no custom #lock_key defined' do
let(:job_class) do
stub_active_job_class do
unique :until_executed

def perform(number1, number2)
number1 / number2
end
end
end

it 'locks the job with the default lock key' do
expect(locks.size).to eq 1
expect(locks.first).to match(/\Aactivejob_uniqueness:my_job:[^:]+\z/)
end
end

context 'when the job has a custom #lock_key defined' do
let(:job_class) do
stub_active_job_class do
unique :until_executed

def perform(number1, number2)
number1 / number2
end

def lock_key
'activejob_uniqueness:whatever'
end
end
end

it 'locks the job with the custom lock key' do
expect(locks.size).to eq 1
expect(locks.first).to eq 'activejob_uniqueness:whatever'
end
end
end
end
44 changes: 44 additions & 0 deletions spec/active_job/uniqueness/strategies/until_executing_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,48 @@ def perform(number1, number2)
end
end
end

describe 'lock key' do
let(:job) { job_class.new(2, 1) }

before { job.lock_strategy.before_enqueue }

context 'when the job has no custom #lock_key defined' do
let(:job_class) do
stub_active_job_class do
unique :until_executing

def perform(number1, number2)
number1 / number2
end
end
end

it 'locks the job with the default lock key' do
expect(locks.size).to eq 1
expect(locks.first).to match(/\Aactivejob_uniqueness:my_job:[^:]+\z/)
end
end

context 'when the job has a custom #lock_key defined' do
let(:job_class) do
stub_active_job_class do
unique :until_executing

def perform(number1, number2)
number1 / number2
end

def lock_key
'activejob_uniqueness:whatever'
end
end
end

it 'locks the job with the custom lock key' do
expect(locks.size).to eq 1
expect(locks.first).to eq 'activejob_uniqueness:whatever'
end
end
end
end
44 changes: 44 additions & 0 deletions spec/active_job/uniqueness/strategies/until_expired_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,48 @@ def perform(number1, number2)
end
end
end

describe 'lock key' do
let(:job) { job_class.new(2, 1) }

before { job.lock_strategy.before_enqueue }

context 'when the job has no custom #lock_key defined' do
let(:job_class) do
stub_active_job_class do
unique :until_expired

def perform(number1, number2)
number1 / number2
end
end
end

it 'locks the job with the default lock key' do
expect(locks.size).to eq 1
expect(locks.first).to match(/\Aactivejob_uniqueness:my_job:[^:]+\z/)
end
end

context 'when the job has a custom #lock_key defined' do
let(:job_class) do
stub_active_job_class do
unique :until_expired

def perform(number1, number2)
number1 / number2
end

def lock_key
'activejob_uniqueness:whatever'
end
end
end

it 'locks the job with the custom lock key' do
expect(locks.size).to eq 1
expect(locks.first).to eq 'activejob_uniqueness:whatever'
end
end
end
end
Loading

0 comments on commit 9ce214f

Please sign in to comment.