sidekiq-unique-jobs 7.0.0.beta4 → 7.0.0.beta5
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.
Potentially problematic release.
This version of sidekiq-unique-jobs might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/README.md +5 -3
- data/lib/sidekiq_unique_jobs/cli.rb +10 -6
- data/lib/sidekiq_unique_jobs/constants.rb +1 -0
- data/lib/sidekiq_unique_jobs/digests.rb +34 -48
- data/lib/sidekiq_unique_jobs/lock/until_and_while_executing.rb +13 -7
- data/lib/sidekiq_unique_jobs/lua/delete_by_digest.lua +9 -13
- data/lib/sidekiq_unique_jobs/on_conflict/replace.rb +5 -1
- data/lib/sidekiq_unique_jobs/orphans/manager.rb +44 -8
- data/lib/sidekiq_unique_jobs/version.rb +1 -1
- data/lib/sidekiq_unique_jobs/web.rb +2 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d70a897c3f1aa180a2ec8d966218836038210bdf294f3dc2be955f3e2f4915f9
|
4
|
+
data.tar.gz: d57e756148fe58b0b72c8d1e32208bb6db61f1121a54b80ecbd275ee79a46828
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5c94eb9bc3b4ce1dd5ebcd7f7af5ae4456ff6783a47783ef5fe96b6bdf5c045d8adfff3e5abfe4390f2ee01d20e8e47a4cc50132cb2f31e17947dccbc0caf82
|
7
|
+
data.tar.gz: e0d9b47ba7930b254ce7e6741577de17a02607f8d89a5ff3aa313ed978bfefdb518d61f62c4437f75a93d267e3ffd3b5cc58bfd003160fc552d0bb91df21ec76
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,35 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v7.0.0.beta4](https://github.com/mhenrixon/sidekiq-unique-jobs/tree/v7.0.0.beta4) (2019-11-25)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/mhenrixon/sidekiq-unique-jobs/compare/v7.0.0.beta3...v7.0.0.beta4)
|
6
|
+
|
7
|
+
**Fixed bugs:**
|
8
|
+
|
9
|
+
- Fix ruby reaper [\#444](https://github.com/mhenrixon/sidekiq-unique-jobs/pull/444) ([mhenrixon](https://github.com/mhenrixon))
|
10
|
+
|
11
|
+
## [v7.0.0.beta3](https://github.com/mhenrixon/sidekiq-unique-jobs/tree/v7.0.0.beta3) (2019-11-24)
|
12
|
+
|
13
|
+
[Full Changelog](https://github.com/mhenrixon/sidekiq-unique-jobs/compare/v7.0.0.beta2...v7.0.0.beta3)
|
14
|
+
|
15
|
+
**Implemented enhancements:**
|
16
|
+
|
17
|
+
- Brpoplpush redis script [\#434](https://github.com/mhenrixon/sidekiq-unique-jobs/pull/434) ([mhenrixon](https://github.com/mhenrixon))
|
18
|
+
- Drop support for almost EOL ruby 2.4 [\#433](https://github.com/mhenrixon/sidekiq-unique-jobs/pull/433) ([mhenrixon](https://github.com/mhenrixon))
|
19
|
+
|
20
|
+
**Fixed bugs:**
|
21
|
+
|
22
|
+
- Redis is busy running script and script never terminates [\#441](https://github.com/mhenrixon/sidekiq-unique-jobs/issues/441)
|
23
|
+
- Make the ruby reaper plain ruby [\#443](https://github.com/mhenrixon/sidekiq-unique-jobs/pull/443) ([mhenrixon](https://github.com/mhenrixon))
|
24
|
+
|
25
|
+
**Closed issues:**
|
26
|
+
|
27
|
+
- Some jobs seem to be treated as duplicate despite empty queue [\#440](https://github.com/mhenrixon/sidekiq-unique-jobs/issues/440)
|
28
|
+
|
29
|
+
**Merged pull requests:**
|
30
|
+
|
31
|
+
- Fix typo and some formatting issues in README [\#442](https://github.com/mhenrixon/sidekiq-unique-jobs/pull/442) ([ajkerr](https://github.com/ajkerr))
|
32
|
+
|
3
33
|
## [v7.0.0.beta2](https://github.com/mhenrixon/sidekiq-unique-jobs/tree/v7.0.0.beta2) (2019-10-08)
|
4
34
|
|
5
35
|
[Full Changelog](https://github.com/mhenrixon/sidekiq-unique-jobs/compare/v7.0.0.beta1...v7.0.0.beta2)
|
data/README.md
CHANGED
@@ -355,7 +355,7 @@ With this lock type it is possible to put any number of these jobs on the queue,
|
|
355
355
|
|
356
356
|
**NOTE** Unless this job is configured with a `lock_timeout: nil` or `lock_timeout: > 0` then all jobs that are attempted to be executed will just be dropped without waiting.
|
357
357
|
|
358
|
-
There is an example of this to try it out in the `
|
358
|
+
There is an example of this to try it out in the `myapp` application. Run `foreman start` in the root of the directory and open the url: `localhost:5000/work/duplicate_while_executing`.
|
359
359
|
|
360
360
|
In the console you should see something like:
|
361
361
|
|
@@ -601,7 +601,8 @@ For sidekiq versions before 5.1 a `sidekiq_retries_exhausted` block is required
|
|
601
601
|
```ruby
|
602
602
|
class MyWorker
|
603
603
|
sidekiq_retries_exhausted do |msg, _ex|
|
604
|
-
|
604
|
+
digest = msg['unique_digest']
|
605
|
+
SidekiqUniqueJobs::Digests.delete_by_digest(digest) if digest
|
605
606
|
end
|
606
607
|
end
|
607
608
|
```
|
@@ -612,7 +613,8 @@ Starting in v5.1, Sidekiq can also fire a global callback when a job dies:
|
|
612
613
|
# this goes in your initializer
|
613
614
|
Sidekiq.configure_server do |config|
|
614
615
|
config.death_handlers << ->(job, _ex) do
|
615
|
-
|
616
|
+
digest = msg['unique_digest']
|
617
|
+
SidekiqUniqueJobs::Digests.delete_by_digest(digest) if digest
|
616
618
|
end
|
617
619
|
end
|
618
620
|
```
|
@@ -16,9 +16,9 @@ module SidekiqUniqueJobs
|
|
16
16
|
desc "list PATTERN", "list all unique digests and their expiry time"
|
17
17
|
option :count, aliases: :c, type: :numeric, default: 1000, desc: "The max number of digests to return"
|
18
18
|
def list(pattern = "*")
|
19
|
-
|
20
|
-
say "Found #{
|
21
|
-
print_in_columns(
|
19
|
+
entries = digests.entries(pattern: pattern, count: options[:count])
|
20
|
+
say "Found #{entries.size} digests matching '#{pattern}':"
|
21
|
+
print_in_columns(entries.sort) if entries.any?
|
22
22
|
end
|
23
23
|
|
24
24
|
desc "del PATTERN", "deletes unique digests from redis by pattern"
|
@@ -27,10 +27,10 @@ module SidekiqUniqueJobs
|
|
27
27
|
def del(pattern)
|
28
28
|
max_count = options[:count]
|
29
29
|
if options[:dry_run]
|
30
|
-
|
31
|
-
say "Would delete #{
|
30
|
+
result = digests.entries(pattern: pattern, count: max_count)
|
31
|
+
say "Would delete #{result.size} digests matching '#{pattern}'"
|
32
32
|
else
|
33
|
-
deleted_count =
|
33
|
+
deleted_count = digests.delete_by_pattern(pattern, count: max_count)
|
34
34
|
say "Deleted #{deleted_count} digests matching '#{pattern}'"
|
35
35
|
end
|
36
36
|
end
|
@@ -46,6 +46,10 @@ module SidekiqUniqueJobs
|
|
46
46
|
end
|
47
47
|
|
48
48
|
no_commands do
|
49
|
+
def digests
|
50
|
+
@digests ||= SidekiqUniqueJobs::Digests.new
|
51
|
+
end
|
52
|
+
|
49
53
|
def console_class
|
50
54
|
require "pry"
|
51
55
|
Pry
|
@@ -27,26 +27,44 @@ module SidekiqUniqueJobs
|
|
27
27
|
redis { |conn| conn.zadd(key, now_f, digest) }
|
28
28
|
end
|
29
29
|
|
30
|
+
# Deletes unique digests by pattern
|
30
31
|
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
# @overload call_script(digest: "abcdefab")
|
34
|
-
# Call script with digest
|
35
|
-
# @param [String] digest: a digest to delete
|
36
|
-
# @overload call_script(pattern: "*", count: 1_000)
|
37
|
-
# Call script with pattern
|
38
|
-
# @param [String] pattern: "*" a pattern to match
|
39
|
-
# @param [String] count: DEFAULT_COUNT the number of keys to delete
|
40
|
-
#
|
41
|
-
# @raise [ArgumentError] when given neither pattern nor digest
|
42
|
-
#
|
32
|
+
# @param [String] pattern a key pattern to match with
|
33
|
+
# @param [Integer] count the maximum number
|
43
34
|
# @return [Array<String>] with unique digests
|
35
|
+
def delete_by_pattern(pattern, count: DEFAULT_COUNT)
|
36
|
+
result, elapsed = timed do
|
37
|
+
digests = entries(pattern: pattern, count: count).keys
|
38
|
+
redis { |conn| BatchDelete.call(digests, conn) }
|
39
|
+
end
|
40
|
+
|
41
|
+
log_info("#{__method__}(#{pattern}, count: #{count}) completed in #{elapsed}ms")
|
42
|
+
|
43
|
+
result
|
44
|
+
end
|
45
|
+
|
46
|
+
# Delete unique digests by digest
|
47
|
+
# Also deletes the :AVAILABLE, :EXPIRED etc keys
|
44
48
|
#
|
45
|
-
|
46
|
-
|
47
|
-
|
49
|
+
# @param [String] digest a unique digest to delete
|
50
|
+
def delete_by_digest(digest) # rubocop:disable Metrics/MethodLength
|
51
|
+
result, elapsed = timed do
|
52
|
+
call_script(:delete_by_digest, [
|
53
|
+
digest,
|
54
|
+
"#{digest}:QUEUED",
|
55
|
+
"#{digest}:PRIMED",
|
56
|
+
"#{digest}:LOCKED",
|
57
|
+
"#{digest}:RUN",
|
58
|
+
"#{digest}:RUN:QUEUED",
|
59
|
+
"#{digest}:RUN:PRIMED",
|
60
|
+
"#{digest}:RUN:LOCKED",
|
61
|
+
key,
|
62
|
+
])
|
63
|
+
end
|
64
|
+
|
65
|
+
log_info("#{__method__}(#{digest}) completed in #{elapsed}ms")
|
48
66
|
|
49
|
-
|
67
|
+
result
|
50
68
|
end
|
51
69
|
|
52
70
|
#
|
@@ -92,37 +110,5 @@ module SidekiqUniqueJobs
|
|
92
110
|
]
|
93
111
|
end
|
94
112
|
end
|
95
|
-
|
96
|
-
private
|
97
|
-
|
98
|
-
# Deletes unique digests by pattern
|
99
|
-
#
|
100
|
-
# @param [String] pattern a key pattern to match with
|
101
|
-
# @param [Integer] count the maximum number
|
102
|
-
# @return [Array<String>] with unique digests
|
103
|
-
def delete_by_pattern(pattern, count: DEFAULT_COUNT)
|
104
|
-
result, elapsed = timed do
|
105
|
-
digests = entries(pattern: pattern, count: count).keys
|
106
|
-
redis { |conn| BatchDelete.call(digests, conn) }
|
107
|
-
end
|
108
|
-
|
109
|
-
log_info("#{__method__}(#{pattern}, count: #{count}) completed in #{elapsed}ms")
|
110
|
-
|
111
|
-
result
|
112
|
-
end
|
113
|
-
|
114
|
-
# Delete unique digests by digest
|
115
|
-
# Also deletes the :AVAILABLE, :EXPIRED etc keys
|
116
|
-
#
|
117
|
-
# @param [String] digest a unique digest to delete
|
118
|
-
def delete_by_digest(digest)
|
119
|
-
result, elapsed = timed do
|
120
|
-
call_script(:delete_by_digest, [digest, key])
|
121
|
-
end
|
122
|
-
|
123
|
-
log_info("#{__method__}(#{digest}) completed in #{elapsed}ms")
|
124
|
-
|
125
|
-
result
|
126
|
-
end
|
127
113
|
end
|
128
114
|
end
|
@@ -17,18 +17,24 @@ module SidekiqUniqueJobs
|
|
17
17
|
# @yield to the worker class perform method
|
18
18
|
def execute
|
19
19
|
if unlock
|
20
|
-
|
20
|
+
lock_on_failure do
|
21
|
+
runtime_lock.execute { return yield }
|
22
|
+
end
|
21
23
|
else
|
22
24
|
log_warn "couldn't unlock digest: #{item[UNIQUE_DIGEST]} #{item[JID]}"
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
private
|
29
|
+
|
30
|
+
def lock_on_failure
|
31
|
+
yield
|
32
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
33
|
+
log_error("Runtime lock failed to execute job, restoring server lock")
|
34
|
+
lock
|
35
|
+
raise
|
36
|
+
end
|
37
|
+
|
32
38
|
def runtime_lock
|
33
39
|
@runtime_lock ||= SidekiqUniqueJobs::Lock::WhileExecuting.new(item, callback, redis_pool)
|
34
40
|
end
|
@@ -1,6 +1,13 @@
|
|
1
1
|
-------- BEGIN keys ---------
|
2
|
-
local digest
|
3
|
-
local
|
2
|
+
local digest = KEYS[1]
|
3
|
+
local queued = KEYS[2]
|
4
|
+
local primed = KEYS[3]
|
5
|
+
local locked = KEYS[4]
|
6
|
+
local run_digest = KEYS[5]
|
7
|
+
local run_queued = KEYS[6]
|
8
|
+
local run_primed = KEYS[7]
|
9
|
+
local run_locked = KEYS[8]
|
10
|
+
local digests = KEYS[9]
|
4
11
|
-------- END keys ---------
|
5
12
|
|
6
13
|
-------- BEGIN injected arguments --------
|
@@ -15,17 +22,6 @@ local redisversion = tostring(ARGV[5])
|
|
15
22
|
<%= include_partial "shared/_common.lua" %>
|
16
23
|
---------- END local functions ----------
|
17
24
|
|
18
|
-
-------- BEGIN Variables --------
|
19
|
-
local queued = digest .. ":QUEUED"
|
20
|
-
local primed = digest .. ":PRIMED"
|
21
|
-
local locked = digest .. ":LOCKED"
|
22
|
-
local run_digest = digest .. ":RUN"
|
23
|
-
local run_queued = digest .. ":RUN:QUEUED"
|
24
|
-
local run_primed = digest .. ":RUN:PRIMED"
|
25
|
-
local run_locked = digest .. ":RUN:LOCKED"
|
26
|
-
-------- END Variables --------
|
27
|
-
|
28
|
-
|
29
25
|
-------- BEGIN delete_by_digest.lua --------
|
30
26
|
local counter = 0
|
31
27
|
local redis_version = toversion(redisversion)
|
@@ -64,7 +64,11 @@ module SidekiqUniqueJobs
|
|
64
64
|
# @return [Integer] the number of keys deleted
|
65
65
|
#
|
66
66
|
def delete_lock
|
67
|
-
|
67
|
+
digests.delete_by_digest(unique_digest)
|
68
|
+
end
|
69
|
+
|
70
|
+
def digests
|
71
|
+
@digests ||= SidekiqUniqueJobs::Digests.new
|
68
72
|
end
|
69
73
|
end
|
70
74
|
end
|
@@ -7,7 +7,9 @@ module SidekiqUniqueJobs
|
|
7
7
|
#
|
8
8
|
# @author Mikael Henriksson <mikael@zoolutions.se>
|
9
9
|
#
|
10
|
-
|
10
|
+
module Manager
|
11
|
+
module_function
|
12
|
+
|
11
13
|
include SidekiqUniqueJobs::Connection
|
12
14
|
include SidekiqUniqueJobs::Logging
|
13
15
|
|
@@ -17,8 +19,11 @@ module SidekiqUniqueJobs
|
|
17
19
|
#
|
18
20
|
# @return [Concurrent::TimerTask] the task that was started
|
19
21
|
#
|
20
|
-
def
|
22
|
+
def start # rubocop:disable
|
23
|
+
return if registered?
|
24
|
+
|
21
25
|
with_logging_context do
|
26
|
+
register_reaper_process
|
22
27
|
log_info("Starting Reaper")
|
23
28
|
task.add_observer(Observer.new)
|
24
29
|
task.execute
|
@@ -32,9 +37,10 @@ module SidekiqUniqueJobs
|
|
32
37
|
#
|
33
38
|
# @return [Boolean]
|
34
39
|
#
|
35
|
-
def
|
40
|
+
def stop
|
36
41
|
with_logging_context do
|
37
42
|
log_info("Stopping Reaper")
|
43
|
+
unregister_reaper_process
|
38
44
|
task.shutdown
|
39
45
|
end
|
40
46
|
end
|
@@ -45,7 +51,7 @@ module SidekiqUniqueJobs
|
|
45
51
|
#
|
46
52
|
# @return [<type>] <description>
|
47
53
|
#
|
48
|
-
def
|
54
|
+
def task
|
49
55
|
@task ||= Concurrent::TimerTask.new(timer_task_options) do
|
50
56
|
with_logging_context do
|
51
57
|
redis do |conn|
|
@@ -61,7 +67,7 @@ module SidekiqUniqueJobs
|
|
61
67
|
#
|
62
68
|
# @return [Hash]
|
63
69
|
#
|
64
|
-
def
|
70
|
+
def timer_task_options
|
65
71
|
{ run_now: true,
|
66
72
|
execution_interval: reaper_interval,
|
67
73
|
timeout_interval: reaper_timeout }
|
@@ -70,14 +76,14 @@ module SidekiqUniqueJobs
|
|
70
76
|
#
|
71
77
|
# @see SidekiqUniqueJobs::Config#reaper_interval
|
72
78
|
#
|
73
|
-
def
|
79
|
+
def reaper_interval
|
74
80
|
SidekiqUniqueJobs.config.reaper_interval
|
75
81
|
end
|
76
82
|
|
77
83
|
#
|
78
84
|
# @see SidekiqUniqueJobs::Config#reaper_timeout
|
79
85
|
#
|
80
|
-
def
|
86
|
+
def reaper_timeout
|
81
87
|
SidekiqUniqueJobs.config.reaper_timeout
|
82
88
|
end
|
83
89
|
|
@@ -88,13 +94,43 @@ module SidekiqUniqueJobs
|
|
88
94
|
# @return [Hash] when logger responds to `:with_context`
|
89
95
|
# @return [String] when logger does not responds to `:with_context`
|
90
96
|
#
|
91
|
-
def
|
97
|
+
def logging_context
|
92
98
|
if logger_context_hash?
|
93
99
|
{ "uniquejobs" => "reaper" }
|
94
100
|
else
|
95
101
|
"uniquejobs=orphan-reaper"
|
96
102
|
end
|
97
103
|
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Checks if a reaper is already registered
|
107
|
+
#
|
108
|
+
#
|
109
|
+
# @return [true, false]
|
110
|
+
#
|
111
|
+
def registered?
|
112
|
+
redis { |conn| conn.get(UNIQUE_REAPER) }.to_i == 1
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# Writes a mutex key to redis
|
117
|
+
#
|
118
|
+
#
|
119
|
+
# @return [void]
|
120
|
+
#
|
121
|
+
def register_reaper_process
|
122
|
+
redis { |conn| conn.set(UNIQUE_REAPER, 1) }
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Removes mutex key from redis
|
127
|
+
#
|
128
|
+
#
|
129
|
+
# @return [void]
|
130
|
+
#
|
131
|
+
def unregister_reaper_process
|
132
|
+
redis { |conn| conn.del(UNIQUE_REAPER) }
|
133
|
+
end
|
98
134
|
end
|
99
135
|
end
|
100
136
|
end
|
@@ -32,7 +32,7 @@ module SidekiqUniqueJobs
|
|
32
32
|
end
|
33
33
|
|
34
34
|
app.get "/locks/delete_all" do
|
35
|
-
digests.
|
35
|
+
digests.delete_by_pattern("*", count: digests.count)
|
36
36
|
redirect_to :locks
|
37
37
|
end
|
38
38
|
|
@@ -44,7 +44,7 @@ module SidekiqUniqueJobs
|
|
44
44
|
end
|
45
45
|
|
46
46
|
app.get "/locks/:digest/delete" do
|
47
|
-
digests.
|
47
|
+
digests.delete_by_digest(params[:digest])
|
48
48
|
redirect_to :locks
|
49
49
|
end
|
50
50
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-unique-jobs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.0.0.
|
4
|
+
version: 7.0.0.beta5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mikael Henriksson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-11-
|
11
|
+
date: 2019-11-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: brpoplpush-redis_script
|
@@ -371,7 +371,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
371
371
|
- !ruby/object:Gem::Version
|
372
372
|
version: 1.3.1
|
373
373
|
requirements: []
|
374
|
-
rubygems_version: 3.0.
|
374
|
+
rubygems_version: 3.0.6
|
375
375
|
signing_key:
|
376
376
|
specification_version: 4
|
377
377
|
summary: Sidekiq middleware that prevents duplicates jobs
|