sidekiq-unique-jobs 7.0.0.beta29 → 7.0.4

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +138 -36
  3. data/README.md +169 -37
  4. data/lib/sidekiq-unique-jobs.rb +0 -2
  5. data/lib/sidekiq_unique_jobs.rb +4 -1
  6. data/lib/sidekiq_unique_jobs/batch_delete.rb +1 -1
  7. data/lib/sidekiq_unique_jobs/changelog.rb +11 -4
  8. data/lib/sidekiq_unique_jobs/constants.rb +1 -0
  9. data/lib/sidekiq_unique_jobs/digests.rb +1 -1
  10. data/lib/sidekiq_unique_jobs/json.rb +7 -1
  11. data/lib/sidekiq_unique_jobs/lock.rb +31 -1
  12. data/lib/sidekiq_unique_jobs/lock_config.rb +2 -0
  13. data/lib/sidekiq_unique_jobs/lua/lock.lua +10 -11
  14. data/lib/sidekiq_unique_jobs/lua/reap_orphans.lua +8 -7
  15. data/lib/sidekiq_unique_jobs/lua/shared/_find_digest_in_process_set.lua +9 -2
  16. data/lib/sidekiq_unique_jobs/middleware.rb +0 -57
  17. data/lib/sidekiq_unique_jobs/on_conflict/replace.rb +9 -8
  18. data/lib/sidekiq_unique_jobs/on_conflict/reschedule.rb +1 -1
  19. data/lib/sidekiq_unique_jobs/orphans/lua_reaper.rb +1 -1
  20. data/lib/sidekiq_unique_jobs/orphans/manager.rb +13 -3
  21. data/lib/sidekiq_unique_jobs/orphans/reaper.rb +10 -0
  22. data/lib/sidekiq_unique_jobs/orphans/ruby_reaper.rb +14 -5
  23. data/lib/sidekiq_unique_jobs/redis/entity.rb +9 -3
  24. data/lib/sidekiq_unique_jobs/redis/sorted_set.rb +27 -0
  25. data/lib/sidekiq_unique_jobs/server.rb +48 -0
  26. data/lib/sidekiq_unique_jobs/timer_task.rb +78 -0
  27. data/lib/sidekiq_unique_jobs/version.rb +1 -1
  28. data/lib/sidekiq_unique_jobs/web.rb +28 -4
  29. data/lib/sidekiq_unique_jobs/web/helpers.rb +24 -3
  30. data/lib/sidekiq_unique_jobs/web/views/changelogs.erb +54 -0
  31. data/lib/sidekiq_unique_jobs/web/views/locks.erb +1 -1
  32. metadata +13 -8
  33. data/lib/sidekiq_unique_jobs/profiler.rb +0 -55
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqUniqueJobs
4
+ # @see [Concurrent::TimerTask] https://www.rubydoc.info/gems/concurrent-ruby/Concurrent/TimerTask
5
+ #
6
+ class TimerTask < ::Concurrent::TimerTask
7
+ private
8
+
9
+ def ns_initialize(opts, &task)
10
+ set_deref_options(opts)
11
+
12
+ self.execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL
13
+ self.timeout_interval = opts[:timeout] || opts[:timeout_interval] || TIMEOUT_INTERVAL
14
+ @run_now = opts[:now] || opts[:run_now]
15
+ @executor = Concurrent::RubySingleThreadExecutor.new
16
+ @running = Concurrent::AtomicBoolean.new(false)
17
+ @task = task
18
+ @value = nil
19
+
20
+ self.observers = Concurrent::Collection::CopyOnNotifyObserverSet.new
21
+ end
22
+
23
+ def schedule_next_task(interval = execution_interval)
24
+ exec_task = ->(completion) { execute_task(completion) }
25
+ Concurrent::ScheduledTask.execute(interval, args: [Concurrent::Event.new], &exec_task)
26
+ nil
27
+ end
28
+
29
+ # @!visibility private
30
+ def execute_task(completion) # rubocop:disable Metrics/MethodLength
31
+ return nil unless @running.true?
32
+
33
+ timeout_task = -> { timeout_task(completion) }
34
+
35
+ Concurrent::ScheduledTask.execute(
36
+ timeout_interval,
37
+ args: [completion],
38
+ &timeout_task
39
+ )
40
+ @thread_completed = Concurrent::Event.new
41
+
42
+ @value = @reason = nil
43
+ @executor.post do
44
+ @value = @task.call(self)
45
+ rescue Exception => ex # rubocop:disable Lint/RescueException
46
+ @reason = ex
47
+ ensure
48
+ @thread_completed.set
49
+ end
50
+
51
+ @thread_completed.wait
52
+
53
+ if completion.try?
54
+ schedule_next_task
55
+ time = Time.now
56
+ observers.notify_observers do
57
+ [time, value, @reason]
58
+ end
59
+ end
60
+ nil
61
+ end
62
+
63
+ # @!visibility private
64
+ def timeout_task(completion)
65
+ return unless @running.true?
66
+ return unless completion.try?
67
+
68
+ @executor.kill
69
+ @executor.wait_for_termination
70
+ @executor = Concurrent::RubySingleThreadExecutor.new
71
+
72
+ @thread_completed.set
73
+
74
+ schedule_next_task
75
+ observers.notify_observers(Time.now, nil, Concurrent::TimeoutError.new)
76
+ end
77
+ end
78
+ end
@@ -3,5 +3,5 @@
3
3
  module SidekiqUniqueJobs
4
4
  #
5
5
  # @return [String] the current SidekiqUniqueJobs version
6
- VERSION = "7.0.0.beta29"
6
+ VERSION = "7.0.4"
7
7
  end
@@ -13,6 +13,23 @@ module SidekiqUniqueJobs
13
13
  include Web::Helpers
14
14
  end
15
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
+
16
33
  app.get "/locks" do
17
34
  @filter = params[:filter] || "*"
18
35
  @filter = "*" if @filter == ""
@@ -53,7 +70,14 @@ module SidekiqUniqueJobs
53
70
  end
54
71
  end
55
72
 
56
- require "sidekiq/web" unless defined?(Sidekiq::Web)
57
- Sidekiq::Web.register(SidekiqUniqueJobs::Web)
58
- Sidekiq::Web.tabs["Locks"] = "locks"
59
- Sidekiq::Web.settings.locales << File.join(File.dirname(__FILE__), "locales")
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"
80
+ Sidekiq::Web.settings.locales << File.join(File.dirname(__FILE__), "locales")
81
+ rescue NameError, LoadError => ex
82
+ SidekiqUniqueJobs.logger.error(ex)
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.beta29
4
+ version: 7.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-16 00:00:00.000000000 Z
11
+ date: 2021-02-17 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,10 +188,12 @@ 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
195
195
  - lib/sidekiq_unique_jobs/testing.rb
196
+ - lib/sidekiq_unique_jobs/timer_task.rb
196
197
  - lib/sidekiq_unique_jobs/timing.rb
197
198
  - lib/sidekiq_unique_jobs/unlockable.rb
198
199
  - lib/sidekiq_unique_jobs/update_version.rb
@@ -202,6 +203,7 @@ files:
202
203
  - lib/sidekiq_unique_jobs/web.rb
203
204
  - lib/sidekiq_unique_jobs/web/helpers.rb
204
205
  - lib/sidekiq_unique_jobs/web/views/_paging.erb
206
+ - lib/sidekiq_unique_jobs/web/views/changelogs.erb
205
207
  - lib/sidekiq_unique_jobs/web/views/lock.erb
206
208
  - lib/sidekiq_unique_jobs/web/views/locks.erb
207
209
  - lib/tasks/changelog.rake
@@ -217,6 +219,9 @@ metadata:
217
219
  post_install_message: |
218
220
  IMPORTANT!
219
221
 
222
+ Automatic configuration of the sidekiq middelware is no longer done.
223
+ Please see: https://github.com/mhenrixon/sidekiq-unique-jobs/blob/master/README.md#add-the-middleware
224
+
220
225
  This version deprecated the following sidekiq_options
221
226
 
222
227
  - sidekiq_options lock_args: :method_name
@@ -248,11 +253,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
248
253
  version: 2.5.0
249
254
  required_rubygems_version: !ruby/object:Gem::Requirement
250
255
  requirements:
251
- - - ">"
256
+ - - ">="
252
257
  - !ruby/object:Gem::Version
253
- version: 1.3.1
258
+ version: '0'
254
259
  requirements: []
255
- rubygems_version: 3.2.4
260
+ rubygems_version: 3.2.6
256
261
  signing_key:
257
262
  specification_version: 4
258
263
  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