activejob-uniqueness 0.1.3 → 0.2.2

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: 881d2ffd39454784d884cc4049639e2e4455d854deec87217c55a95c4f4a5869
4
- data.tar.gz: 469900d8be29af691e4efa9d883ba3d037dbbd06ac9ca7a6cf38b8cd0b2fb17d
3
+ metadata.gz: 3892ccb031a8fe8b0bc47154ed236dda2a700b404dbcc9063b83479fbe264dc7
4
+ data.tar.gz: 73a8f4866017e2310bc0c966a49d2f25578921ad64b2e23a2c28931923b34b04
5
5
  SHA512:
6
- metadata.gz: c9907d17c12ff54e319d75253c7190a97b8114f7492e728d98b19ea94a14219722365f35e98422a3b2cd474d6aa14cb8c45bd5b7facfc68febdb541e14110c3a
7
- data.tar.gz: a4f99ea028e1290f7742d67025b04069c0bec2912bdf4b455f0e422b91c411e8adb6e0ad2193c3b7a0067f2921cdb3b7fe7bf2ff3f251258e87b931956f96112
6
+ metadata.gz: a47bdbedbc98c95c24983ae4f46652f6c5f10584cfbe65312b4ee336a2d3fae21d03d6ea4c6b3fd09760cd5e9cfd3dde2fe02a89423e0918a6de0353a6f90c9b
7
+ data.tar.gz: ebfcf1e76b74662f5fb60008789b1858061c8685783a87317dff5818637064519894780f941c6a543687e02952747db2105e872efb95079e5b321a4f149e38bd
data/CHANGELOG.md CHANGED
@@ -3,7 +3,34 @@ 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.1.3...HEAD)
6
+ ## [Unreleased](https://github.com/veeqo/activejob-uniqueness/compare/v0.2.2...HEAD)
7
+
8
+ ## [0.2.2](https://github.com/veeqo/activejob-uniqueness/compare/v0.2.1...v0.2.2) - 2021-10-22
9
+
10
+ ### Added
11
+ - [#32](https://github.com/veeqo/activejob-uniqueness/pull/32) Add ability to set a custom runtime lock key for `:until_and_while_executing` strategy
12
+
13
+ ## [0.2.1](https://github.com/veeqo/activejob-uniqueness/compare/v0.2.0...v0.2.1) - 2021-08-24
14
+
15
+ ### Added
16
+ - [#30](https://github.com/veeqo/activejob-uniqueness/pull/30) Add Sidekiq::JobRecord support (reported by [@dwightwatson](https://github.com/dwightwatson))
17
+
18
+ ## [0.2.0](https://github.com/veeqo/activejob-uniqueness/compare/v0.1.4...v0.2.0) - 2021-05-09
19
+
20
+ ### Added
21
+ - [#22](https://github.com/veeqo/activejob-uniqueness/pull/22) Test with ruby 3.0.1
22
+
23
+ ### Changed
24
+ - [#20](https://github.com/veeqo/activejob-uniqueness/pull/20) **Breaking** Sidekiq patch is not applied automatically anymore
25
+ - [#21](https://github.com/veeqo/activejob-uniqueness/pull/21) Migrate from Travis to Github Actions
26
+ - [#24](https://github.com/veeqo/activejob-uniqueness/pull/24) The default value for `retry_count` of redlock is now 0
27
+ - Require ruby 2.5+
28
+
29
+ ## [0.1.4](https://github.com/veeqo/activejob-uniqueness/compare/v0.1.3...v0.1.4) - 2020-09-22
30
+
31
+ ### Fixed
32
+ - [#11](https://github.com/veeqo/activejob-uniqueness/pull/11) Fix deprecation warnings for ruby 2.7 by [@DanAndreasson](https://github.com/DanAndreasson)
33
+ - [#13](https://github.com/veeqo/activejob-uniqueness/pull/13) Fix deprecation warnings for ruby 2.7
7
34
 
8
35
  ## [0.1.3](https://github.com/veeqo/activejob-uniqueness/compare/v0.1.2...v0.1.3) - 2020-08-17
9
36
 
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Job uniqueness for ActiveJob
2
- [![Build Status](https://travis-ci.com/veeqo/activejob-uniqueness.svg?branch=master)](https://travis-ci.com/veeqo/activejob-uniqueness) [![Gem Version](https://badge.fury.io/rb/activejob-uniqueness.svg)](https://badge.fury.io/rb/activejob-uniqueness)
2
+ [![Build Status](https://github.com/veeqo/activejob-uniqueness/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/veeqo/activejob-uniqueness/actions/workflows/main.yml) [![Gem Version](https://badge.fury.io/rb/activejob-uniqueness.svg)](https://badge.fury.io/rb/activejob-uniqueness)
3
3
 
4
4
  The gem allows to protect job uniqueness with next strategies:
5
5
 
@@ -8,10 +8,16 @@ The gem allows to protect job uniqueness with next strategies:
8
8
  | `until_executing` | when **pushed** to the queue | when **processing starts** |
9
9
  | `until_executed` | when **pushed** to the queue | when the job is **processed successfully** |
10
10
  | `until_expired` | when **pushed** to the queue | when the lock is **expired** |
11
- | `until_and_while_executing` | when **pushed** to the queue | when **processing starts**<br>a runtime lock is acquired to **prevent simultaneous jobs** |
11
+ | `until_and_while_executing` | when **pushed** to the queue | when **processing starts**<br>a runtime lock is acquired to **prevent simultaneous jobs**<br>*has extra options: `runtime_lock_ttl`, `on_runtime_conflict`* |
12
12
  | `while_executing` | when **processing starts** | when the job is **processed**<br>with any result including an error |
13
13
 
14
- Inspired by [SidekiqUniqueJobs](https://github.com/mhenrixon/sidekiq-unique-jobs), uses [Redlock](https://github.com/leandromoreira/redlock-rb) under the hood, sponsored by [Veeqo](https://www.veeqo.com/).
14
+ Inspired by [SidekiqUniqueJobs](https://github.com/mhenrixon/sidekiq-unique-jobs), uses [Redlock](https://github.com/leandromoreira/redlock-rb) under the hood.
15
+
16
+ <p align="center">
17
+ <a href="https://www.veeqo.com/" title="Sponsored by Veeqo">
18
+ <img src="https://static.veeqo.com/assets/sponsored_by_veeqo.png" width="360" />
19
+ </a>
20
+ </p>
15
21
 
16
22
  ## Installation
17
23
 
@@ -21,6 +27,11 @@ Add the `activejob-uniqueness` gem to your Gemfile.
21
27
  gem 'activejob-uniqueness'
22
28
  ```
23
29
 
30
+ If you want jobs unlocking for Sidekiq Web UI, require the patch explicitly. [**Queues cleanup becomes slower!**](#sidekiq-api-support)
31
+ ```ruby
32
+ gem 'activejob-uniqueness', require: 'active_job/uniqueness/sidekiq_patch'
33
+ ```
34
+
24
35
  And run `bundle install` command.
25
36
 
26
37
  ## Configuration
@@ -34,27 +45,87 @@ rails generate active_job:uniqueness:install
34
45
 
35
46
  ## Usage
36
47
 
37
- Define uniqueness strategy for your job via `unique` class method:
48
+
49
+ ### Make the job to be unique
50
+
51
+ ```ruby
52
+ class MyJob < ActiveJob::Base
53
+ # new jobs with the same args will raise error until existing one is executed
54
+ unique :until_executed
55
+
56
+ def perform(args)
57
+ # work
58
+ end
59
+ end
60
+ ```
61
+
62
+ ### Tune uniqueness settings per job
63
+
64
+ ```ruby
65
+ class MyJob < ActiveJob::Base
66
+ # new jobs with the same args will be logged within 3 hours or until existing one is being executing
67
+ unique :until_executing, lock_ttl: 3.hours, on_conflict: :log
68
+
69
+ def perform(args)
70
+ # work
71
+ end
72
+ end
73
+ ```
74
+
75
+ You can set defaults globally with [the configuration](#configuration)
76
+
77
+ ### Control lock conflicts
78
+
79
+ ```ruby
80
+ class MyJob < ActiveJob::Base
81
+ # Proc gets the job instance including its arguments
82
+ unique :until_executing, on_conflict: ->(job) { job.logger.info "Oops: #{job.arguments}" }
83
+
84
+ def perform(args)
85
+ # work
86
+ end
87
+ end
88
+ ```
89
+
90
+ ### Control lock key arguments
38
91
 
39
92
  ```ruby
40
93
  class MyJob < ActiveJob::Base
41
94
  unique :until_executed
42
95
 
43
- # Custom expiration:
44
- # unique :until_executed, lock_ttl: 3.hours
96
+ def perform(foo, bar, baz)
97
+ # work
98
+ end
99
+
100
+ def lock_key_arguments
101
+ arguments.first(2) # baz is ignored
102
+ end
103
+ end
104
+ ```
105
+
106
+ ### Control the lock key
107
+
108
+ ```ruby
109
+ class MyJob < ActiveJob::Base
110
+ unique :until_executed
45
111
 
46
- # Do not raise error on non unique jobs enqueuing:
47
- # unique :until_executed, on_conflict: :log
112
+ def perform(foo, bar, baz)
113
+ # work
114
+ end
48
115
 
49
- # Handle conflict by custom Proc:
50
- # unique :until_executed, on_conflict: ->(job) { job.logger.info 'Oops' }
116
+ def lock_key
117
+ 'qux' # completely custom lock key
118
+ end
51
119
 
52
- # The :until_and_while_executing strategy supports extra attributes for a runtime lock:
53
- # unique :until_and_while_executing runtime_lock_ttl: 10.minutes, on_runtime_conflict: :log
120
+ def runtime_lock_key
121
+ 'quux' # completely custom runtime lock key for :until_and_while_executing
122
+ end
54
123
  end
55
124
  ```
56
125
 
57
- ActiveJob::Uniqueness allows to manually unlock jobs:
126
+ ### Unlock jobs manually
127
+
128
+ The selected strategy automatically unlocks jobs, but in some cases (e.g. the queue is purged) it is handy to unlock jobs manually.
58
129
 
59
130
  ```ruby
60
131
  # Remove the lock for particular arguments:
@@ -91,7 +162,7 @@ ActiveJob::Uniqueness instruments `ActiveSupport::Notifications` with next event
91
162
 
92
163
  And then writes to `ActiveJob::Base.logger`.
93
164
 
94
- ### ActiveJob prior to version `6.1` will always log `Enqueued MyJob (Job ID) ...` even if the callback chain was halted. [Details](https://github.com/rails/rails/pull/37830)
165
+ **ActiveJob prior to version `6.1` will always log `Enqueued MyJob (Job ID) ...` even if the callback chain is halted. [Details](https://github.com/rails/rails/pull/37830)**
95
166
 
96
167
  ## Testing
97
168
 
@@ -107,15 +178,14 @@ bundle
107
178
  rake
108
179
  ```
109
180
 
110
- Use [wwtd](https://github.com/grosser/wwtd) to run test matrix:
111
- ```sh
112
- gem install wwtd
113
- wwtd
114
- ```
181
+ ## Sidekiq API support
115
182
 
116
- ## Sidekiq adapter support
183
+ ActiveJob::Uniqueness supports Sidekiq API to unset job locks on queues cleanup (e.g. via Sidekiq Web UI). Starting Sidekiq 5.1 job death also triggers locks cleanup.
184
+ Take into account that **[big queues clanup becomes much slower](https://github.com/veeqo/activejob-uniqueness/issues/16)** because each job is being unlocked individually. In order to activate Sidekiq API patch require it explicitly in your Gemfile:
117
185
 
118
- ActiveJob::Uniqueness patches Sidekiq API to unset locks on jobs cleanup. Starting Sidekiq 5.1 job death also triggers locks cleanup.
186
+ ```ruby
187
+ gem 'activejob-uniqueness', require: 'active_job/uniqueness/sidekiq_patch'
188
+ ```
119
189
 
120
190
  ## Contributing
121
191
 
@@ -124,3 +194,9 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/veeqo/
124
194
  ## License
125
195
 
126
196
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
197
+
198
+ ## About [Veeqo](https://www.veeqo.com)
199
+
200
+ At Veeqo, our team of Engineers is on a mission to create a world-class Inventory and Shipping platform, built to the highest standards in best coding practices. We are a growing team, looking for other passionate developers to [join us](https://veeqo-ltd.breezy.hr/) on our journey. If you're looking for a career working for one of the most exciting tech companies in ecommerce, we want to hear from you.
201
+
202
+ [Veeqo developers blog](https://devs.veeqo.com)
@@ -55,7 +55,7 @@ module ActiveJob
55
55
  end
56
56
 
57
57
  def lock_strategy
58
- @lock_strategy ||= lock_strategy_class.new(**lock_options.merge(lock_key: lock_key, job: self))
58
+ @lock_strategy ||= lock_strategy_class.new(job: self)
59
59
  end
60
60
 
61
61
  # Override in your job class if you want to customize arguments set for a digest.
@@ -64,11 +64,11 @@ module ActiveJob
64
64
  end
65
65
 
66
66
  # Override lock_key method in your job class if you want to build completely custom lock key.
67
- delegate :lock_key, to: :lock_key_generator
67
+ delegate :lock_key, :runtime_lock_key, to: :lock_key_generator
68
68
 
69
69
  def lock_key_generator
70
- ActiveJob::Uniqueness::LockKey.new job_class_name: self.class.name,
71
- arguments: lock_key_arguments
70
+ @lock_key_generator ||= ActiveJob::Uniqueness::LockKey.new job_class_name: self.class.name,
71
+ arguments: lock_key_arguments
72
72
  end
73
73
  end
74
74
 
@@ -11,14 +11,18 @@ module ActiveJob
11
11
  class Configuration
12
12
  include ActiveSupport::Configurable
13
13
 
14
- config_accessor(:lock_ttl) { 1.day }
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(:digest_method) { OpenSSL::Digest::MD5 }
18
17
  config_accessor(:redlock_servers) { [ENV.fetch('REDIS_URL', 'redis://localhost:6379')] }
19
- config_accessor(:redlock_options) { {} }
18
+ config_accessor(:redlock_options) { { retry_count: 0 } }
20
19
  config_accessor(:lock_strategies) { {} }
21
20
 
21
+ config_accessor(:digest_method) do
22
+ require 'openssl'
23
+ OpenSSL::Digest::MD5
24
+ end
25
+
22
26
  def on_conflict=(action)
23
27
  validate_on_conflict_action!(action)
24
28
 
@@ -28,6 +28,14 @@ module ActiveJob
28
28
  ].join(':')
29
29
  end
30
30
 
31
+ # used only by :until_and_while_executing strategy
32
+ def runtime_lock_key
33
+ [
34
+ lock_key,
35
+ 'runtime'
36
+ ].join(':')
37
+ end
38
+
31
39
  def wildcard_key
32
40
  [
33
41
  lock_prefix,
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'activejob/uniqueness'
4
+ require 'sidekiq/api'
5
+
3
6
  module ActiveJob
4
7
  module Uniqueness
5
8
  def self.unlock_sidekiq_job!(job_data)
@@ -73,12 +76,21 @@ end
73
76
 
74
77
  Sidekiq::SortedEntry.prepend ActiveJob::Uniqueness::SidekiqPatch::SortedEntry
75
78
  Sidekiq::ScheduledSet.prepend ActiveJob::Uniqueness::SidekiqPatch::ScheduledSet
76
- Sidekiq::Job.prepend ActiveJob::Uniqueness::SidekiqPatch::Job
77
79
  Sidekiq::Queue.prepend ActiveJob::Uniqueness::SidekiqPatch::Queue
78
80
  Sidekiq::JobSet.prepend ActiveJob::Uniqueness::SidekiqPatch::JobSet
79
81
 
82
+ sidekiq_version = Gem::Version.new(Sidekiq::VERSION)
83
+
84
+ # Sidekiq 6.2.2 renames Sidekiq::Job to Sidekiq::JobRecord
85
+ # https://github.com/mperham/sidekiq/issues/4955
86
+ if sidekiq_version >= Gem::Version.new('6.2.2')
87
+ Sidekiq::JobRecord.prepend ActiveJob::Uniqueness::SidekiqPatch::Job
88
+ else
89
+ Sidekiq::Job.prepend ActiveJob::Uniqueness::SidekiqPatch::Job
90
+ end
91
+
80
92
  # Global death handlers are introduced in Sidekiq 5.1
81
93
  # https://github.com/mperham/sidekiq/blob/e7acb124fbeb0bece0a7c3d657c39a9cc18d72c6/Changes.md#510
82
- if Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new('5.1')
94
+ if sidekiq_version >= Gem::Version.new('5.1')
83
95
  Sidekiq.death_handlers << ->(job, _ex) { ActiveJob::Uniqueness.unlock_sidekiq_job!(job) }
84
96
  end
@@ -13,10 +13,10 @@ module ActiveJob
13
13
 
14
14
  attr_reader :lock_key, :lock_ttl, :on_conflict, :job
15
15
 
16
- def initialize(lock_key:, lock_ttl: nil, on_conflict: nil, job: nil)
17
- @lock_key = lock_key
18
- @lock_ttl = (lock_ttl || config.lock_ttl).to_i * 1000 # ms
19
- @on_conflict = on_conflict || config.on_conflict
16
+ def initialize(job:)
17
+ @lock_key = job.lock_key
18
+ @lock_ttl = (job.lock_options[:lock_ttl] || config.lock_ttl).to_i * 1000 # ms
19
+ @on_conflict = job.lock_options[:on_conflict] || config.on_conflict
20
20
  @job = job
21
21
  end
22
22
 
@@ -9,12 +9,16 @@ module ActiveJob
9
9
  class UntilAndWhileExecuting < Base
10
10
  include LockingOnEnqueue
11
11
 
12
- attr_reader :runtime_lock_ttl, :on_runtime_conflict
12
+ attr_reader :runtime_lock_key, :runtime_lock_ttl, :on_runtime_conflict
13
13
 
14
- def initialize(runtime_lock_ttl: nil, on_runtime_conflict: nil, **params)
15
- super(params)
16
- @runtime_lock_ttl = runtime_lock_ttl.present? ? runtime_lock_ttl.to_i * 1000 : lock_ttl
17
- @on_runtime_conflict = on_runtime_conflict || on_conflict
14
+ def initialize(job:)
15
+ super
16
+ @runtime_lock_key = job.runtime_lock_key
17
+
18
+ runtime_lock_ttl_option = job.lock_options[:runtime_lock_ttl]
19
+ @runtime_lock_ttl = runtime_lock_ttl_option.present? ? runtime_lock_ttl_option.to_i * 1000 : lock_ttl
20
+
21
+ @on_runtime_conflict = job.lock_options[:on_runtime_conflict] || on_conflict
18
22
  end
19
23
 
20
24
  def before_perform
@@ -33,10 +37,6 @@ module ActiveJob
33
37
  ensure
34
38
  unlock(resource: runtime_lock_key, event: :runtime_unlock) unless @job_aborted
35
39
  end
36
-
37
- def runtime_lock_key
38
- [lock_key, 'runtime'].join(':')
39
- end
40
40
  end
41
41
  end
42
42
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveJob
4
4
  module Uniqueness
5
- VERSION = '0.1.3'
5
+ VERSION = '0.2.2'
6
6
  end
7
7
  end
@@ -1,22 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support'
4
- require 'active_support/core_ext'
3
+ require 'active_job'
5
4
  require 'redlock'
6
- require 'openssl'
5
+
7
6
  require 'active_job/uniqueness/version'
8
7
  require 'active_job/uniqueness/errors'
9
8
  require 'active_job/uniqueness/log_subscriber'
10
9
  require 'active_job/uniqueness/active_job_patch'
11
10
 
12
- begin
13
- require 'sidekiq/api'
14
- rescue LoadError
15
- # ¯\_(ツ)_/¯
16
- else
17
- require 'active_job/uniqueness/sidekiq_patch'
18
- end
19
-
20
11
  module ActiveJob
21
12
  module Uniqueness
22
13
  extend ActiveSupport::Autoload
@@ -40,8 +31,8 @@ module ActiveJob
40
31
  @lock_manager ||= ActiveJob::Uniqueness::LockManager.new(config.redlock_servers, config.redlock_options)
41
32
  end
42
33
 
43
- def unlock!(*args)
44
- lock_manager.delete_locks(ActiveJob::Uniqueness::LockKey.new(*args).wildcard_key)
34
+ def unlock!(**args)
35
+ lock_manager.delete_locks(ActiveJob::Uniqueness::LockKey.new(**args).wildcard_key)
45
36
  end
46
37
 
47
38
  def test_mode!
@@ -31,7 +31,7 @@ ActiveJob::Uniqueness.configure do |config|
31
31
  # Custom options for Redlock.
32
32
  # Read more at https://github.com/leandromoreira/redlock-rb#redlock-configuration
33
33
  #
34
- # config.redlock_options = {}
34
+ # config.redlock_options = { retry_count: 0 }
35
35
 
36
36
  # Custom strategies.
37
37
  # config.lock_strategies = { my_strategy: MyStrategy }
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.1.3
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rustam Sharshenov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-17 00:00:00.000000000 Z
11
+ date: 2021-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -142,7 +142,7 @@ licenses:
142
142
  metadata:
143
143
  homepage_uri: https://github.com/veeqo/activejob-uniqueness
144
144
  source_code_uri: https://github.com/veeqo/activejob-uniqueness
145
- changelog_uri: https://github.com/veeqo/activejob-uniqueness/blob/master/CHANGELOG.md
145
+ changelog_uri: https://github.com/veeqo/activejob-uniqueness/blob/main/CHANGELOG.md
146
146
  post_install_message:
147
147
  rdoc_options: []
148
148
  require_paths:
@@ -151,14 +151,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
151
151
  requirements:
152
152
  - - ">="
153
153
  - !ruby/object:Gem::Version
154
- version: '0'
154
+ version: '2.5'
155
155
  required_rubygems_version: !ruby/object:Gem::Requirement
156
156
  requirements:
157
157
  - - ">="
158
158
  - !ruby/object:Gem::Version
159
159
  version: '0'
160
160
  requirements: []
161
- rubygems_version: 3.0.6
161
+ rubygems_version: 3.0.9
162
162
  signing_key:
163
163
  specification_version: 4
164
164
  summary: Ensure uniqueness of your ActiveJob jobs