resque-unique_by_arity 1.0.12 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +17 -12
- data/.rspec_status +35 -0
- data/.rubocop.yml +2 -0
- data/.rubocop_todo.yml +72 -0
- data/.ruby-version +1 -0
- data/Gemfile +10 -5
- data/LICENSE +22 -0
- data/README.md +56 -24
- data/Rakefile +8 -4
- data/bin/console +3 -3
- data/lib/resque/plugins/unique_by_arity.rb +55 -0
- data/lib/resque/unique_by_arity.rb +43 -13
- data/lib/resque/unique_by_arity/configuration.rb +33 -16
- data/lib/resque/unique_by_arity/{cop_modulizer.rb → modulizer.rb} +49 -29
- data/lib/resque/unique_by_arity/validation.rb +0 -1
- data/lib/resque/unique_by_arity/version.rb +1 -1
- data/resque-unique_by_arity.gemspec +28 -22
- metadata +139 -22
- data/lib/resque/resque_solo/queue.rb +0 -9
- data/lib/resque/unique_by_arity/cop.rb +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4ffc61efab34c9af47299c3527bee8da39876ed1e0a2bfd1144c05c0698f8f1b
|
|
4
|
+
data.tar.gz: f5604f556244558ff6893cfc57f09b5315f708db073463dd2644928d1cb51d1d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3a71f1fc0529d0d1a42685a239b65e3d787d9650d4e96b217e3bdf8feed18b8cb5c45a7327e44b25ef83e0e11ca6a36a1f57ea5fb2d405726bdc694c18d54693
|
|
7
|
+
data.tar.gz: bdefff7d32f6f61df917344be934206e188c9c661db6692fe79f9ec0388b8a4b5dd57a5ac6f0e5fb1ca49930048412ec08ccdaf3138f4f45a4bfd9be8464ad82
|
data/.gitignore
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
.bundle
|
|
4
|
+
.config
|
|
5
|
+
.yardoc
|
|
6
|
+
Gemfile.lock
|
|
7
|
+
InstalledFiles
|
|
8
|
+
_yardoc
|
|
9
|
+
coverage
|
|
10
|
+
doc/
|
|
11
|
+
lib/bundler/man
|
|
12
|
+
pkg
|
|
13
|
+
rdoc
|
|
14
|
+
spec/reports
|
|
15
|
+
test/tmp
|
|
16
|
+
test/version_tmp
|
|
17
|
+
tmp
|
data/.rspec_status
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
example_id | status | run_time |
|
|
2
|
+
-------------------------------------------------- | ------ | --------------- |
|
|
3
|
+
./spec/resque/unique_by_arity_spec.rb[1:1] | passed | 0.00088 seconds |
|
|
4
|
+
./spec/resque/unique_by_arity_spec.rb[1:2:1:1] | passed | 0.00094 seconds |
|
|
5
|
+
./spec/resque/unique_by_arity_spec.rb[1:3:1] | passed | 0.00017 seconds |
|
|
6
|
+
./spec/resque/unique_by_arity_spec.rb[1:4:1:1] | passed | 0.00016 seconds |
|
|
7
|
+
./spec/resque/unique_by_arity_spec.rb[1:5:1:1] | passed | 0.00159 seconds |
|
|
8
|
+
./spec/resque/unique_by_arity_spec.rb[1:5:2:1] | passed | 0.00029 seconds |
|
|
9
|
+
./spec/resque/unique_by_arity_spec.rb[1:5:3:1] | passed | 0.0003 seconds |
|
|
10
|
+
./spec/resque/unique_by_arity_spec.rb[1:6:1] | passed | 0.00016 seconds |
|
|
11
|
+
./spec/resque/unique_by_arity_spec.rb[1:7:1] | passed | 0.00033 seconds |
|
|
12
|
+
./spec/resque/unique_by_arity_spec.rb[1:7:2:1] | passed | 0.00028 seconds |
|
|
13
|
+
./spec/resque/unique_by_arity_spec.rb[1:7:3:1] | passed | 0.00036 seconds |
|
|
14
|
+
./spec/resque/unique_by_arity_spec.rb[1:7:4:1] | passed | 0.00035 seconds |
|
|
15
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:1:1:1:1] | passed | 0.00811 seconds |
|
|
16
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:1:2:1:1] | passed | 0.0006 seconds |
|
|
17
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:1:3:1:1] | passed | 0.00104 seconds |
|
|
18
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:2:1:1:1] | passed | 0.00079 seconds |
|
|
19
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:2:2:1:1] | passed | 0.0003 seconds |
|
|
20
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:2:3:1:1] | passed | 0.00026 seconds |
|
|
21
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:3:1:1:1] | passed | 0.00042 seconds |
|
|
22
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:3:2:1:1] | passed | 0.00083 seconds |
|
|
23
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:3:3:1:1] | passed | 0.00028 seconds |
|
|
24
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:4:1:1:1] | passed | 0.00032 seconds |
|
|
25
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:4:2:1:1] | passed | 0.00076 seconds |
|
|
26
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:4:3:1:1] | passed | 0.00031 seconds |
|
|
27
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:5:1:1:1] | passed | 0.00032 seconds |
|
|
28
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:5:2:1:1] | passed | 0.001 seconds |
|
|
29
|
+
./spec/resque/unique_by_arity_spec.rb[1:8:5:3:1] | passed | 0.00078 seconds |
|
|
30
|
+
./spec/resque/unique_by_arity_spec.rb[1:9:1] | passed | 0.00452 seconds |
|
|
31
|
+
./spec/resque/unique_by_arity_spec.rb[1:10:1:1] | passed | 0.00026 seconds |
|
|
32
|
+
./spec/resque/unique_by_arity_spec.rb[1:11:1:1] | passed | 0.00052 seconds |
|
|
33
|
+
./spec/resque/unique_by_arity_spec.rb[1:11:2:1:1] | passed | 0.00083 seconds |
|
|
34
|
+
./spec/resque/unique_by_arity_spec.rb[1:11:2:2:1] | passed | 0.00264 seconds |
|
|
35
|
+
./spec/resque/unique_by_arity_spec.rb[1:11:2:3:1] | passed | 0.00328 seconds |
|
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# This configuration was generated by
|
|
2
|
+
# `rubocop --auto-gen-config`
|
|
3
|
+
# on 2018-11-07 04:06:55 -0800 using RuboCop version 0.60.0.
|
|
4
|
+
# The point is for the user to remove these configuration records
|
|
5
|
+
# one by one as the offenses are removed from the code base.
|
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
|
8
|
+
|
|
9
|
+
# Offense count: 1
|
|
10
|
+
# Configuration parameters: Include.
|
|
11
|
+
# Include: **/*.gemspec
|
|
12
|
+
Gemspec/RequiredRubyVersion:
|
|
13
|
+
Exclude:
|
|
14
|
+
- 'resque-unique_by_arity.gemspec'
|
|
15
|
+
|
|
16
|
+
# Offense count: 5
|
|
17
|
+
Metrics/AbcSize:
|
|
18
|
+
Max: 97
|
|
19
|
+
|
|
20
|
+
# Offense count: 8
|
|
21
|
+
# Configuration parameters: CountComments, ExcludedMethods.
|
|
22
|
+
# ExcludedMethods: refine
|
|
23
|
+
Metrics/BlockLength:
|
|
24
|
+
Max: 525
|
|
25
|
+
|
|
26
|
+
# Offense count: 4
|
|
27
|
+
Metrics/CyclomaticComplexity:
|
|
28
|
+
Max: 20
|
|
29
|
+
|
|
30
|
+
# Offense count: 5
|
|
31
|
+
# Configuration parameters: CountComments, ExcludedMethods.
|
|
32
|
+
Metrics/MethodLength:
|
|
33
|
+
Max: 72
|
|
34
|
+
|
|
35
|
+
# Offense count: 4
|
|
36
|
+
Metrics/PerceivedComplexity:
|
|
37
|
+
Max: 22
|
|
38
|
+
|
|
39
|
+
# Offense count: 4
|
|
40
|
+
Style/Documentation:
|
|
41
|
+
Exclude:
|
|
42
|
+
- 'spec/**/*'
|
|
43
|
+
- 'test/**/*'
|
|
44
|
+
- 'lib/resque/unique_by_arity.rb'
|
|
45
|
+
- 'lib/resque/unique_by_arity/configuration.rb'
|
|
46
|
+
- 'lib/resque/unique_by_arity/modulizer.rb'
|
|
47
|
+
- 'lib/resque/unique_by_arity/validation.rb'
|
|
48
|
+
|
|
49
|
+
# Offense count: 3
|
|
50
|
+
# Configuration parameters: MinBodyLength.
|
|
51
|
+
Style/GuardClause:
|
|
52
|
+
Exclude:
|
|
53
|
+
- 'lib/resque/unique_by_arity/configuration.rb'
|
|
54
|
+
- 'lib/resque/unique_by_arity/validation.rb'
|
|
55
|
+
|
|
56
|
+
# Offense count: 1
|
|
57
|
+
Style/IfInsideElse:
|
|
58
|
+
Exclude:
|
|
59
|
+
- 'lib/resque/unique_by_arity/configuration.rb'
|
|
60
|
+
|
|
61
|
+
# Offense count: 4
|
|
62
|
+
# Cop supports --auto-correct.
|
|
63
|
+
Style/IfUnlessModifier:
|
|
64
|
+
Exclude:
|
|
65
|
+
- 'lib/resque/unique_by_arity/configuration.rb'
|
|
66
|
+
- 'lib/resque/unique_by_arity/modulizer.rb'
|
|
67
|
+
|
|
68
|
+
# Offense count: 83
|
|
69
|
+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
|
70
|
+
# URISchemes: http, https
|
|
71
|
+
Metrics/LineLength:
|
|
72
|
+
Max: 385
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ruby-2.5.1
|
data/Gemfile
CHANGED
|
@@ -2,12 +2,17 @@ source 'https://rubygems.org'
|
|
|
2
2
|
|
|
3
3
|
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
|
4
4
|
|
|
5
|
+
gem 'resque-unique_in_queue', path: '/Users/pboling/Documents/code/intricately/resque-unique_in_queue'
|
|
6
|
+
gem 'resque-unique_at_runtime', path: '/Users/pboling/Documents/code/intricately/resque-unique_at_runtime'
|
|
7
|
+
|
|
5
8
|
group :test do
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
unless ENV['TRAVIS']
|
|
10
|
+
gem 'byebug', '~> 10', platform: :mri, require: false
|
|
11
|
+
gem 'pry', '~> 0', platform: :mri, require: false
|
|
12
|
+
gem 'pry-byebug', '~> 3', platform: :mri, require: false
|
|
13
|
+
end
|
|
14
|
+
gem 'rubocop', '~> 0.60.0'
|
|
15
|
+
gem 'rubocop-rspec', '~> 1.30.0'
|
|
11
16
|
gem 'simplecov', '~> 0', require: false
|
|
12
17
|
end
|
|
13
18
|
|
data/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2017 - 2018 Peter H. Boling
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
|
@@ -24,7 +24,11 @@ Why? `resque-lonely_job` and `resque_solo` can't be used together, because their
|
|
|
24
24
|
|
|
25
25
|
## Important Note
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
See `lib/resque/unique_by_arity/configuration.rb` for all config options. Only a smattering of what is available is documented in this README.
|
|
28
|
+
|
|
29
|
+
## Most Important Note
|
|
30
|
+
|
|
31
|
+
You must configure this gem *after* you define the perform class method in your job or an error will be raised thanks to `perform` not having been defined yet.
|
|
28
32
|
|
|
29
33
|
Example:
|
|
30
34
|
|
|
@@ -67,16 +71,13 @@ Some jobs have parameters that you do not want to consider for determination of
|
|
|
67
71
|
|
|
68
72
|
```ruby
|
|
69
73
|
class MyJob
|
|
70
|
-
def self.perform(
|
|
71
|
-
#
|
|
74
|
+
def self.perform(my, cat, is, the, best, opts = {})
|
|
75
|
+
# Only the first 3: [my, cat, is] will be considered for determination of uniqueness
|
|
72
76
|
end
|
|
73
77
|
include UniqueByArity::Cop.new(
|
|
74
78
|
arity_for_uniqueness: 3,
|
|
75
79
|
unique_at_runtime: true
|
|
76
80
|
)
|
|
77
|
-
def self.perform(my, cat, is, the, best, opts = {})
|
|
78
|
-
# Only the first 3: [my, cat, is] will be considered for determination of uniqueness
|
|
79
|
-
end
|
|
80
81
|
end
|
|
81
82
|
```
|
|
82
83
|
|
|
@@ -86,14 +87,6 @@ Want this gem to tell you when it is misconfigured? It can.
|
|
|
86
87
|
|
|
87
88
|
```ruby
|
|
88
89
|
class MyJob
|
|
89
|
-
def self.perform(arg1, arg2, arg3)
|
|
90
|
-
# do stuff
|
|
91
|
-
end
|
|
92
|
-
include UniqueByArity::Cop.new(
|
|
93
|
-
arity_for_uniqueness: 3,
|
|
94
|
-
arity_validation: :warning, # or :skip, :error, or an error class to be raised, e.g. RuntimeError
|
|
95
|
-
unique_at_runtime: true
|
|
96
|
-
)
|
|
97
90
|
def self.perform(my, cat, opts = {})
|
|
98
91
|
# Because the third argument is optional the arity valdiation will not approve.
|
|
99
92
|
# Arguments to be considered for uniqueness should be required arguments.
|
|
@@ -101,6 +94,11 @@ class MyJob
|
|
|
101
94
|
#
|
|
102
95
|
# MyJob.perform has the following required parameters: [:my, :cat], which is not enough to satisfy the configured arity_for_uniqueness of 3
|
|
103
96
|
end
|
|
97
|
+
include UniqueByArity::Cop.new(
|
|
98
|
+
arity_for_uniqueness: 3,
|
|
99
|
+
arity_validation: :warning, # or :skip, :error, or an error class to be raised, e.g. RuntimeError
|
|
100
|
+
unique_at_runtime: true
|
|
101
|
+
)
|
|
104
102
|
end
|
|
105
103
|
```
|
|
106
104
|
|
|
@@ -155,6 +153,17 @@ class MyJob
|
|
|
155
153
|
end
|
|
156
154
|
```
|
|
157
155
|
|
|
156
|
+
#### Oops, I have stale runtime uniqueness keys for MyJob stored in Redis...
|
|
157
|
+
|
|
158
|
+
Preventing jobs with matching signatures from running, and they never get
|
|
159
|
+
dequeued because there is no actual corresponding job to dequeue.
|
|
160
|
+
|
|
161
|
+
*How to deal?*
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
MyJob.purge_unique_at_runtime_redis_keys
|
|
165
|
+
```
|
|
166
|
+
|
|
158
167
|
### Unique At Queue Time
|
|
159
168
|
|
|
160
169
|
#### Unique In Job's Specific Queue
|
|
@@ -189,6 +198,29 @@ class MyJob
|
|
|
189
198
|
end
|
|
190
199
|
```
|
|
191
200
|
|
|
201
|
+
#### Oops, I have stale Queue Time uniqueness keys...
|
|
202
|
+
|
|
203
|
+
Preventing jobs with matching signatures from being queued, and they never get
|
|
204
|
+
dequeued because there is no actual corresponding job to dequeue.
|
|
205
|
+
|
|
206
|
+
*How to deal?*
|
|
207
|
+
|
|
208
|
+
Option: Rampage
|
|
209
|
+
|
|
210
|
+
```ruby
|
|
211
|
+
# Delete *all* queued jobs in the queue, and
|
|
212
|
+
# delete *all* unqueness keys for the queue.
|
|
213
|
+
Redis.remove_queue('queue_name')
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Option: Butterfly
|
|
217
|
+
|
|
218
|
+
```ruby
|
|
219
|
+
# Delete *no* queued jobs at all, and
|
|
220
|
+
# delete *all* unqueness keys for the queue (might then allow duplicates).
|
|
221
|
+
Resque::UniqueInQueue::Queue.cleanup('queue_name')
|
|
222
|
+
```
|
|
223
|
+
|
|
192
224
|
### All Together Now
|
|
193
225
|
|
|
194
226
|
#### Unique At Runtime (across all queues) AND Unique In Job's Specific Queue
|
|
@@ -231,7 +263,7 @@ end
|
|
|
231
263
|
|
|
232
264
|
### Debugging
|
|
233
265
|
|
|
234
|
-
Run your worker with `RESQUE_DEBUG=true` to see payloads printed before they are used to determine uniqueness.
|
|
266
|
+
Run your worker with `RESQUE_DEBUG=true` to see payloads printed before they are used to determine uniqueness, as well as a lot of other debugging output.
|
|
235
267
|
|
|
236
268
|
### Customize Unique Keys Per Job
|
|
237
269
|
|
|
@@ -249,28 +281,28 @@ class MyJob
|
|
|
249
281
|
# Core hashing algorithm for a job used for *all 3 types* of uniqueness
|
|
250
282
|
# @return [Array<String, arguments>], where the string is the unique digest, and arguments are the specific args that were used to calculate the digest
|
|
251
283
|
def self.redis_unique_hash(payload)
|
|
252
|
-
|
|
284
|
+
modulizer.rb
|
|
253
285
|
# for how the built-in version works
|
|
254
286
|
# uniqueness_args = payload["args"] # over simplified & ignoring arity
|
|
255
287
|
# args = { class: job, args: uniqueness_args }
|
|
256
288
|
# return [Digest::MD5.hexdigest(Resque.encode(args)), uniqueness_args]
|
|
257
289
|
end
|
|
258
290
|
|
|
259
|
-
|
|
260
|
-
def self.
|
|
291
|
+
unique_in_queue
|
|
292
|
+
def self.unique_at_runtime_redis_key_prefix
|
|
261
293
|
# "unique_job:#{self}" # <= default value
|
|
262
294
|
end
|
|
263
295
|
|
|
264
|
-
|
|
265
|
-
def self.
|
|
296
|
+
unique_in_queue
|
|
297
|
+
def self.unique_at_runtime_key_namespace(queue = nil)
|
|
266
298
|
# definition depends on which type of uniqueness is chosen, be careful if you customize
|
|
267
|
-
# "
|
|
268
|
-
# "
|
|
299
|
+
# "r-uiq:queue:#{queue}:job" # <= is for unique within queue at queue time
|
|
300
|
+
# "r-uiq:across_queues:job" # <= is for unique across all queues at queue time
|
|
269
301
|
end
|
|
270
302
|
|
|
271
|
-
def self.
|
|
303
|
+
def self.unique_in_queue_redis_key(queue, payload)
|
|
272
304
|
# unique_hash, _args_for_uniqueness = redis_unique_hash(payload)
|
|
273
|
-
# "#{
|
|
305
|
+
# "#{unique_at_runtime_key_namespace(queue)}:#{unique_at_runtime_redis_key_prefix}:#{unique_hash}"
|
|
274
306
|
end
|
|
275
307
|
|
|
276
308
|
def self.runtime_key_namespace
|
data/Rakefile
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
require 'bundler/gem_tasks'
|
|
5
|
+
require 'rspec/core/rake_task'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
RSpec::Core::RakeTask.new
|
|
8
|
+
|
|
9
|
+
task default: :spec
|
|
10
|
+
task test: :spec
|
data/bin/console
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
require 'resque/unique_by_arity'
|
|
5
5
|
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
|
@@ -10,5 +10,5 @@ require "resque/unique_by_arity"
|
|
|
10
10
|
# require "pry"
|
|
11
11
|
# Pry.start
|
|
12
12
|
|
|
13
|
-
require
|
|
13
|
+
require 'irb'
|
|
14
14
|
IRB.start(__FILE__)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module Resque
|
|
2
|
+
module Plugins
|
|
3
|
+
# If you want your job to support uniqueness either at enqueue-time or at
|
|
4
|
+
# runtime, or both, and you want that uniqueness based on a specific arity
|
|
5
|
+
# of arguments, simply include this module into your job class.
|
|
6
|
+
#
|
|
7
|
+
# NOTE: This module gets instantiated.
|
|
8
|
+
# It is a module - class hybrid.
|
|
9
|
+
# That's unconventional, and extremely powerful.
|
|
10
|
+
#
|
|
11
|
+
# class EnqueueAndRunAlone
|
|
12
|
+
# @queue = :enqueue_and_run_alone
|
|
13
|
+
#
|
|
14
|
+
# def self.perform(arg1, arg2)
|
|
15
|
+
# alone_stuff
|
|
16
|
+
# end
|
|
17
|
+
# include Resque::Plugins::UniqueByArity.new(
|
|
18
|
+
# arity_for_uniqueness: 1,
|
|
19
|
+
# arity_validation: :warning, # or nil, false, or :error
|
|
20
|
+
# unique_at_runtime: true,
|
|
21
|
+
# unique_in_queue: true
|
|
22
|
+
# )
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
class UniqueByArity < Module
|
|
26
|
+
def initialize(**config)
|
|
27
|
+
@configuration = Resque::UniqueByArity::Configuration.new(**config)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def included(base)
|
|
31
|
+
return unless @configuration
|
|
32
|
+
|
|
33
|
+
@configuration.base_klass_name = base.to_s
|
|
34
|
+
@configuration.validate
|
|
35
|
+
base.send(:extend, Resque::UniqueByArity)
|
|
36
|
+
base.uniqueness_config_reset(@configuration.dup)
|
|
37
|
+
|
|
38
|
+
# gem is unique_in_queue, which is a rewrite of resque-loner
|
|
39
|
+
# see: https://github.com/neighborland/resque_solo
|
|
40
|
+
# defines a redis_key method, which we have to override.
|
|
41
|
+
base.send(:include, Resque::Plugins::UniqueInQueue) if @configuration.unique_in_queue || @configuration.unique_across_queues
|
|
42
|
+
|
|
43
|
+
# gem is resque-unique_at_runtime, which is a rewrite of resque-lonely_job
|
|
44
|
+
# see: https://github.com/pboling/resque-unique_at_runtime
|
|
45
|
+
base.send(:extend, Resque::Plugins::UniqueAtRuntime) if @configuration.unique_at_runtime
|
|
46
|
+
|
|
47
|
+
uniqueness_cop_module = Resque::UniqueByArity::Modulizer.to_mod(@configuration)
|
|
48
|
+
# This will override methods from both plugins above, if configured for both
|
|
49
|
+
base.send(:extend, uniqueness_cop_module)
|
|
50
|
+
|
|
51
|
+
base.include Resque::UniqueByArity::Validation unless @configuration.skip_arity_validation?
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'resque/unique_by_arity/version'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
require
|
|
5
|
-
|
|
6
|
-
require "resque/resque_solo/queue"
|
|
3
|
+
# External Gems
|
|
4
|
+
require 'colorized_string'
|
|
5
|
+
require 'resque'
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
require
|
|
10
|
-
require
|
|
11
|
-
|
|
12
|
-
require
|
|
7
|
+
# External Resque Plugins
|
|
8
|
+
require 'resque-unique_in_queue'
|
|
9
|
+
require 'resque-unique_at_runtime'
|
|
10
|
+
|
|
11
|
+
require 'resque/plugins/unique_by_arity'
|
|
12
|
+
require 'resque/unique_by_arity/configuration'
|
|
13
|
+
require 'resque/unique_by_arity/modulizer'
|
|
14
|
+
require 'resque/unique_by_arity/validation'
|
|
13
15
|
|
|
14
16
|
# Usage:
|
|
15
17
|
#
|
|
16
18
|
# class MyJob
|
|
17
|
-
#
|
|
19
|
+
# def self.perform(arg1, arg2)
|
|
20
|
+
# end
|
|
21
|
+
# include Resque::Plugins::UniqueByArity.new(
|
|
18
22
|
# arity_for_uniqueness: 1,
|
|
19
23
|
# arity_validation: :warning, # or nil, false, or :error
|
|
20
24
|
# unique_at_runtime: true,
|
|
@@ -22,12 +26,23 @@ require "resque/unique_by_arity/validation"
|
|
|
22
26
|
# )
|
|
23
27
|
# end
|
|
24
28
|
#
|
|
29
|
+
# NOTE: DO NOT include this module directly.
|
|
30
|
+
# Use the Resque::Plugins::UniqueByArity approach as above.
|
|
31
|
+
# This module is ultimately extended into the job class.
|
|
25
32
|
module Resque
|
|
26
33
|
module UniqueByArity
|
|
34
|
+
PLUGIN_TAG = (ColorizedString['[R-UBA] '].green).freeze
|
|
35
|
+
|
|
27
36
|
def unique_log(message, config_proxy = nil)
|
|
28
37
|
config_proxy ||= uniqueness_configuration
|
|
29
|
-
config_proxy.unique_logger
|
|
38
|
+
config_proxy.unique_logger&.send(config_proxy.unique_log_level, message) if config_proxy.unique_logger
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def unique_debug(message, config_proxy = nil)
|
|
42
|
+
config_proxy ||= uniqueness_configuration
|
|
43
|
+
config_proxy.unique_logger&.debug("#{Resque::UniqueByArity::PLUGIN_TAG}#{message}") if config_proxy.debug_mode
|
|
30
44
|
end
|
|
45
|
+
module_function(:unique_log, :unique_debug)
|
|
31
46
|
|
|
32
47
|
# There are times when the class will need access to the configuration object,
|
|
33
48
|
# such as to override it per instance method
|
|
@@ -48,55 +63,70 @@ module Resque
|
|
|
48
63
|
def uniqueness_config_reset(config = Configuration.new)
|
|
49
64
|
@uniqueness_configuration = config
|
|
50
65
|
end
|
|
66
|
+
|
|
51
67
|
def uniqueness_log_level
|
|
52
68
|
@uniqueness_configuration.log_level
|
|
53
69
|
end
|
|
70
|
+
|
|
54
71
|
def uniqueness_log_level=(log_level)
|
|
55
72
|
@uniqueness_configuration.log_level = log_level
|
|
56
73
|
end
|
|
74
|
+
|
|
57
75
|
def uniqueness_arity_for_uniqueness
|
|
58
76
|
@uniqueness_configuration.arity_for_uniqueness
|
|
59
77
|
end
|
|
78
|
+
|
|
60
79
|
def uniqueness_arity_for_uniqueness=(arity_for_uniqueness)
|
|
61
80
|
@uniqueness_configuration.arity_for_uniqueness = arity_for_uniqueness
|
|
62
81
|
end
|
|
82
|
+
|
|
63
83
|
def uniqueness_arity_validation
|
|
64
84
|
@uniqueness_configuration.arity_validation
|
|
65
85
|
end
|
|
86
|
+
|
|
66
87
|
def uniqueness_arity_validation=(arity_validation)
|
|
67
88
|
@uniqueness_configuration.arity_validation = arity_validation
|
|
68
89
|
end
|
|
90
|
+
|
|
69
91
|
def uniqueness_lock_after_execution_period
|
|
70
92
|
@uniqueness_configuration.lock_after_execution_period
|
|
71
93
|
end
|
|
94
|
+
|
|
72
95
|
def uniqueness_lock_after_execution_period=(lock_after_execution_period)
|
|
73
96
|
@uniqueness_configuration.lock_after_execution_period = lock_after_execution_period
|
|
74
97
|
end
|
|
98
|
+
|
|
75
99
|
def uniqueness_runtime_lock_timeout
|
|
76
100
|
@uniqueness_configuration.runtime_lock_timeout
|
|
77
101
|
end
|
|
102
|
+
|
|
78
103
|
def uniqueness_runtime_lock_timeout=(runtime_lock_timeout)
|
|
79
104
|
@uniqueness_configuration.runtime_lock_timeout = runtime_lock_timeout
|
|
80
105
|
end
|
|
106
|
+
|
|
81
107
|
def uniqueness_unique_at_runtime
|
|
82
108
|
@uniqueness_configuration.unique_at_runtime
|
|
83
109
|
end
|
|
110
|
+
|
|
84
111
|
def uniqueness_unique_at_runtime=(unique_at_runtime)
|
|
85
112
|
@uniqueness_configuration.unique_at_runtime = unique_at_runtime
|
|
86
113
|
end
|
|
114
|
+
|
|
87
115
|
def uniqueness_unique_in_queue
|
|
88
116
|
@uniqueness_configuration.unique_in_queue
|
|
89
117
|
end
|
|
118
|
+
|
|
90
119
|
def uniqueness_unique_in_queue=(unique_in_queue)
|
|
91
120
|
@uniqueness_configuration.unique_in_queue = unique_in_queue
|
|
92
121
|
end
|
|
122
|
+
|
|
93
123
|
def uniqueness_unique_across_queues
|
|
94
124
|
@uniqueness_configuration.unique_across_queues
|
|
95
125
|
end
|
|
126
|
+
|
|
96
127
|
def uniqueness_unique_across_queues=(unique_across_queues)
|
|
97
128
|
@uniqueness_configuration.unique_across_queues = unique_across_queues
|
|
98
129
|
end
|
|
99
130
|
self.uniqueness_configuration = Configuration.new # setup defaults
|
|
100
|
-
module_function(:unique_log)
|
|
101
131
|
end
|
|
102
132
|
end
|
|
@@ -2,36 +2,50 @@ require 'logger'
|
|
|
2
2
|
module Resque
|
|
3
3
|
module UniqueByArity
|
|
4
4
|
class Configuration
|
|
5
|
-
VALID_ARITY_VALIDATION_LEVELS = [
|
|
6
|
-
SKIPPED_ARITY_VALIDATION_LEVELS = [
|
|
5
|
+
VALID_ARITY_VALIDATION_LEVELS = [:warning, :error, :skip, nil, false].freeze
|
|
6
|
+
SKIPPED_ARITY_VALIDATION_LEVELS = [:skip, nil, false].freeze
|
|
7
|
+
DEFAULT_AT_RUNTIME_KEY_BASE = 'r-uar'.freeze
|
|
8
|
+
DEFAULT_IN_QUEUE_KEY_BASE = 'r-uiq'.freeze
|
|
7
9
|
attr_accessor :logger
|
|
8
10
|
attr_accessor :log_level
|
|
9
11
|
attr_accessor :arity_for_uniqueness
|
|
10
12
|
attr_accessor :arity_validation
|
|
11
13
|
attr_accessor :lock_after_execution_period
|
|
12
14
|
attr_accessor :runtime_lock_timeout
|
|
15
|
+
attr_accessor :runtime_requeue_interval
|
|
13
16
|
attr_accessor :unique_at_runtime
|
|
17
|
+
attr_accessor :unique_at_runtime_key_base
|
|
14
18
|
attr_accessor :unique_in_queue
|
|
19
|
+
attr_accessor :unique_in_queue_key_base
|
|
15
20
|
attr_accessor :unique_across_queues
|
|
16
21
|
attr_accessor :base_klass_name
|
|
22
|
+
attr_accessor :debug_mode
|
|
17
23
|
def initialize(**options)
|
|
18
24
|
@logger = options.key?(:logger) ? options[:logger] : Logger.new(STDOUT)
|
|
19
25
|
@log_level = options.key?(:log_level) ? options[:log_level] : :debug
|
|
20
26
|
@arity_for_uniqueness = options.key?(:arity_for_uniqueness) ? options[:arity_for_uniqueness] : 1
|
|
21
27
|
@arity_validation = options.key?(:arity_validation) ? options[:arity_validation] : :warning
|
|
22
|
-
raise ArgumentError, "UniqueByArity
|
|
28
|
+
raise ArgumentError, "Resque::Plugins::UniqueByArity.new requires arity_validation values of #{arity_validation.inspect}, or a class inheriting from Exception, but the value is #{@arity_validation} (#{@arity_validation.class})" unless VALID_ARITY_VALIDATION_LEVELS.include?(@arity_validation) || !@arity_validation.respond_to?(:ancestors) || @arity_validation.ancestors.include?(Exception)
|
|
29
|
+
|
|
23
30
|
@lock_after_execution_period = options.key?(:lock_after_execution_period) ? options[:lock_after_execution_period] : nil
|
|
24
31
|
@runtime_lock_timeout = options.key?(:runtime_lock_timeout) ? options[:runtime_lock_timeout] : nil
|
|
32
|
+
@runtime_requeue_interval = options.key?(:runtime_requeue_interval) ? options[:runtime_requeue_interval] : nil
|
|
25
33
|
@unique_at_runtime = options.key?(:unique_at_runtime) ? options[:unique_at_runtime] : false
|
|
34
|
+
@unique_at_runtime_key_base = options.key?(:unique_at_runtime_key_base) ? options[:unique_at_runtime_key_base] : DEFAULT_AT_RUNTIME_KEY_BASE
|
|
35
|
+
@unique_in_queue_key_base = options.key?(:unique_in_queue_key_base) ? options[:unique_in_queue_key_base] : DEFAULT_IN_QUEUE_KEY_BASE
|
|
26
36
|
@unique_in_queue = options.key?(:unique_in_queue) ? options[:unique_in_queue] : false
|
|
27
37
|
@unique_across_queues = options.key?(:unique_across_queues) ? options[:unique_across_queues] : false
|
|
38
|
+
env_debug = ENV['RESQUE_DEBUG']
|
|
39
|
+
@debug_mode = options.key?(:debug_mode) ? options[:debug_mode] : env_debug == 'true' || (env_debug.is_a?(String) && env_debug.match?(/arity/))
|
|
28
40
|
end
|
|
29
41
|
|
|
30
42
|
def validate
|
|
31
43
|
# The default config initialization shouldn't trigger any warnings.
|
|
32
44
|
if base_klass_name && logger
|
|
33
45
|
log "[#{base_klass_name}] :arity_for_uniqueness is set to #{arity_for_uniqueness}, but no uniqueness enforcement was turned on [:unique_at_runtime, :unique_in_queue, :unique_across_queues]" unless unique_at_runtime || unique_in_queue || unique_across_queues
|
|
34
|
-
log "[#{base_klass_name}] :lock_after_execution_period is set to #{lock_after_execution_period}, but :unique_at_runtime is not set" if lock_after_execution_period && !
|
|
46
|
+
log "[#{base_klass_name}] :lock_after_execution_period is set to #{lock_after_execution_period}, but :unique_at_runtime is not set" if lock_after_execution_period && !(unique_in_queue || unique_across_queues)
|
|
47
|
+
log "[#{base_klass_name}] :runtime_lock_timeout is set to #{runtime_lock_timeout}, but :unique_at_runtime is not set" if runtime_lock_timeout && !unique_at_runtime
|
|
48
|
+
log "[#{base_klass_name}] :runtime_requeue_interval is set to #{runtime_requeue_interval}, but :unique_at_runtime is not set" if runtime_requeue_interval && !unique_at_runtime
|
|
35
49
|
log "[#{base_klass_name}] :unique_in_queue and :unique_across_queues should not be set at the same time, as :unique_across_queues will always supercede :unique_in_queue" if unique_in_queue && unique_across_queues
|
|
36
50
|
end
|
|
37
51
|
end
|
|
@@ -50,15 +64,15 @@ module Resque
|
|
|
50
64
|
|
|
51
65
|
def to_hash
|
|
52
66
|
{
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
logger: logger,
|
|
68
|
+
log_level: log_level,
|
|
69
|
+
arity_for_uniqueness: arity_for_uniqueness,
|
|
70
|
+
arity_validation: arity_validation,
|
|
71
|
+
lock_after_execution_period: lock_after_execution_period,
|
|
72
|
+
runtime_lock_timeout: runtime_lock_timeout,
|
|
73
|
+
unique_at_runtime: unique_at_runtime,
|
|
74
|
+
unique_in_queue: unique_in_queue,
|
|
75
|
+
unique_across_queues: unique_across_queues
|
|
62
76
|
}
|
|
63
77
|
end
|
|
64
78
|
|
|
@@ -68,6 +82,7 @@ module Resque
|
|
|
68
82
|
|
|
69
83
|
def validate_arity(klass_string, perform_method)
|
|
70
84
|
return true if skip_arity_validation?
|
|
85
|
+
|
|
71
86
|
# method.arity -
|
|
72
87
|
# Returns an indication of the number of arguments accepted by a method.
|
|
73
88
|
# Returns a non-negative integer for methods that take a fixed number of arguments.
|
|
@@ -83,7 +98,7 @@ module Resque
|
|
|
83
98
|
"#{klass_string}.#{perform_method.name} has arity of #{perform_method.arity} which will not work with arity_for_uniqueness of #{arity_for_uniqueness}"
|
|
84
99
|
end
|
|
85
100
|
else
|
|
86
|
-
if
|
|
101
|
+
if perform_method.arity.abs < arity_for_uniqueness
|
|
87
102
|
# parform(a, b, c, opts = {}) # => arity == -4
|
|
88
103
|
# and in this case arity for uniqueness can be 0, 1, 2, or 3, because 4 of the arguments are required
|
|
89
104
|
"#{klass_string}.#{perform_method.name} has arity of #{perform_method.arity} which will not work with arity_for_uniqueness of #{arity_for_uniqueness}"
|
|
@@ -91,14 +106,16 @@ module Resque
|
|
|
91
106
|
"#{klass_string}.#{perform_method.name} has the following required parameters: #{required_parameter_names}, which is not enough to satisfy the configured arity_for_uniqueness of #{arity_for_uniqueness}"
|
|
92
107
|
end
|
|
93
108
|
end
|
|
94
|
-
|
|
109
|
+
if msg
|
|
110
|
+
case arity_validation
|
|
95
111
|
when :warning then
|
|
96
112
|
log(ColorizedString[msg].red)
|
|
97
113
|
when :error then
|
|
98
114
|
raise ArgumentError, msg
|
|
99
115
|
else
|
|
100
116
|
raise arity_validation, msg
|
|
101
|
-
|
|
117
|
+
end
|
|
118
|
+
end
|
|
102
119
|
end
|
|
103
120
|
end
|
|
104
121
|
end
|
|
@@ -1,42 +1,63 @@
|
|
|
1
1
|
module Resque
|
|
2
2
|
module UniqueByArity
|
|
3
|
-
module
|
|
3
|
+
module Modulizer
|
|
4
4
|
def self.to_mod(configuration)
|
|
5
5
|
Module.new do
|
|
6
6
|
if configuration.unique_in_queue || configuration.unique_at_runtime || configuration.unique_across_queues
|
|
7
7
|
# @return [Array<String, arguments>] the key base hash used to enforce uniqueness, and the arguments from the payload used to calculate it
|
|
8
8
|
define_method(:redis_unique_hash) do |payload|
|
|
9
9
|
payload = Resque.decode(Resque.encode(payload))
|
|
10
|
-
Resque::UniqueByArity.
|
|
11
|
-
job = payload[
|
|
10
|
+
Resque::UniqueByArity.unique_debug("payload is #{payload.inspect}")
|
|
11
|
+
job = payload['class']
|
|
12
12
|
# It seems possible that some jobs may not have an "args" key in the payload.
|
|
13
|
-
args = payload[
|
|
13
|
+
args = payload['args'] || []
|
|
14
14
|
args.map! do |arg|
|
|
15
15
|
arg.is_a?(Hash) ? arg.sort : arg
|
|
16
16
|
end
|
|
17
17
|
# what is the configured arity for uniqueness?
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
uniqueness_args = if configuration.arity_for_uniqueness.zero?
|
|
19
|
+
[]
|
|
20
|
+
else
|
|
21
|
+
# minus one because zero indexed, so
|
|
22
|
+
# when arity_for_uniqueness is 2 we use args
|
|
23
|
+
# at indexes 0 and 1.
|
|
24
|
+
args[0..(configuration.arity_for_uniqueness - 1)]
|
|
25
|
+
end
|
|
20
26
|
args = { class: job, args: uniqueness_args }
|
|
21
27
|
return [Digest::MD5.hexdigest(Resque.encode(args)), uniqueness_args]
|
|
22
28
|
end
|
|
23
29
|
end
|
|
24
30
|
|
|
25
31
|
if configuration.lock_after_execution_period
|
|
26
|
-
|
|
32
|
+
instance_variable_set(:@lock_after_execution_period, configuration.lock_after_execution_period)
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
if configuration.runtime_lock_timeout
|
|
30
|
-
|
|
36
|
+
instance_variable_set(:@runtime_lock_timeout, configuration.runtime_lock_timeout)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if configuration.runtime_requeue_interval
|
|
40
|
+
instance_variable_set(:@runtime_requeue_interval, configuration.runtime_requeue_interval)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if configuration.unique_at_runtime_key_base
|
|
44
|
+
instance_variable_set(:@unique_at_runtime_key_base, configuration.unique_at_runtime_key_base)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if configuration.unique_in_queue_key_base
|
|
48
|
+
# Can't be overridden per each class because it wouldn't make sense.
|
|
49
|
+
# It wouldn't be able to determine or enforce uniqueness across queues,
|
|
50
|
+
# and general cleanup of stray keys would be nearly impossible.
|
|
51
|
+
Resque::UniqueInQueue.uniq_config&.unique_in_queue_key_base = configuration.unique_in_queue_key_base
|
|
31
52
|
end
|
|
32
53
|
|
|
33
54
|
if configuration.unique_in_queue || configuration.unique_across_queues
|
|
34
|
-
### Gem:
|
|
55
|
+
### Gem: unique_in_queue
|
|
35
56
|
### Plugin Name: Resque::Plugins::UniqueJob
|
|
36
57
|
### Provides: Queue-time uniqueness for a single queue, or across queues
|
|
37
58
|
#
|
|
38
59
|
# Returns a string, used by Resque::Plugins::UniqueJob, that will be used as the prefix to the redis key
|
|
39
|
-
define_method(:
|
|
60
|
+
define_method(:unique_at_runtime_redis_key_prefix) do
|
|
40
61
|
"unique_job:#{self}"
|
|
41
62
|
end
|
|
42
63
|
#
|
|
@@ -46,30 +67,30 @@ module Resque
|
|
|
46
67
|
# Payload is what Resque stored for this job along with the job's class name:
|
|
47
68
|
# a hash containing :class and :args
|
|
48
69
|
# @return [String] the key used to enforce uniqueness (at queue-time)
|
|
49
|
-
define_method(:
|
|
70
|
+
define_method(:unique_in_queue_redis_key) do |queue, payload|
|
|
50
71
|
unique_hash, args_for_uniqueness = redis_unique_hash(payload)
|
|
51
|
-
key = "#{
|
|
52
|
-
Resque::UniqueByArity.
|
|
72
|
+
key = "#{unique_at_runtime_key_namespace(queue)}:#{unique_at_runtime_redis_key_prefix}:#{unique_hash}"
|
|
73
|
+
Resque::UniqueByArity.unique_debug("#{self}.unique_in_queue_redis_key for #{args_for_uniqueness} is: #{ColorizedString[key].green}")
|
|
53
74
|
key
|
|
54
75
|
end
|
|
55
76
|
#
|
|
56
77
|
# @return [Fixnum] number of keys that were deleted
|
|
57
78
|
define_method(:purge_unique_queued_redis_keys) do
|
|
58
|
-
#
|
|
59
|
-
key_match = "#{
|
|
79
|
+
# unique_at_runtime_key_namespace may or may not ignore the queue passed in, depending on config.
|
|
80
|
+
key_match = "#{unique_at_runtime_key_namespace(instance_variable_get(:@queue))}:#{unique_at_runtime_redis_key_prefix}:*"
|
|
60
81
|
keys = Resque.redis.keys(key_match)
|
|
61
|
-
Resque::UniqueByArity.unique_log
|
|
62
|
-
Resque.redis.del keys
|
|
82
|
+
Resque::UniqueByArity.unique_log("#{Resque::UniqueByArity::PLUGIN_TAG}#{Resque::UniqueInQueue::PLUGIN_TAG} Purging #{keys.length} keys from #{ColorizedString[key_match].red}")
|
|
83
|
+
Resque.redis.del keys unless keys.empty?
|
|
63
84
|
end
|
|
64
85
|
if configuration.unique_in_queue
|
|
65
86
|
# @return [String] the Redis namespace of the key used to enforce uniqueness (at queue-time)
|
|
66
|
-
define_method(:
|
|
67
|
-
"
|
|
87
|
+
define_method(:unique_at_runtime_key_namespace) do |queue = nil|
|
|
88
|
+
"#{configuration.unique_in_queue_key_base}:queue:#{queue}:job"
|
|
68
89
|
end
|
|
69
90
|
elsif configuration.unique_across_queues
|
|
70
91
|
# @return [String] the Redis namespace of the key used to enforce uniqueness (at queue-time)
|
|
71
|
-
define_method(:
|
|
72
|
-
"
|
|
92
|
+
define_method(:unique_at_runtime_key_namespace) do |_queue = nil|
|
|
93
|
+
"#{configuration.unique_in_queue_key_base}:across_queues:job"
|
|
73
94
|
end
|
|
74
95
|
end
|
|
75
96
|
end
|
|
@@ -80,29 +101,28 @@ module Resque
|
|
|
80
101
|
if configuration.unique_at_runtime
|
|
81
102
|
# @return [String] the Redis namespace of the key used to enforce uniqueness (at runtime)
|
|
82
103
|
define_method(:runtime_key_namespace) do
|
|
83
|
-
|
|
84
|
-
"unique_at_runtime:#{self}"
|
|
104
|
+
"#{configuration.unique_at_runtime_key_base}:#{self}"
|
|
85
105
|
end
|
|
86
106
|
# Returns a string, used by Resque::Plugins::UniqueAtRuntime, that will be used as the redis key
|
|
87
|
-
# The versions of redis_key from
|
|
107
|
+
# The versions of redis_key from unique_in_queue and resque-lonely_job are incompatible.
|
|
88
108
|
# So we forked resque-lonely_job, change the name of the method so it would not conflict,
|
|
89
109
|
# and now we can override it, and fix the params to be compatible with the redis_key
|
|
90
|
-
# from
|
|
110
|
+
# from unique_in_queue
|
|
91
111
|
# Does not need any customization for arity, because it funnels down to redis_key,
|
|
92
112
|
# and we handle the arity option there
|
|
93
113
|
# @return [String] the key used to enforce loneliness (uniqueness at runtime)
|
|
94
114
|
define_method(:unique_at_runtime_redis_key) do |*args|
|
|
95
|
-
unique_hash, args_for_uniqueness = redis_unique_hash(
|
|
115
|
+
unique_hash, args_for_uniqueness = redis_unique_hash('class' => to_s, 'args' => args)
|
|
96
116
|
key = "#{runtime_key_namespace}:#{unique_hash}"
|
|
97
|
-
Resque::UniqueByArity.
|
|
117
|
+
Resque::UniqueByArity.unique_debug("#{ColorizedString['[R-UAR]'].yellow} #{self}.unique_at_runtime_redis_key for #{args_for_uniqueness} is: #{ColorizedString[key].yellow}")
|
|
98
118
|
key
|
|
99
119
|
end
|
|
100
120
|
# @return [Fixnum] number of keys that were deleted
|
|
101
121
|
define_method(:purge_unique_at_runtime_redis_keys) do
|
|
102
122
|
key_match = "#{runtime_key_namespace}:*"
|
|
103
123
|
keys = Resque.redis.keys(key_match)
|
|
104
|
-
Resque::UniqueByArity.unique_log
|
|
105
|
-
Resque.redis.del keys
|
|
124
|
+
Resque::UniqueByArity.unique_log("#{ColorizedString['[R-UBA][R-UAR]'].red} Purging #{keys.length} keys from #{ColorizedString[key_match].red}")
|
|
125
|
+
Resque.redis.del keys unless keys.empty?
|
|
106
126
|
end
|
|
107
127
|
end
|
|
108
128
|
end
|
|
@@ -1,31 +1,37 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
require 'resque/unique_by_arity/version'
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require File.expand_path('lib/resque/unique_by_arity/version', __dir__)
|
|
5
4
|
|
|
6
5
|
Gem::Specification.new do |spec|
|
|
7
|
-
spec.name =
|
|
6
|
+
spec.name = 'resque-unique_by_arity'
|
|
8
7
|
spec.version = Resque::UniqueByArity::VERSION
|
|
9
|
-
spec.authors = [
|
|
10
|
-
spec.email = [
|
|
11
|
-
spec.license =
|
|
8
|
+
spec.authors = ['Peter H. Boling']
|
|
9
|
+
spec.email = ['peter.boling@gmail.com']
|
|
10
|
+
spec.license = 'MIT'
|
|
12
11
|
|
|
13
|
-
spec.summary =
|
|
14
|
-
spec.description =
|
|
15
|
-
spec.homepage =
|
|
16
|
-
spec.required_ruby_version =
|
|
12
|
+
spec.summary = 'Configure resque-unique_in_queue and resque-unique_at_runtime uniqueness by arity of perform method'
|
|
13
|
+
spec.description = 'Configure resque-unique_in_queue and resque-unique_at_runtime uniqueness by arity of perform method, with automated cleanup tools'
|
|
14
|
+
spec.homepage = 'https://github.com/pboling/resque-unique_by_arity'
|
|
15
|
+
spec.required_ruby_version = '>= 2.3.0'
|
|
17
16
|
|
|
18
|
-
spec.files
|
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
19
18
|
f.match(%r{^(test|spec|features)/})
|
|
20
19
|
end
|
|
21
|
-
spec.
|
|
22
|
-
|
|
23
|
-
spec.
|
|
20
|
+
spec.require_paths = ['lib']
|
|
21
|
+
|
|
22
|
+
spec.add_runtime_dependency 'colorize', '~> 0.8'
|
|
23
|
+
spec.add_runtime_dependency 'resque', '>= 1.2'
|
|
24
|
+
spec.add_runtime_dependency 'resque-unique_in_queue', '~> 1.0'
|
|
25
|
+
spec.add_runtime_dependency 'resque-unique_at_runtime', '~> 3.0'
|
|
24
26
|
|
|
25
|
-
spec.
|
|
26
|
-
spec.
|
|
27
|
-
spec.
|
|
28
|
-
spec.add_development_dependency
|
|
29
|
-
spec.add_development_dependency
|
|
30
|
-
spec.add_development_dependency
|
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
|
28
|
+
spec.add_development_dependency 'byebug', '~> 10.0'
|
|
29
|
+
spec.add_development_dependency 'pry', '~> 0.11'
|
|
30
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.6'
|
|
31
|
+
spec.add_development_dependency 'rake', '~> 12.3'
|
|
32
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
33
|
+
spec.add_development_dependency 'rspec-block_is_expected', '~> 1.0'
|
|
34
|
+
spec.add_development_dependency 'rspec-stubbed_env', '~> 1.0'
|
|
35
|
+
spec.add_development_dependency 'rubocop', '~> 0.60'
|
|
36
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 1.30'
|
|
31
37
|
end
|
metadata
CHANGED
|
@@ -1,71 +1,127 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: resque-unique_by_arity
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
- Peter Boling
|
|
7
|
+
- Peter H. Boling
|
|
8
8
|
autorequire:
|
|
9
|
-
bindir:
|
|
9
|
+
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-
|
|
11
|
+
date: 2018-11-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: colorize
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '0.8'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
26
|
+
version: '0.8'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: resque
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.2'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.2'
|
|
27
41
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
42
|
+
name: resque-unique_in_queue
|
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
|
30
44
|
requirements:
|
|
31
45
|
- - "~>"
|
|
32
46
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '0
|
|
47
|
+
version: '1.0'
|
|
34
48
|
type: :runtime
|
|
35
49
|
prerelease: false
|
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
51
|
requirements:
|
|
38
52
|
- - "~>"
|
|
39
53
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '0
|
|
54
|
+
version: '1.0'
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
56
|
+
name: resque-unique_at_runtime
|
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
|
44
58
|
requirements:
|
|
45
59
|
- - "~>"
|
|
46
60
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0
|
|
61
|
+
version: '3.0'
|
|
48
62
|
type: :runtime
|
|
49
63
|
prerelease: false
|
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
65
|
requirements:
|
|
52
66
|
- - "~>"
|
|
53
67
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0
|
|
68
|
+
version: '3.0'
|
|
55
69
|
- !ruby/object:Gem::Dependency
|
|
56
70
|
name: bundler
|
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
|
58
72
|
requirements:
|
|
59
73
|
- - "~>"
|
|
60
74
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '1.
|
|
75
|
+
version: '1.16'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '1.16'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: byebug
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '10.0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '10.0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: pry
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0.11'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0.11'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: pry-byebug
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '3.6'
|
|
62
118
|
type: :development
|
|
63
119
|
prerelease: false
|
|
64
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
121
|
requirements:
|
|
66
122
|
- - "~>"
|
|
67
123
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
124
|
+
version: '3.6'
|
|
69
125
|
- !ruby/object:Gem::Dependency
|
|
70
126
|
name: rake
|
|
71
127
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,7 +150,64 @@ dependencies:
|
|
|
94
150
|
- - "~>"
|
|
95
151
|
- !ruby/object:Gem::Version
|
|
96
152
|
version: '3.0'
|
|
97
|
-
|
|
153
|
+
- !ruby/object:Gem::Dependency
|
|
154
|
+
name: rspec-block_is_expected
|
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - "~>"
|
|
158
|
+
- !ruby/object:Gem::Version
|
|
159
|
+
version: '1.0'
|
|
160
|
+
type: :development
|
|
161
|
+
prerelease: false
|
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - "~>"
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '1.0'
|
|
167
|
+
- !ruby/object:Gem::Dependency
|
|
168
|
+
name: rspec-stubbed_env
|
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - "~>"
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '1.0'
|
|
174
|
+
type: :development
|
|
175
|
+
prerelease: false
|
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
177
|
+
requirements:
|
|
178
|
+
- - "~>"
|
|
179
|
+
- !ruby/object:Gem::Version
|
|
180
|
+
version: '1.0'
|
|
181
|
+
- !ruby/object:Gem::Dependency
|
|
182
|
+
name: rubocop
|
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
|
184
|
+
requirements:
|
|
185
|
+
- - "~>"
|
|
186
|
+
- !ruby/object:Gem::Version
|
|
187
|
+
version: '0.60'
|
|
188
|
+
type: :development
|
|
189
|
+
prerelease: false
|
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
191
|
+
requirements:
|
|
192
|
+
- - "~>"
|
|
193
|
+
- !ruby/object:Gem::Version
|
|
194
|
+
version: '0.60'
|
|
195
|
+
- !ruby/object:Gem::Dependency
|
|
196
|
+
name: rubocop-rspec
|
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
|
198
|
+
requirements:
|
|
199
|
+
- - "~>"
|
|
200
|
+
- !ruby/object:Gem::Version
|
|
201
|
+
version: '1.30'
|
|
202
|
+
type: :development
|
|
203
|
+
prerelease: false
|
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
205
|
+
requirements:
|
|
206
|
+
- - "~>"
|
|
207
|
+
- !ruby/object:Gem::Version
|
|
208
|
+
version: '1.30'
|
|
209
|
+
description: Configure resque-unique_in_queue and resque-unique_at_runtime uniqueness
|
|
210
|
+
by arity of perform method, with automated cleanup tools
|
|
98
211
|
email:
|
|
99
212
|
- peter.boling@gmail.com
|
|
100
213
|
executables: []
|
|
@@ -103,17 +216,21 @@ extra_rdoc_files: []
|
|
|
103
216
|
files:
|
|
104
217
|
- ".gitignore"
|
|
105
218
|
- ".rspec"
|
|
219
|
+
- ".rspec_status"
|
|
220
|
+
- ".rubocop.yml"
|
|
221
|
+
- ".rubocop_todo.yml"
|
|
222
|
+
- ".ruby-version"
|
|
106
223
|
- ".travis.yml"
|
|
107
224
|
- Gemfile
|
|
225
|
+
- LICENSE
|
|
108
226
|
- README.md
|
|
109
227
|
- Rakefile
|
|
110
228
|
- bin/console
|
|
111
229
|
- bin/setup
|
|
112
|
-
- lib/resque/
|
|
230
|
+
- lib/resque/plugins/unique_by_arity.rb
|
|
113
231
|
- lib/resque/unique_by_arity.rb
|
|
114
232
|
- lib/resque/unique_by_arity/configuration.rb
|
|
115
|
-
- lib/resque/unique_by_arity/
|
|
116
|
-
- lib/resque/unique_by_arity/cop_modulizer.rb
|
|
233
|
+
- lib/resque/unique_by_arity/modulizer.rb
|
|
117
234
|
- lib/resque/unique_by_arity/validation.rb
|
|
118
235
|
- lib/resque/unique_by_arity/version.rb
|
|
119
236
|
- resque-unique_by_arity.gemspec
|
|
@@ -129,7 +246,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
129
246
|
requirements:
|
|
130
247
|
- - ">="
|
|
131
248
|
- !ruby/object:Gem::Version
|
|
132
|
-
version: 2.
|
|
249
|
+
version: 2.3.0
|
|
133
250
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
251
|
requirements:
|
|
135
252
|
- - ">="
|
|
@@ -140,6 +257,6 @@ rubyforge_project:
|
|
|
140
257
|
rubygems_version: 2.7.7
|
|
141
258
|
signing_key:
|
|
142
259
|
specification_version: 4
|
|
143
|
-
summary:
|
|
144
|
-
|
|
260
|
+
summary: Configure resque-unique_in_queue and resque-unique_at_runtime uniqueness
|
|
261
|
+
by arity of perform method
|
|
145
262
|
test_files: []
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
module ResqueSoloUniqueHack
|
|
2
|
-
def unique_key(queue, item)
|
|
3
|
-
# "solo:queue:#{queue}:job:#{const_for(item).redis_key(item)}"
|
|
4
|
-
const_for(item).unique_at_queue_time_redis_key(queue, item)
|
|
5
|
-
# NOTE: DOES NOT CALL SUPER
|
|
6
|
-
end
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
ResqueSolo::Queue.singleton_class.prepend ResqueSoloUniqueHack
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
module Resque
|
|
2
|
-
module UniqueByArity
|
|
3
|
-
class Cop < Module
|
|
4
|
-
def initialize(**config)
|
|
5
|
-
@configuration = Resque::UniqueByArity::Configuration.new(**config)
|
|
6
|
-
end
|
|
7
|
-
def included(base)
|
|
8
|
-
return unless @configuration
|
|
9
|
-
@configuration.base_klass_name = base.to_s
|
|
10
|
-
@configuration.validate
|
|
11
|
-
base.send(:extend, Resque::UniqueByArity)
|
|
12
|
-
base.uniqueness_config_reset(@configuration.dup)
|
|
13
|
-
|
|
14
|
-
# gem is resque_solo, which is a rewrite of resque-loner
|
|
15
|
-
# see: https://github.com/neighborland/resque_solo
|
|
16
|
-
# defines a redis_key method, which we have to override.
|
|
17
|
-
base.send(:include, Resque::Plugins::UniqueJob) if @configuration.unique_in_queue || @configuration.unique_across_queues
|
|
18
|
-
|
|
19
|
-
# gem is resque-unique_at_runtime, which is a rewrite of resque-lonely_job
|
|
20
|
-
# see: https://github.com/pboling/resque-unique_at_runtime
|
|
21
|
-
base.send(:extend, Resque::Plugins::UniqueAtRuntime) if @configuration.unique_at_runtime
|
|
22
|
-
|
|
23
|
-
uniqueness_cop_module = Resque::UniqueByArity::CopModulizer.to_mod(@configuration)
|
|
24
|
-
# This will override methods from both plugins above, if configured for both
|
|
25
|
-
base.send(:extend, uniqueness_cop_module)
|
|
26
|
-
|
|
27
|
-
base.include Resque::UniqueByArity::Validation unless @configuration.skip_arity_validation?
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|