sidekiq-unique-jobs 7.0.0.beta26 → 7.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq-unique-jobs might be problematic. Click here for more details.

Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +89 -11
  3. data/README.md +154 -36
  4. data/lib/sidekiq-unique-jobs.rb +0 -2
  5. data/lib/sidekiq_unique_jobs.rb +1 -0
  6. data/lib/sidekiq_unique_jobs/changelog.rb +11 -4
  7. data/lib/sidekiq_unique_jobs/config.rb +2 -2
  8. data/lib/sidekiq_unique_jobs/constants.rb +4 -0
  9. data/lib/sidekiq_unique_jobs/digests.rb +1 -1
  10. data/lib/sidekiq_unique_jobs/job.rb +1 -1
  11. data/lib/sidekiq_unique_jobs/lock/validator.rb +2 -1
  12. data/lib/sidekiq_unique_jobs/lock/while_executing.rb +1 -1
  13. data/lib/sidekiq_unique_jobs/lock_args.rb +4 -4
  14. data/lib/sidekiq_unique_jobs/lock_config.rb +2 -0
  15. data/lib/sidekiq_unique_jobs/locksmith.rb +1 -1
  16. data/lib/sidekiq_unique_jobs/lua/delete_by_digest.lua +1 -1
  17. data/lib/sidekiq_unique_jobs/lua/lock.lua +1 -2
  18. data/lib/sidekiq_unique_jobs/lua/reap_orphans.lua +8 -7
  19. data/lib/sidekiq_unique_jobs/lua/shared/_find_digest_in_process_set.lua +9 -2
  20. data/lib/sidekiq_unique_jobs/middleware.rb +0 -57
  21. data/lib/sidekiq_unique_jobs/on_conflict/replace.rb +9 -8
  22. data/lib/sidekiq_unique_jobs/on_conflict/reschedule.rb +1 -1
  23. data/lib/sidekiq_unique_jobs/orphans/lua_reaper.rb +1 -1
  24. data/lib/sidekiq_unique_jobs/orphans/reaper.rb +10 -0
  25. data/lib/sidekiq_unique_jobs/orphans/ruby_reaper.rb +9 -2
  26. data/lib/sidekiq_unique_jobs/redis/entity.rb +9 -3
  27. data/lib/sidekiq_unique_jobs/redis/sorted_set.rb +27 -0
  28. data/lib/sidekiq_unique_jobs/server.rb +48 -0
  29. data/lib/sidekiq_unique_jobs/sidekiq_unique_jobs.rb +1 -1
  30. data/lib/sidekiq_unique_jobs/sidekiq_worker_methods.rb +1 -1
  31. data/lib/sidekiq_unique_jobs/testing.rb +2 -1
  32. data/lib/sidekiq_unique_jobs/version.rb +1 -1
  33. data/lib/sidekiq_unique_jobs/web.rb +26 -9
  34. data/lib/sidekiq_unique_jobs/web/helpers.rb +24 -3
  35. data/lib/sidekiq_unique_jobs/web/views/changelogs.erb +54 -0
  36. data/lib/sidekiq_unique_jobs/web/views/locks.erb +1 -1
  37. metadata +25 -9
  38. data/lib/sidekiq_unique_jobs/profiler.rb +0 -55
@@ -48,10 +48,10 @@ module SidekiqUniqueJobs
48
48
  def exist?
49
49
  redis do |conn|
50
50
  value = conn.exists(key)
51
- return true if value.is_a?(TrueClass)
52
- return false if value.is_a?(FalseClass)
53
51
 
54
- value.positive?
52
+ return value if boolean?(value)
53
+
54
+ value.to_i.positive?
55
55
  end
56
56
  end
57
57
 
@@ -95,6 +95,12 @@ module SidekiqUniqueJobs
95
95
  def count
96
96
  0
97
97
  end
98
+
99
+ private
100
+
101
+ def boolean?(value)
102
+ [TrueClass, FalseClass].any? { |klazz| value.is_a?(klazz) }
103
+ end
98
104
  end
99
105
  end
100
106
  end
@@ -23,6 +23,23 @@ module SidekiqUniqueJobs
23
23
  entrys.each_with_object({}) { |pair, hash| hash[pair[0]] = pair[1] }
24
24
  end
25
25
 
26
+ #
27
+ # Adds a value to the sorted set
28
+ #
29
+ # @param [Array<Float, String>, String] the values to add
30
+ #
31
+ # @return [Boolean, Integer] <description>
32
+ #
33
+ def add(values)
34
+ redis do |conn|
35
+ if values.is_a?(Array)
36
+ conn.zadd(key, values)
37
+ else
38
+ conn.zadd(key, now_f, values)
39
+ end
40
+ end
41
+ end
42
+
26
43
  #
27
44
  # Return the zrak of the member
28
45
  #
@@ -45,6 +62,16 @@ module SidekiqUniqueJobs
45
62
  redis { |conn| conn.zscore(key, member) }
46
63
  end
47
64
 
65
+ #
66
+ # Clears the sorted set from all entries
67
+ #
68
+ #
69
+ # @return [Integer] number of entries removed
70
+ #
71
+ def clear
72
+ redis { |conn| conn.zremrangebyrank(key, 0, count) }
73
+ end
74
+
48
75
  #
49
76
  # Returns the count for this sorted set
50
77
  #
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqUniqueJobs
4
+ # The unique sidekiq middleware for the server processor
5
+ #
6
+ # @author Mikael Henriksson <mikael@mhenrixon.com>
7
+ class Server
8
+ DEATH_HANDLER ||= (lambda do |job, _ex|
9
+ return unless (digest = job["lock_digest"])
10
+
11
+ SidekiqUniqueJobs::Digests.new.delete_by_digest(digest)
12
+ end).freeze
13
+ #
14
+ # Configure the server middleware
15
+ #
16
+ #
17
+ # @return [Sidekiq] the sidekiq configuration
18
+ #
19
+ def self.configure(config)
20
+ config.on(:startup) { start }
21
+ config.on(:shutdown) { stop }
22
+
23
+ return unless config.respond_to?(:death_handlers)
24
+
25
+ config.death_handlers << death_handler
26
+ end
27
+
28
+ def self.start
29
+ SidekiqUniqueJobs::UpdateVersion.call
30
+ SidekiqUniqueJobs::UpgradeLocks.call
31
+ SidekiqUniqueJobs::Orphans::Manager.start
32
+ end
33
+
34
+ def self.stop
35
+ SidekiqUniqueJobs::Orphans::Manager.stop
36
+ end
37
+
38
+ #
39
+ # A death handler for dead jobs
40
+ #
41
+ #
42
+ # @return [lambda]
43
+ #
44
+ def self.death_handler
45
+ DEATH_HANDLER
46
+ end
47
+ end
48
+ end
@@ -168,7 +168,7 @@ module SidekiqUniqueJobs
168
168
  # @option options [Integer] :lock_timeout (default is 0)
169
169
  # @option options [Integer] :lock_ttl (default is 0)
170
170
  # @option options [true,false] :enabled (default is true)
171
- # @option options [String] :unique_prefix (default is 'uniquejobs')
171
+ # @option options [String] :lock_prefix (default is 'uniquejobs')
172
172
  # @option options [Logger] :logger (default is Sidekiq.logger)
173
173
  # @yield control to the caller when given block
174
174
  def configure(options = {})
@@ -50,7 +50,7 @@ module SidekiqUniqueJobs
50
50
  # @return [Sidekiq::Worker]
51
51
  def worker_class_constantize(klazz = @worker_class)
52
52
  return klazz.class if klazz.is_a?(Sidekiq::Worker) # sidekiq v6.x
53
- return klazz unless klazz.is_a?(String)
53
+ return klazz unless klazz.is_a?(String)
54
54
 
55
55
  Object.const_get(klazz)
56
56
  rescue NameError => ex
@@ -93,7 +93,8 @@ module Sidekiq
93
93
  #
94
94
  def clear_all
95
95
  super
96
- SidekiqUniqueJobs::Digests.new.del(pattern: "*", count: 1_000)
96
+
97
+ SidekiqUniqueJobs::Digests.new.delete_by_pattern("*", count: 10_000)
97
98
  end
98
99
  end
99
100
 
@@ -3,5 +3,5 @@
3
3
  module SidekiqUniqueJobs
4
4
  #
5
5
  # @return [String] the current SidekiqUniqueJobs version
6
- VERSION = "7.0.0.beta26"
6
+ VERSION = "7.0.1"
7
7
  end
@@ -1,11 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- begin
4
- require "sidekiq/web"
5
- rescue LoadError
6
- # client-only usage
7
- end
8
-
9
3
  require_relative "web/helpers"
10
4
 
11
5
  module SidekiqUniqueJobs
@@ -19,6 +13,23 @@ module SidekiqUniqueJobs
19
13
  include Web::Helpers
20
14
  end
21
15
 
16
+ app.get "/changelogs" do
17
+ @filter = params[:filter] || "*"
18
+ @filter = "*" if @filter == ""
19
+ @count = (params[:count] || 100).to_i
20
+ @current_cursor = params[:cursor]
21
+ @prev_cursor = params[:prev_cursor]
22
+ @pagination = { pattern: @filter, cursor: @current_cursor, page_size: @count }
23
+ @total_size, @next_cursor, @changelogs = changelog.page(**@pagination)
24
+
25
+ erb(unique_template(:changelogs))
26
+ end
27
+
28
+ app.get "/changelogs/delete_all" do
29
+ changelog.clear
30
+ redirect_to :changelogs
31
+ end
32
+
22
33
  app.get "/locks" do
23
34
  @filter = params[:filter] || "*"
24
35
  @filter = "*" if @filter == ""
@@ -59,8 +70,14 @@ module SidekiqUniqueJobs
59
70
  end
60
71
  end
61
72
 
62
- if defined?(Sidekiq::Web)
63
- Sidekiq::Web.register SidekiqUniqueJobs::Web
64
- Sidekiq::Web.tabs["Locks"] = "locks"
73
+ begin
74
+ require "delegate" unless defined?(DelegateClass)
75
+ require "sidekiq/web" unless defined?(Sidekiq::Web)
76
+
77
+ Sidekiq::Web.register(SidekiqUniqueJobs::Web)
78
+ Sidekiq::Web.tabs["Locks"] = "locks"
79
+ Sidekiq::Web.tabs["Changelogs"] = "changelogs"
65
80
  Sidekiq::Web.settings.locales << File.join(File.dirname(__FILE__), "locales")
81
+ rescue NameError, LoadError => ex
82
+ SidekiqUniqueJobs.logger.error(ex)
66
83
  end
@@ -10,12 +10,12 @@ module SidekiqUniqueJobs
10
10
  module Helpers
11
11
  #
12
12
  # @return [String] the path to gem specific views
13
- VIEW_PATH = File.expand_path("../web/views", __dir__)
13
+ VIEW_PATH = File.expand_path("../web/views", __dir__).freeze
14
14
  #
15
15
  # @return [Array<String>] safe params
16
16
  SAFE_CPARAMS = %w[cursor prev_cursor].freeze
17
17
 
18
- module_function
18
+ extend self
19
19
 
20
20
  #
21
21
  # Opens a template file contained within this gem
@@ -25,7 +25,18 @@ module SidekiqUniqueJobs
25
25
  # @return [String] the file contents of the template
26
26
  #
27
27
  def unique_template(name)
28
- File.open(File.join(VIEW_PATH, "#{name}.erb")).read
28
+ File.open(unique_filename(name)).read
29
+ end
30
+
31
+ #
32
+ # Construct template file name
33
+ #
34
+ # @param [Symbol] name the name of the template
35
+ #
36
+ # @return [String] the full name of the file
37
+ #
38
+ def unique_filename(name)
39
+ File.join(VIEW_PATH, "#{name}.erb")
29
40
  end
30
41
 
31
42
  #
@@ -38,6 +49,16 @@ module SidekiqUniqueJobs
38
49
  @digests ||= SidekiqUniqueJobs::Digests.new
39
50
  end
40
51
 
52
+ #
53
+ # The collection of changelog entries
54
+ #
55
+ #
56
+ # @return [SidekiqUniqueJobs::Digests] the sorted set with digests
57
+ #
58
+ def changelog
59
+ @changelog ||= SidekiqUniqueJobs::Changelog.new
60
+ end
61
+
41
62
  #
42
63
  # Creates url safe parameters
43
64
  #
@@ -0,0 +1,54 @@
1
+ <header class="row">
2
+ <div class="col-sm-5">
3
+ <h3>
4
+ <%= t('Changelog Entries') %>
5
+ </h3>
6
+ </div>
7
+ <form action="<%= root_path %>changelogs" class="form form-inline" method="get">
8
+ <%= csrf_tag %>
9
+ <input name="filter" class="form-control" type="text" value="<%= @filter %>" />
10
+ <button class="btn btn-default" type="submit">
11
+ <%= t('Filter') %>
12
+ </button>
13
+ </form>
14
+ <% if @changelogs.any? && @total_size > @count.to_i %>
15
+ <div class="col-sm-4">
16
+ <%= erb unique_template(:_paging), locals: { url: "#{root_path}changelogs" } %>
17
+ </div>
18
+ <% end %>
19
+ </header>
20
+ <% if @changelogs.any? %>
21
+ <div class="table_container">
22
+ <form action="<%= root_path %>changelogs/delete_all" method="get">
23
+ <input class="btn btn-danger btn-xs" type="submit" name="delete_all" value="<%= t('DeleteAll') %>" data-confirm="<%= t('AreYouSure') %>" />
24
+ </form>
25
+ <br/>
26
+ <table class="table table-striped table-bordered table-hover">
27
+ <thead>
28
+ <tr>
29
+ <th><%= t('Time') %></th>
30
+ <th><%= t('Digest') %></th>
31
+ <th><%= t('Script') %></th>
32
+ <th><%= t('JID') %></th>
33
+ <th><%= t('Prev JID') %></th>
34
+ <th><%= t('Message') %></th>
35
+ </tr>
36
+ </thead>
37
+ <tbody>
38
+ <% @changelogs.each do |changelog| %>
39
+ <tr>
40
+ <td><%= safe_relative_time(changelog["time"]) %></td>
41
+ <td><%= changelog["digest"] %></td>
42
+ <td><%= changelog["script"] %></td>
43
+ <td><%= changelog["job_id"] %></td>
44
+ <td><%= changelog["prev_jid"] %></td>
45
+ <td><%= changelog["message"] %></th>
46
+ </tr>
47
+ <% end %>
48
+ </tbody>
49
+ </table>
50
+ <form action="<%= root_path %>changelogs/delete_all" method="get">
51
+ <input class="btn btn-danger btn-xs" type="submit" name="delete_all" value="<%= t('DeleteAll') %>" data-confirm="<%= t('AreYouSure') %>" />
52
+ </form>
53
+ </div>
54
+ <% end %>
@@ -32,7 +32,7 @@
32
32
  <% @locks.each do |lock| %>
33
33
  <tr>
34
34
  <td>
35
- <form action="<%= root_path %>locks/<%= lock.key %>" method="get">
35
+ <form action="<%= root_path %>locks/<%= lock.key %>/delete" method="get">
36
36
  <%= csrf_tag %>
37
37
  <input name="lock" value="<%= h lock.key %>" type="hidden" />
38
38
  <input class="btn btn-danger btn-xs" type="submit" name="delete" value="<%= t('Delete') %>" data-confirm="<%= t('AreYouSure') %>" />
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.beta26
4
+ version: 7.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-28 00:00:00.000000000 Z
11
+ date: 2021-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: brpoplpush-redis_script
@@ -56,7 +56,7 @@ dependencies:
56
56
  requirements:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
- version: '4.0'
59
+ version: '5.0'
60
60
  - - "<"
61
61
  - !ruby/object:Gem::Version
62
62
  version: '7.0'
@@ -66,7 +66,7 @@ dependencies:
66
66
  requirements:
67
67
  - - ">="
68
68
  - !ruby/object:Gem::Version
69
- version: '4.0'
69
+ version: '5.0'
70
70
  - - "<"
71
71
  - !ruby/object:Gem::Version
72
72
  version: '7.0'
@@ -177,7 +177,6 @@ files:
177
177
  - lib/sidekiq_unique_jobs/orphans/observer.rb
178
178
  - lib/sidekiq_unique_jobs/orphans/reaper.rb
179
179
  - lib/sidekiq_unique_jobs/orphans/ruby_reaper.rb
180
- - lib/sidekiq_unique_jobs/profiler.rb
181
180
  - lib/sidekiq_unique_jobs/redis.rb
182
181
  - lib/sidekiq_unique_jobs/redis/entity.rb
183
182
  - lib/sidekiq_unique_jobs/redis/hash.rb
@@ -189,6 +188,7 @@ files:
189
188
  - lib/sidekiq_unique_jobs/rspec/matchers/have_valid_sidekiq_options.rb
190
189
  - lib/sidekiq_unique_jobs/script.rb
191
190
  - lib/sidekiq_unique_jobs/script/caller.rb
191
+ - lib/sidekiq_unique_jobs/server.rb
192
192
  - lib/sidekiq_unique_jobs/sidekiq_unique_ext.rb
193
193
  - lib/sidekiq_unique_jobs/sidekiq_unique_jobs.rb
194
194
  - lib/sidekiq_unique_jobs/sidekiq_worker_methods.rb
@@ -202,6 +202,7 @@ files:
202
202
  - lib/sidekiq_unique_jobs/web.rb
203
203
  - lib/sidekiq_unique_jobs/web/helpers.rb
204
204
  - lib/sidekiq_unique_jobs/web/views/_paging.erb
205
+ - lib/sidekiq_unique_jobs/web/views/changelogs.erb
205
206
  - lib/sidekiq_unique_jobs/web/views/lock.erb
206
207
  - lib/sidekiq_unique_jobs/web/views/locks.erb
207
208
  - lib/tasks/changelog.rake
@@ -215,7 +216,22 @@ metadata:
215
216
  source_code_uri: https://github.com/mhenrixon/sidekiq-unique-jobs
216
217
  changelog_uri: https://github.com/mhenrixon/sidekiq-unique-jobs/blob/master/CHANGELOG.md
217
218
  post_install_message: |
218
- This version deprecated the configuration options:
219
+ IMPORTANT!
220
+
221
+ Automatic configuration of the sidekiq middelware is no longer done.
222
+ Please see: https://github.com/mhenrixon/sidekiq-unique-jobs/blob/master/README.md#add-the-middleware
223
+
224
+ This version deprecated the following sidekiq_options
225
+
226
+ - sidekiq_options lock_args: :method_name
227
+
228
+ It is now configured with:
229
+
230
+ - sidekiq_options lock_args_method: :method_name
231
+
232
+ This is also true for `Sidekiq.default_worker_options`
233
+
234
+ We also deprecated the global configuration options:
219
235
  - default_lock_ttl
220
236
  - default_lock_ttl=
221
237
  - default_lock_timeout
@@ -236,11 +252,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
236
252
  version: 2.5.0
237
253
  required_rubygems_version: !ruby/object:Gem::Requirement
238
254
  requirements:
239
- - - ">"
255
+ - - ">="
240
256
  - !ruby/object:Gem::Version
241
- version: 1.3.1
257
+ version: '0'
242
258
  requirements: []
243
- rubygems_version: 3.1.2
259
+ rubygems_version: 3.2.4
244
260
  signing_key:
245
261
  specification_version: 4
246
262
  summary: Sidekiq middleware that prevents duplicates jobs
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SidekiqUniqueJobs
4
- #
5
- # Class MethodProfiler provides method level profiling
6
- #
7
- # @author Mikael Henriksson <mikael@mhenrixon.com>
8
- #
9
- class Profiler
10
- def self.patch(klass, methods, name) # rubocop:disable Metrics/MethodLength
11
- patches = methods.map do |method_name|
12
- <<~RUBY
13
- unless defined?(#{method_name}__mp_unpatched)
14
- alias_method :#{method_name}__mp_unpatched, :#{method_name}
15
- def #{method_name}(*args, &blk)
16
- unless prof = Thread.current[:_method_profiler]
17
- return #{method_name}__mp_unpatched(*args, &blk)
18
- end
19
- begin
20
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
21
- #{method_name}__mp_unpatched(*args, &blk)
22
- ensure
23
- data = (prof[:#{name}] ||= {duration: 0.0, calls: 0})
24
- data[:duration] += Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
25
- data[:calls] += 1
26
- end
27
- end
28
- end
29
- RUBY
30
- end.join("\n")
31
-
32
- klass.class_eval patches
33
- end
34
-
35
- def self.start
36
- Thread.current[:_method_profiler] = {
37
- __start: current_timestamp,
38
- }
39
- end
40
-
41
- def self.stop
42
- finish = current_timestamp
43
- return unless (data = Thread.current[:_method_profiler])
44
-
45
- Thread.current[:_method_profiler] = nil
46
- start = data.delete(:__start)
47
- data[:total_duration] = finish - start
48
- data
49
- end
50
-
51
- def self.current_timestamp
52
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
53
- end
54
- end
55
- end