activejob-uniqueness 0.3.2 → 0.4.0

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: cb511653e8e225bda4efe0408f12b083994172a42e47de60fae20e05497fc40f
4
- data.tar.gz: 59d9980246fb8c42e17f8fcc723b48c035d3c9bdffe3c5e899f425e277a231c4
3
+ metadata.gz: cd76f286ed7e6482860247e9b98b754dc1b73a381fcd2579d73e469da95d0200
4
+ data.tar.gz: 866f0949c68745c168217cbc70b10c65799f70aa372902dbd145781726217acd
5
5
  SHA512:
6
- metadata.gz: 68b535216f33f227330202077d672ed768c95dbe2044488501d8a95925c26d204f1395954170afd9420b2da351fa79cf112d0d2f114dbccd1a21607fdfa03403
7
- data.tar.gz: a53f4053972e88b33e515f643b65dfd1d6dd3b389b8a2849094cc23b4a5d766c32a30cfbe69bc8ad5b323917dc904934ccdf5cffa33a8330a899825a3428708f
6
+ metadata.gz: 5cb9299a82cc40a9fca3df73c771cf553ead9393b11eca505afea5e55fe3023722db67d509528d0886db66e64569ba1caea04622ebde3ca365df1d768f04d2f4
7
+ data.tar.gz: 205d78f3c41721bce16c16d991204423fe5429b23c1d6fad48eb1a4ac7f775057f7e15192e8d1c32c97fc0c2c4af60a1584d6a666c253df71e1acce9ba02e5fa
data/CHANGELOG.md CHANGED
@@ -3,15 +3,26 @@ All notable changes to this project will be documented in this file.
3
3
 
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
5
 
6
- ## [Unreleased](https://github.com/veeqo/activejob-uniqueness/compare/v0.3.2...HEAD)
6
+ ## [Unreleased](https://github.com/veeqo/activejob-uniqueness/compare/v0.4.0...HEAD)
7
+
8
+
9
+ ## [0.4.0](https://github.com/veeqo/activejob-uniqueness/compare/v0.3.2...v0.4.0) - 2024-12-07
10
+
11
+ ### Added
12
+
13
+ - [#86](https://github.com/veeqo/activejob-uniqueness/pull/86) Add Rails 8.0 rc1 support by[@sharshenov](https://github.com/sharshenov)
14
+ - [#78](https://github.com/veeqo/activejob-uniqueness/pull/78) Add on_redis_connection_error config to adjust to new redlock behaviour by[@nduitz](https://github.com/nduitz)
15
+
16
+ ### Changed
17
+ - [#82](https://github.com/veeqo/activejob-uniqueness/pull/82) Optimize bulk unlocking [@sharshenov](https://github.com/sharshenov)
7
18
 
8
19
  ## [0.3.2](https://github.com/veeqo/activejob-uniqueness/compare/v0.3.1...v0.3.2) - 2024-08-16
9
20
 
10
21
  ### Added
11
- - [#80](https://github.com/veeqo/activejob-uniqueness/pull/80) Add rails 7.2 support by [viralpraxis]
22
+ - [#80](https://github.com/veeqo/activejob-uniqueness/pull/80) Add rails 7.2 support by [@viralpraxis](https://github.com/viralpraxis)
12
23
 
13
24
  ### Changed
14
- - [#74](https://github.com/veeqo/activejob-uniqueness/pull/74) Fix log subscriber by [@shahidkhaliq]
25
+ - [#74](https://github.com/veeqo/activejob-uniqueness/pull/74) Fix log subscriber by [@shahidkhaliq](https://github.com/shahidkhaliq)
15
26
 
16
27
  ## [0.3.1](https://github.com/veeqo/activejob-uniqueness/compare/v0.3.0...v0.3.1) - 2023-10-30
17
28
 
data/README.md CHANGED
@@ -43,6 +43,19 @@ To override the defaults, create an initializer `config/initializers/active_job_
43
43
  rails generate active_job:uniqueness:install
44
44
  ```
45
45
 
46
+ This gem relies on `redlock` for it's Redis connection, that means **it will not inherit global configuration of `Sidekiq`**. To configure the connection, you can use `config.redlock_servers`, for example to disable SSL verification for Redis/Key-Value cloud providers:
47
+
48
+ ```ruby
49
+ ActiveJob::Uniqueness.configure do |config|
50
+ config.redlock_servers = [
51
+ RedisClient.new(
52
+ url: ENV['REDIS_URL'],
53
+ ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
54
+ )
55
+ ]
56
+ end
57
+ ```
58
+
46
59
  ## Usage
47
60
 
48
61
 
@@ -87,6 +100,19 @@ class MyJob < ActiveJob::Base
87
100
  end
88
101
  ```
89
102
 
103
+ ### Control redis connection errors
104
+
105
+ ```ruby
106
+ class MyJob < ActiveJob::Base
107
+ # Proc gets the job instance including its arguments, and as keyword arguments the resource(lock key) `resource` and the original error `error`
108
+ unique :until_executing, on_redis_connection_error: ->(job, resource: _, error: _) { job.logger.info "Oops: #{job.arguments}" }
109
+
110
+ def perform(args)
111
+ # work
112
+ end
113
+ end
114
+ ```
115
+
90
116
  ### Control lock key arguments
91
117
 
92
118
  ```ruby
@@ -27,6 +27,7 @@ module ActiveJob
27
27
  def unique(strategy, options = {})
28
28
  validate_on_conflict_action!(options[:on_conflict])
29
29
  validate_on_conflict_action!(options[:on_runtime_conflict])
30
+ validate_on_redis_connection_error!(options[:on_redis_connection_error])
30
31
 
31
32
  self.lock_strategy_class = ActiveJob::Uniqueness::Strategies.lookup(strategy)
32
33
  self.lock_options = options
@@ -40,7 +41,9 @@ module ActiveJob
40
41
 
41
42
  private
42
43
 
43
- delegate :validate_on_conflict_action!, to: :'ActiveJob::Uniqueness.config'
44
+ delegate :validate_on_conflict_action!,
45
+ :validate_on_redis_connection_error!,
46
+ to: :'ActiveJob::Uniqueness.config'
44
47
  end
45
48
 
46
49
  included do
@@ -14,6 +14,7 @@ module ActiveJob
14
14
  config_accessor(:lock_ttl) { 86_400 } # 1.day
15
15
  config_accessor(:lock_prefix) { 'activejob_uniqueness' }
16
16
  config_accessor(:on_conflict) { :raise }
17
+ config_accessor(:on_redis_connection_error) { :raise }
17
18
  config_accessor(:redlock_servers) { [ENV.fetch('REDIS_URL', 'redis://localhost:6379')] }
18
19
  config_accessor(:redlock_options) { { retry_count: 0 } }
19
20
  config_accessor(:lock_strategies) { {} }
@@ -34,6 +35,18 @@ module ActiveJob
34
35
 
35
36
  raise ActiveJob::Uniqueness::InvalidOnConflictAction, "Unexpected '#{action}' action on conflict"
36
37
  end
38
+
39
+ def on_redis_connection_error=(action)
40
+ validate_on_redis_connection_error!(action)
41
+
42
+ config.on_redis_connection_error = action
43
+ end
44
+
45
+ def validate_on_redis_connection_error!(action)
46
+ return if action.nil? || action == :raise || action.respond_to?(:call)
47
+
48
+ raise ActiveJob::Uniqueness::InvalidOnConflictAction, "Unexpected '#{action}' action on_redis_connection_error"
49
+ end
37
50
  end
38
51
  end
39
52
  end
@@ -17,11 +17,17 @@ module ActiveJob
17
17
  true
18
18
  end
19
19
 
20
+ DELETE_LOCKS_SCAN_COUNT = 1000
21
+
20
22
  # Unlocks multiple resources by key wildcard.
21
23
  def delete_locks(wildcard)
22
24
  @servers.each do |server|
23
25
  synced_redis_connection(server) do |conn|
24
- conn.scan('MATCH', wildcard).each { |key| conn.call('DEL', key) }
26
+ cursor = 0
27
+ while cursor != '0'
28
+ cursor, keys = conn.call('SCAN', cursor, 'MATCH', wildcard, 'COUNT', DELETE_LOCKS_SCAN_COUNT)
29
+ conn.call('UNLINK', *keys) unless keys.empty?
30
+ end
25
31
  end
26
32
  end
27
33
 
@@ -11,12 +11,13 @@ module ActiveJob
11
11
 
12
12
  delegate :lock_manager, :config, to: :'ActiveJob::Uniqueness'
13
13
 
14
- attr_reader :lock_key, :lock_ttl, :on_conflict, :job
14
+ attr_reader :lock_key, :lock_ttl, :on_conflict, :on_redis_connection_error, :job
15
15
 
16
16
  def initialize(job:)
17
17
  @lock_key = job.lock_key
18
18
  @lock_ttl = (job.lock_options[:lock_ttl] || config.lock_ttl).to_i * 1000 # ms
19
19
  @on_conflict = job.lock_options[:on_conflict] || config.on_conflict
20
+ @on_redis_connection_error = job.lock_options[:on_redis_connection_error] || config.on_redis_connection_error
20
21
  @job = job
21
22
  end
22
23
 
@@ -60,6 +61,12 @@ module ActiveJob
60
61
 
61
62
  handle_conflict(resource: lock_key, on_conflict: on_conflict)
62
63
  abort_job
64
+ rescue RedisClient::ConnectionError => e
65
+ handle_redis_connection_error(
66
+ resource: lock_key, on_redis_connection_error:
67
+ on_redis_connection_error, error: e
68
+ )
69
+ abort_job
63
70
  end
64
71
 
65
72
  def around_enqueue(block)
@@ -86,6 +93,14 @@ module ActiveJob
86
93
  end
87
94
  end
88
95
 
96
+ def handle_redis_connection_error(resource:, on_redis_connection_error:, error:)
97
+ case on_redis_connection_error
98
+ when :raise, nil then raise error
99
+ else
100
+ on_redis_connection_error.call(job, resource: resource, error: error)
101
+ end
102
+ end
103
+
89
104
  def abort_job
90
105
  @job_aborted = true # ActiveJob 4.2 workaround
91
106
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveJob
4
4
  module Uniqueness
5
- VERSION = '0.3.2'
5
+ VERSION = '0.4.0'
6
6
  end
7
7
  end
@@ -19,6 +19,13 @@ ActiveJob::Uniqueness.configure do |config|
19
19
  #
20
20
  # config.on_conflict = :raise
21
21
 
22
+ # Default action on redis connection error. Can be set per job.
23
+ # Allowed values are
24
+ # :raise - raises ActiveJob::Uniqueness::JobNotUnique
25
+ # proc - custom Proc. For example, ->(job, resource: _, error: _) { job.logger.info("Job already in queue: #{job.class.name} #{job.arguments.inspect} (#{job.job_id})") }
26
+ #
27
+ # config.on_redis_connection_error = :raise
28
+
22
29
  # Digest method for lock keys generating. Expected to have `hexdigest` class method.
23
30
  #
24
31
  # config.digest_method = OpenSSL::Digest::MD5
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activejob-uniqueness
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rustam Sharshenov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-16 00:00:00.000000000 Z
11
+ date: 2024-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '4.2'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '7.3'
22
+ version: '8.1'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: '4.2'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '7.3'
32
+ version: '8.1'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: redlock
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -187,7 +187,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
187
187
  - !ruby/object:Gem::Version
188
188
  version: '0'
189
189
  requirements: []
190
- rubygems_version: 3.3.27
190
+ rubygems_version: 3.2.33
191
191
  signing_key:
192
192
  specification_version: 4
193
193
  summary: Ensure uniqueness of your ActiveJob jobs