sidekiq-unique-jobs 5.0.2 → 5.0.11

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 (207) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +8 -0
  3. data/.rubocop.yml +20 -13
  4. data/.simplecov +1 -1
  5. data/.travis.yml +30 -20
  6. data/Appraisals +10 -2
  7. data/CHANGELOG.md +33 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +13 -12
  10. data/LICENSE.txt +21 -0
  11. data/README.md +48 -4
  12. data/Rakefile +0 -1
  13. data/bin/bench +1 -1
  14. data/lib/sidekiq/simulator.rb +4 -10
  15. data/lib/sidekiq-unique-jobs.rb +16 -6
  16. data/lib/sidekiq_unique_jobs/cli.rb +14 -12
  17. data/lib/sidekiq_unique_jobs/client/middleware.rb +2 -0
  18. data/lib/sidekiq_unique_jobs/config.rb +2 -0
  19. data/lib/sidekiq_unique_jobs/constants.rb +20 -17
  20. data/lib/sidekiq_unique_jobs/core_ext.rb +18 -0
  21. data/lib/sidekiq_unique_jobs/lock/until_and_while_executing.rb +2 -0
  22. data/lib/sidekiq_unique_jobs/lock/until_executed.rb +4 -5
  23. data/lib/sidekiq_unique_jobs/lock/until_executing.rb +2 -0
  24. data/lib/sidekiq_unique_jobs/lock/until_timeout.rb +2 -0
  25. data/lib/sidekiq_unique_jobs/lock/while_executing.rb +10 -9
  26. data/lib/sidekiq_unique_jobs/lock.rb +2 -0
  27. data/lib/sidekiq_unique_jobs/middleware.rb +2 -0
  28. data/lib/sidekiq_unique_jobs/normalizer.rb +3 -1
  29. data/lib/sidekiq_unique_jobs/options_with_fallback.rb +22 -3
  30. data/lib/sidekiq_unique_jobs/run_lock_failed.rb +2 -0
  31. data/lib/sidekiq_unique_jobs/script_mock.rb +16 -15
  32. data/lib/sidekiq_unique_jobs/scripts/acquire_lock.rb +2 -0
  33. data/lib/sidekiq_unique_jobs/scripts/release_lock.rb +2 -0
  34. data/lib/sidekiq_unique_jobs/scripts.rb +17 -12
  35. data/lib/sidekiq_unique_jobs/server/middleware.rb +2 -0
  36. data/lib/sidekiq_unique_jobs/sidekiq_unique_ext.rb +4 -14
  37. data/lib/sidekiq_unique_jobs/testing/sidekiq_overrides.rb +2 -0
  38. data/lib/sidekiq_unique_jobs/testing.rb +4 -2
  39. data/lib/sidekiq_unique_jobs/timeout_calculator.rb +2 -0
  40. data/lib/sidekiq_unique_jobs/unique_args.rb +27 -14
  41. data/lib/sidekiq_unique_jobs/unlockable.rb +6 -6
  42. data/lib/sidekiq_unique_jobs/util.rb +34 -14
  43. data/lib/sidekiq_unique_jobs/version.rb +3 -1
  44. data/redis/acquire_lock.lua +1 -1
  45. data/sidekiq-unique-jobs.gemspec +27 -23
  46. metadata +44 -212
  47. data/LICENSE +0 -13
  48. data/circle.yml +0 -41
  49. data/gemfiles/sidekiq_4.0.gemfile +0 -22
  50. data/gemfiles/sidekiq_4.1.gemfile +0 -22
  51. data/gemfiles/sidekiq_4.2.gemfile +0 -22
  52. data/gemfiles/sidekiq_5.0.gemfile +0 -22
  53. data/gemfiles/sidekiq_develop.gemfile +0 -22
  54. data/rails_example/.env +0 -12
  55. data/rails_example/.env.test +0 -12
  56. data/rails_example/.gitignore +0 -13
  57. data/rails_example/.rspec +0 -2
  58. data/rails_example/Gemfile +0 -38
  59. data/rails_example/Procfile +0 -2
  60. data/rails_example/README.rdoc +0 -28
  61. data/rails_example/Rakefile +0 -6
  62. data/rails_example/app/assets/images/.keep +0 -0
  63. data/rails_example/app/assets/javascripts/application.js +0 -16
  64. data/rails_example/app/assets/stylesheets/application.css +0 -15
  65. data/rails_example/app/channels/appearance_channel.rb +0 -17
  66. data/rails_example/app/channels/application_cable/channel.rb +0 -4
  67. data/rails_example/app/channels/application_cable/connection.rb +0 -9
  68. data/rails_example/app/channels/post_channel.rb +0 -5
  69. data/rails_example/app/controllers/application_controller.rb +0 -5
  70. data/rails_example/app/controllers/concerns/.keep +0 -0
  71. data/rails_example/app/controllers/work_controller.rb +0 -39
  72. data/rails_example/app/helpers/application_helper.rb +0 -2
  73. data/rails_example/app/mailers/.keep +0 -0
  74. data/rails_example/app/models/.keep +0 -0
  75. data/rails_example/app/models/application_record.rb +0 -3
  76. data/rails_example/app/models/concerns/.keep +0 -0
  77. data/rails_example/app/models/guest.rb +0 -18
  78. data/rails_example/app/models/post.rb +0 -2
  79. data/rails_example/app/views/layouts/application.html.erb +0 -15
  80. data/rails_example/app/workers/simple_worker.rb +0 -15
  81. data/rails_example/app/workers/slow_until_executing_worker.rb +0 -15
  82. data/rails_example/app/workers/spawn_simple_worker.rb +0 -10
  83. data/rails_example/app/workers/while_executing_worker.rb +0 -12
  84. data/rails_example/app/workers/without_args_worker.rb +0 -16
  85. data/rails_example/app/workers/without_argument_worker.rb +0 -12
  86. data/rails_example/bin/bundle +0 -3
  87. data/rails_example/bin/check_or_setup_db +0 -56
  88. data/rails_example/bin/docker-setup +0 -20
  89. data/rails_example/bin/rails +0 -4
  90. data/rails_example/bin/rake +0 -4
  91. data/rails_example/bin/setup +0 -34
  92. data/rails_example/bin/update +0 -29
  93. data/rails_example/cable.ru +0 -6
  94. data/rails_example/common-services.yml +0 -52
  95. data/rails_example/config/application.rb +0 -27
  96. data/rails_example/config/boot.rb +0 -3
  97. data/rails_example/config/cable.yml +0 -9
  98. data/rails_example/config/database.docker.yml +0 -12
  99. data/rails_example/config/database.yml +0 -19
  100. data/rails_example/config/environment.rb +0 -5
  101. data/rails_example/config/environments/development.rb +0 -54
  102. data/rails_example/config/environments/production.rb +0 -86
  103. data/rails_example/config/environments/test.rb +0 -42
  104. data/rails_example/config/initializers/application_controller_renderer.rb +0 -6
  105. data/rails_example/config/initializers/assets.rb +0 -11
  106. data/rails_example/config/initializers/backtrace_silencers.rb +0 -9
  107. data/rails_example/config/initializers/cookies_serializer.rb +0 -5
  108. data/rails_example/config/initializers/filter_parameter_logging.rb +0 -4
  109. data/rails_example/config/initializers/inflections.rb +0 -16
  110. data/rails_example/config/initializers/mime_types.rb +0 -4
  111. data/rails_example/config/initializers/new_framework_defaults.rb +0 -23
  112. data/rails_example/config/initializers/session_store.rb +0 -3
  113. data/rails_example/config/initializers/sidekiq.rb +0 -13
  114. data/rails_example/config/initializers/wrap_parameters.rb +0 -14
  115. data/rails_example/config/locales/en.yml +0 -23
  116. data/rails_example/config/puma.rb +0 -38
  117. data/rails_example/config/routes.rb +0 -8
  118. data/rails_example/config/secrets.yml +0 -22
  119. data/rails_example/config/sidekiq.yml +0 -6
  120. data/rails_example/config/spring.rb +0 -6
  121. data/rails_example/config.ru +0 -4
  122. data/rails_example/db/migrate/20160724111322_create_posts.rb +0 -12
  123. data/rails_example/db/schema.rb +0 -25
  124. data/rails_example/db/seeds.rb +0 -7
  125. data/rails_example/dev-entrypoint.sh +0 -55
  126. data/rails_example/dev.env +0 -12
  127. data/rails_example/docker/rails.Dockerfile +0 -27
  128. data/rails_example/docker-compose.yml +0 -71
  129. data/rails_example/lib/assets/.keep +0 -0
  130. data/rails_example/lib/tasks/.keep +0 -0
  131. data/rails_example/log/.keep +0 -0
  132. data/rails_example/public/404.html +0 -67
  133. data/rails_example/public/422.html +0 -67
  134. data/rails_example/public/500.html +0 -66
  135. data/rails_example/public/favicon.ico +0 -0
  136. data/rails_example/public/robots.txt +0 -5
  137. data/rails_example/simple.ru +0 -12
  138. data/rails_example/spec/controllers/work_controller_mock_spec.rb +0 -91
  139. data/rails_example/spec/controllers/work_controller_spec.rb +0 -77
  140. data/rails_example/spec/factories/posts.rb +0 -8
  141. data/rails_example/spec/models/post_spec.rb +0 -4
  142. data/rails_example/spec/rails_helper.rb +0 -20
  143. data/rails_example/spec/spec_helper.rb +0 -20
  144. data/rails_example/spec/support/sidekiq_meta.rb +0 -12
  145. data/rails_example/spec/workers/simple_worker_spec.rb +0 -4
  146. data/rails_example/vendor/assets/javascripts/.keep +0 -0
  147. data/rails_example/vendor/assets/stylesheets/.keep +0 -0
  148. data/spec/jobs/another_unique_job.rb +0 -13
  149. data/spec/jobs/custom_queue_job.rb +0 -5
  150. data/spec/jobs/custom_queue_job_with_filter_method.rb +0 -7
  151. data/spec/jobs/custom_queue_job_with_filter_proc.rb +0 -10
  152. data/spec/jobs/expiring_job.rb +0 -4
  153. data/spec/jobs/inline_worker.rb +0 -8
  154. data/spec/jobs/just_a_worker.rb +0 -7
  155. data/spec/jobs/long_running_job.rb +0 -6
  156. data/spec/jobs/main_job.rb +0 -7
  157. data/spec/jobs/my_job.rb +0 -12
  158. data/spec/jobs/my_unique_job.rb +0 -11
  159. data/spec/jobs/my_unique_job_with_filter_method.rb +0 -17
  160. data/spec/jobs/my_unique_job_with_filter_proc.rb +0 -15
  161. data/spec/jobs/notify_worker.rb +0 -10
  162. data/spec/jobs/plain_class.rb +0 -3
  163. data/spec/jobs/simple_worker.rb +0 -11
  164. data/spec/jobs/spawn_simple_worker.rb +0 -8
  165. data/spec/jobs/test_class.rb +0 -3
  166. data/spec/jobs/unique_job_with_conditional_parameter.rb +0 -14
  167. data/spec/jobs/unique_job_with_filter_method.rb +0 -18
  168. data/spec/jobs/unique_job_with_nil_unique_args.rb +0 -16
  169. data/spec/jobs/unique_job_with_no_unique_args_method.rb +0 -12
  170. data/spec/jobs/unique_job_withthout_unique_args_parameter.rb +0 -14
  171. data/spec/jobs/unique_on_all_queues_job.rb +0 -13
  172. data/spec/jobs/until_and_while_executing_job.rb +0 -7
  173. data/spec/jobs/until_executed_job.rb +0 -17
  174. data/spec/jobs/until_executing_job.rb +0 -7
  175. data/spec/jobs/until_global_timeout_job.rb +0 -8
  176. data/spec/jobs/until_timeout_job.rb +0 -8
  177. data/spec/jobs/while_executing_job.rb +0 -12
  178. data/spec/jobs/without_argument_job.rb +0 -9
  179. data/spec/lib/sidekiq_unique_jobs/cli_spec.rb +0 -183
  180. data/spec/lib/sidekiq_unique_jobs/client/middleware_spec.rb +0 -316
  181. data/spec/lib/sidekiq_unique_jobs/config_spec.rb +0 -84
  182. data/spec/lib/sidekiq_unique_jobs/core_ext_spec.rb +0 -25
  183. data/spec/lib/sidekiq_unique_jobs/lock/until_and_while_executing_spec.rb +0 -42
  184. data/spec/lib/sidekiq_unique_jobs/lock/until_executed_spec.rb +0 -37
  185. data/spec/lib/sidekiq_unique_jobs/lock/until_timeout_spec.rb +0 -26
  186. data/spec/lib/sidekiq_unique_jobs/lock/while_executing_spec.rb +0 -86
  187. data/spec/lib/sidekiq_unique_jobs/normalizer_spec.rb +0 -21
  188. data/spec/lib/sidekiq_unique_jobs/options_with_fallback_spec.rb +0 -113
  189. data/spec/lib/sidekiq_unique_jobs/queue_lock_timeout_calculator_spec.rb +0 -47
  190. data/spec/lib/sidekiq_unique_jobs/run_lock_timeout_calculator_spec.rb +0 -36
  191. data/spec/lib/sidekiq_unique_jobs/script_mock_spec.rb +0 -88
  192. data/spec/lib/sidekiq_unique_jobs/scripts/acquire_lock_spec.rb +0 -50
  193. data/spec/lib/sidekiq_unique_jobs/scripts/release_lock_spec.rb +0 -41
  194. data/spec/lib/sidekiq_unique_jobs/scripts_spec.rb +0 -68
  195. data/spec/lib/sidekiq_unique_jobs/server/middleware_spec.rb +0 -78
  196. data/spec/lib/sidekiq_unique_jobs/sidekiq_testing_enabled_spec.rb +0 -164
  197. data/spec/lib/sidekiq_unique_jobs/sidekiq_unique_ext_spec.rb +0 -75
  198. data/spec/lib/sidekiq_unique_jobs/sidekiq_unique_jobs_spec.rb +0 -41
  199. data/spec/lib/sidekiq_unique_jobs/timeout_calculator_spec.rb +0 -70
  200. data/spec/lib/sidekiq_unique_jobs/unique_args_spec.rb +0 -257
  201. data/spec/lib/sidekiq_unique_jobs/unlockable_spec.rb +0 -37
  202. data/spec/lib/sidekiq_unique_jobs/util_spec.rb +0 -81
  203. data/spec/spec_helper.rb +0 -92
  204. data/spec/support/matchers/redis_matchers.rb +0 -29
  205. data/spec/support/ruby_meta.rb +0 -17
  206. data/spec/support/sidekiq_meta.rb +0 -30
  207. data/spec/support/unique_macros.rb +0 -71
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SidekiqUniqueJobs
2
4
  module Lock
3
5
  class UntilExecuted
4
- OK ||= 'OK'.freeze
6
+ OK ||= 'OK'
5
7
 
6
8
  include SidekiqUniqueJobs::Unlockable
7
9
 
@@ -36,9 +38,7 @@ module SidekiqUniqueJobs
36
38
  end
37
39
 
38
40
  def lock(scope)
39
- if scope.to_sym != :client
40
- raise ArgumentError, "#{scope} middleware can't #{__method__} #{unique_key}"
41
- end
41
+ raise ArgumentError, "#{scope} middleware can't #{__method__} #{unique_key}" if scope.to_sym != :client
42
42
 
43
43
  Scripts::AcquireLock.execute(
44
44
  redis_pool,
@@ -47,7 +47,6 @@ module SidekiqUniqueJobs
47
47
  max_lock_time,
48
48
  )
49
49
  end
50
- # rubocop:enable MethodLength
51
50
 
52
51
  def unique_key
53
52
  @unique_key ||= UniqueArgs.digest(item)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SidekiqUniqueJobs
2
4
  module Lock
3
5
  class UntilExecuting < UntilExecuted
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SidekiqUniqueJobs
2
4
  module Lock
3
5
  class UntilTimeout < UntilExecuted
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SidekiqUniqueJobs
2
4
  module Lock
3
5
  class WhileExecuting
@@ -7,22 +9,21 @@ module SidekiqUniqueJobs
7
9
 
8
10
  def initialize(item, redis_pool = nil)
9
11
  @item = item
10
- @mutex = Mutex.new
11
12
  @redis_pool = redis_pool
12
13
  @unique_digest = "#{create_digest}:run"
14
+ @mutex = Mutex.new
13
15
  end
14
16
 
15
17
  def synchronize
16
- @mutex.lock
17
- sleep 0.1 until locked?
18
-
19
- yield
18
+ @mutex.synchronize do
19
+ sleep 0.1 until locked?
20
+ yield
21
+ end
20
22
  rescue Sidekiq::Shutdown
21
23
  logger.fatal { "the unique_key: #{@unique_digest} needs to be unlocked manually" }
22
24
  raise
23
25
  ensure
24
- SidekiqUniqueJobs.connection(@redis_pool) { |c| c.del @unique_digest }
25
- @mutex.unlock
26
+ SidekiqUniqueJobs.connection(@redis_pool) { |conn| conn.del @unique_digest }
26
27
  end
27
28
 
28
29
  def locked?
@@ -42,8 +43,8 @@ module SidekiqUniqueJobs
42
43
  end
43
44
 
44
45
  def create_digest
45
- @unique_digest ||= @item[UNIQUE_DIGEST_KEY]
46
- @unique_digest ||= SidekiqUniqueJobs::UniqueArgs.digest(@item)
46
+ @create_digest ||= @item[UNIQUE_DIGEST_KEY]
47
+ @create_digest ||= SidekiqUniqueJobs::UniqueArgs.digest(@item)
47
48
  end
48
49
  end
49
50
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sidekiq_unique_jobs/lock/until_executed'
2
4
  require 'sidekiq_unique_jobs/lock/until_executing'
3
5
  require 'sidekiq_unique_jobs/lock/while_executing'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sidekiq'
2
4
 
3
5
  module SidekiqUniqueJobs
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
 
3
5
  module SidekiqUniqueJobs
4
6
  module Normalizer
5
7
  def self.jsonify(args)
6
- JSON.parse(args.to_json)
8
+ Sidekiq.load_json(Sidekiq.dump_json(args))
7
9
  end
8
10
  end
9
11
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SidekiqUniqueJobs
2
4
  module OptionsWithFallback
3
5
  def self.included(base)
4
- base.class_attribute :lock_cache unless base.respond_to?(:lock_cache)
5
- base.lock_cache ||= {}
6
+ base.send(:extend, SidekiqUniqueJobs::OptionsWithFallback::ClassMethods)
6
7
  end
7
8
 
8
9
  def unique_enabled?
@@ -22,7 +23,7 @@ module SidekiqUniqueJobs
22
23
  end
23
24
 
24
25
  def lock_class
25
- lock_cache[unique_lock.to_sym] ||= "SidekiqUniqueJobs::Lock::#{unique_lock.to_s.classify}".constantize
26
+ lock_cache[unique_lock.to_sym] ||= Object.const_get("SidekiqUniqueJobs::Lock::#{unique_lock.to_s.classify}")
26
27
  end
27
28
 
28
29
  def unique_lock
@@ -45,11 +46,29 @@ module SidekiqUniqueJobs
45
46
  hash[key]
46
47
  end
47
48
 
49
+ def lock_cache
50
+ self.class.lock_cache
51
+ end
52
+
53
+ def lock_cache=(obj)
54
+ self.class.lock_cache = obj
55
+ end
56
+
48
57
  def options
49
58
  @options ||= worker_class.get_sidekiq_options if worker_class.respond_to?(:get_sidekiq_options)
50
59
  @options ||= Sidekiq.default_worker_options
51
60
  @options ||= {}
52
61
  @options &&= @options.stringify_keys
53
62
  end
63
+
64
+ module ClassMethods
65
+ def lock_cache
66
+ @lock_cache ||= {}
67
+ end
68
+
69
+ def lock_cache=(obj)
70
+ @lock_cache = obj
71
+ end
72
+ end
54
73
  end
55
74
  end
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  SidekiqUniqueJobs::RunLockFailed = Class.new(StandardError)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pathname'
2
4
  require 'digest/sha1'
3
5
 
@@ -13,53 +15,52 @@ module SidekiqUniqueJobs
13
15
  end
14
16
 
15
17
  def acquire_lock(redis_pool, options = {})
16
- connection(redis_pool) do |redis|
18
+ connection(redis_pool) do |conn|
17
19
  unique_key = options[:keys][0]
18
20
  job_id = options[:argv][0]
19
21
  expires = options[:argv][1].to_i
20
- stored_jid = redis.get(unique_key)
22
+ stored_jid = conn.get(unique_key)
21
23
 
22
24
  return (stored_jid == job_id) ? 1 : 0 if stored_jid
25
+ return 0 unless conn.set(unique_key, job_id, nx: true, ex: expires)
26
+
27
+ conn.hsetnx(SidekiqUniqueJobs::HASH_KEY, job_id, unique_key)
23
28
 
24
- return 0 unless redis.set(unique_key, job_id, nx: true, ex: expires)
25
- redis.hsetnx(SidekiqUniqueJobs::HASH_KEY, job_id, unique_key)
26
29
  return 1
27
30
  end
28
31
  end
29
32
 
30
33
  def release_lock(redis_pool, options = {})
31
- connection(redis_pool) do |redis|
34
+ connection(redis_pool) do |conn|
32
35
  unique_key = options[:keys][0]
33
36
  job_id = options[:argv][0]
34
- stored_jid = redis.get(unique_key)
37
+ stored_jid = conn.get(unique_key)
35
38
 
36
39
  return -1 unless stored_jid
37
40
  return 0 unless stored_jid == job_id || stored_jid == '2'
38
41
 
39
- redis.del(unique_key)
40
- redis.hdel(SidekiqUniqueJobs::HASH_KEY, job_id)
42
+ conn.del(unique_key)
43
+ conn.hdel(SidekiqUniqueJobs::HASH_KEY, job_id)
44
+
41
45
  return 1
42
46
  end
43
47
  end
44
48
 
45
49
  def synchronize(redis_pool, options = {})
46
- connection(redis_pool) do |redis|
50
+ connection(redis_pool) do |conn|
47
51
  unique_key = options[:keys][0]
48
52
  time = options[:argv][0].to_i
49
53
  expires = options[:argv][1].to_f
50
54
 
51
- return 1 if redis.set(unique_key, time + expires, nx: true, ex: expires)
55
+ return 1 if conn.set(unique_key, time + expires, nx: true, ex: expires)
52
56
 
53
- stored_time = redis.get(unique_key)
57
+ stored_time = conn.get(unique_key)
54
58
  if stored_time && stored_time < time
55
- if redis.set(unique_key, time + expires, xx: true, ex: expires)
56
- return 1
57
- end
59
+ return 1 if conn.set(unique_key, time + expires, xx: true, ex: expires)
58
60
  end
59
61
 
60
62
  return 0
61
63
  end
62
64
  end
63
65
  end
64
- # rubocop:enable MethodLength
65
66
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SidekiqUniqueJobs
2
4
  module Scripts
3
5
  class AcquireLock
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SidekiqUniqueJobs
2
4
  module Scripts
3
5
  class ReleaseLock
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pathname'
2
4
  require 'digest/sha1'
3
5
  require 'concurrent/map'
@@ -22,22 +24,25 @@ module SidekiqUniqueJobs
22
24
  extend SingleForwardable
23
25
  def_delegators :SidekiqUniqueJobs, :connection, :logger
24
26
 
25
- def call(file_name, redis_pool, options = {}) # rubocop:disable MethodLength
26
- connection(redis_pool) do |redis|
27
- if SCRIPT_SHAS[file_name].nil?
28
- SCRIPT_SHAS[file_name] = redis.script(:load, script_source(file_name))
29
- end
30
- redis.evalsha(SCRIPT_SHAS[file_name], options)
31
- end
27
+ def call(file_name, redis_pool, options = {})
28
+ internal_call(file_name, redis_pool, options)
32
29
  rescue Redis::CommandError => ex
33
- if ex.message == 'NOSCRIPT No matching script. Please use EVAL.'
30
+ handle_error(ex, file_name, redis_pool, options)
31
+ end
32
+
33
+ def internal_call(file_name, redis_pool, options = {})
34
+ connection(redis_pool) do |conn|
35
+ SCRIPT_SHAS[file_name] = conn.script(:load, script_source(file_name)) if SCRIPT_SHAS[file_name].nil?
36
+ conn.evalsha(SCRIPT_SHAS[file_name], options)
37
+ end
38
+ end
39
+
40
+ def handle_error(exception, file_name, redis_pool, options = {})
41
+ if exception.message == 'NOSCRIPT No matching script. Please use EVAL.' # rubocop:disable Style/GuardClause
34
42
  SCRIPT_SHAS.delete(file_name)
35
43
  call(file_name, redis_pool, options)
36
- raise
37
44
  else
38
- raise ScriptError,
39
- "#{file_name}.lua\n\n#{ex.message}\n\n#{script_source(file_name)}" \
40
- "#{ex.backtrace.join("\n")}"
45
+ raise ScriptError, "Problem compiling #{file_name}. Invalid LUA syntax?"
41
46
  end
42
47
  end
43
48
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SidekiqUniqueJobs
2
4
  module Server
3
5
  class Middleware
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sidekiq/api'
2
4
 
3
5
  module Sidekiq
@@ -5,7 +7,6 @@ module Sidekiq
5
7
  module UniqueExtension
6
8
  def self.included(base)
7
9
  base.class_eval do
8
- include SidekiqUniqueJobs::Unlockable
9
10
  alias_method :delete_orig, :delete
10
11
  alias_method :delete, :delete_ext
11
12
  alias_method :remove_job_orig, :remove_job
@@ -34,7 +35,6 @@ module Sidekiq
34
35
  module UniqueExtension
35
36
  def self.included(base)
36
37
  base.class_eval do
37
- include SidekiqUniqueJobs::Unlockable
38
38
  alias_method :delete_orig, :delete
39
39
  alias_method :delete, :delete_ext
40
40
  end
@@ -43,13 +43,6 @@ module Sidekiq
43
43
  def delete_ext
44
44
  SidekiqUniqueJobs::Unlockable.unlock(item) if delete_orig
45
45
  end
46
-
47
- def remove_job_ext
48
- remove_job_orig do |message|
49
- SidekiqUniqueJobs::Unlockable.unlock(Sidekiq.load_json(message))
50
- yield message
51
- end
52
- end
53
46
  end
54
47
  include UniqueExtension
55
48
  end
@@ -58,15 +51,14 @@ module Sidekiq
58
51
  module UniqueExtension
59
52
  def self.included(base)
60
53
  base.class_eval do
61
- include SidekiqUniqueJobs::Unlockable
62
54
  alias_method :delete_orig, :delete
63
55
  alias_method :delete, :delete_ext
64
56
  end
65
57
  end
66
58
 
67
59
  def delete_ext
68
- SidekiqUniqueJobs::Unlockable.unlock(item)
69
60
  delete_orig
61
+ SidekiqUniqueJobs::Unlockable.unlock(item)
70
62
  end
71
63
  end
72
64
 
@@ -77,7 +69,6 @@ module Sidekiq
77
69
  module UniqueExtension
78
70
  def self.included(base)
79
71
  base.class_eval do
80
- include SidekiqUniqueJobs::Unlockable
81
72
  alias_method :clear_orig, :clear
82
73
  alias_method :clear, :clear_ext
83
74
  end
@@ -96,7 +87,6 @@ module Sidekiq
96
87
  module UniqueExtension
97
88
  def self.included(base)
98
89
  base.class_eval do
99
- include SidekiqUniqueJobs::Unlockable
100
90
  if base.method_defined?(:clear)
101
91
  alias_method :clear_orig, :clear
102
92
  alias_method :clear, :clear_ext
@@ -115,7 +105,7 @@ module Sidekiq
115
105
  end
116
106
 
117
107
  def delete_by_value_ext(name, value)
118
- SidekiqUniqueJobs::Unlockable.unlock(JSON.parse(value)) if delete_by_value_orig(name, value)
108
+ SidekiqUniqueJobs::Unlockable.unlock(Sidekiq.load_json(value)) if delete_by_value_orig(name, value)
119
109
  end
120
110
  end
121
111
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sidekiq/testing'
2
4
 
3
5
  module Sidekiq
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sidekiq_unique_jobs/testing/sidekiq_overrides'
2
4
  require 'sidekiq_unique_jobs/script_mock'
3
5
 
@@ -44,8 +46,8 @@ module SidekiqUniqueJobs
44
46
  worker_class = SidekiqUniqueJobs.worker_class_constantize(worker_class)
45
47
 
46
48
  if Sidekiq::Testing.inline?
47
- _server.call(worker_class.new, item, queue, redis_pool) do
48
- call_real(worker_class, item, queue, redis_pool) do
49
+ call_real(worker_class, item, queue, redis_pool) do
50
+ _server.call(worker_class.new, item, queue, redis_pool) do
49
51
  yield
50
52
  end
51
53
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SidekiqUniqueJobs
2
4
  class TimeoutCalculator
3
5
  def self.for_item(item)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'digest'
2
4
  require 'sidekiq_unique_jobs/normalizer'
3
5
 
@@ -5,9 +7,8 @@ module SidekiqUniqueJobs
5
7
  # This class exists to be testable and the entire api should be considered private
6
8
  # rubocop:disable ClassLength
7
9
  class UniqueArgs
8
- CLASS_NAME = 'SidekiqUniqueJobs::UniqueArgs'.freeze
10
+ CLASS_NAME = 'SidekiqUniqueJobs::UniqueArgs'
9
11
  extend Forwardable
10
- include Normalizer
11
12
 
12
13
  def_delegators :SidekiqUniqueJobs, :config, :worker_class_constantize
13
14
  def_delegators :Sidekiq, :logger
@@ -16,9 +17,9 @@ module SidekiqUniqueJobs
16
17
  new(item).unique_digest
17
18
  end
18
19
 
19
- def initialize(job)
20
+ def initialize(item)
20
21
  Sidekiq::Logging.with_context(CLASS_NAME) do
21
- @item = job
22
+ @item = item
22
23
  @worker_class ||= worker_class_constantize(@item[CLASS_KEY])
23
24
  @item[UNIQUE_PREFIX_KEY] ||= unique_prefix
24
25
  @item[UNIQUE_ARGS_KEY] = unique_args(@item[ARGS_KEY])
@@ -41,15 +42,16 @@ module SidekiqUniqueJobs
41
42
  end
42
43
 
43
44
  def digestable_hash
44
- hash = @item.slice(CLASS_KEY, QUEUE_KEY, UNIQUE_ARGS_KEY)
45
-
46
- if unique_on_all_queues?
47
- logger.debug do
48
- "#{__method__} uniqueness specified across all queues (deleting queue: #{@item[QUEUE_KEY]} from hash)"
45
+ @item.slice(CLASS_KEY, QUEUE_KEY, UNIQUE_ARGS_KEY).tap do |hash|
46
+ if unique_on_all_queues?
47
+ logger.debug { "#{__method__} deleting queue: #{@item[QUEUE_KEY]}" }
48
+ hash.delete(QUEUE_KEY)
49
+ end
50
+ if unique_across_workers?
51
+ logger.debug { "#{__method__} deleting class: #{@item[CLASS_KEY]}" }
52
+ hash.delete(CLASS_KEY)
49
53
  end
50
- hash.delete(QUEUE_KEY)
51
54
  end
52
- hash
53
55
  end
54
56
 
55
57
  def unique_args(args)
@@ -59,9 +61,13 @@ module SidekiqUniqueJobs
59
61
  logger.debug { "#{__method__} : unique arguments disabled" }
60
62
  args
61
63
  end
62
- rescue NameError
63
- # fallback to not filtering args when class can't be instantiated
64
- return args
64
+ rescue NameError => ex
65
+ logger.error "#{__method__}(#{args}) : failed with (#{ex.message})"
66
+ logger.error ex
67
+
68
+ raise if config.raise_unique_args_errors
69
+
70
+ args
65
71
  end
66
72
 
67
73
  def unique_on_all_queues?
@@ -69,6 +75,11 @@ module SidekiqUniqueJobs
69
75
  @item[UNIQUE_ON_ALL_QUEUES_KEY] || @worker_class.get_sidekiq_options[UNIQUE_ON_ALL_QUEUES_KEY]
70
76
  end
71
77
 
78
+ def unique_across_workers?
79
+ return unless sidekiq_worker_class?
80
+ @item[UNIQUE_ACROSS_WORKERS_KEY] || @worker_class.get_sidekiq_options[UNIQUE_ACROSS_WORKERS_KEY]
81
+ end
82
+
72
83
  def unique_args_enabled?
73
84
  return unless sidekiq_worker_class?
74
85
  unique_args_method # && !unique_args_method.is_a?(Boolean)
@@ -106,6 +117,7 @@ module SidekiqUniqueJobs
106
117
  logger.warn { "#{__method__} : unique_args_method is nil. Returning (#{args})" }
107
118
  return args
108
119
  end
120
+
109
121
  filter_args = unique_args_method.call(args)
110
122
  logger.debug { "#{__method__} : #{args} -> #{filter_args}" }
111
123
  filter_args
@@ -134,4 +146,5 @@ module SidekiqUniqueJobs
134
146
  @unique_args_method ||= Sidekiq.default_worker_options.stringify_keys[UNIQUE_ARGS_KEY]
135
147
  end
136
148
  end
149
+ # rubocop:enable ClassLength
137
150
  end
@@ -1,22 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SidekiqUniqueJobs
2
4
  module Unlockable
3
5
  module_function
4
6
 
5
7
  def unlock(item)
8
+ return unless item[UNIQUE_DIGEST_KEY]
6
9
  unlock_by_key(item[UNIQUE_DIGEST_KEY], item[JID_KEY])
7
10
  end
8
11
 
9
12
  def unlock_by_key(unique_key, jid, redis_pool = nil)
10
- return false unless Scripts::ReleaseLock.execute(redis_pool, unique_key, jid)
11
- after_unlock(jid)
12
- end
13
-
14
- def after_unlock(jid)
13
+ lock_released = Scripts::ReleaseLock.execute(redis_pool, unique_key, jid)
15
14
  ensure_job_id_removed(jid)
15
+ lock_released
16
16
  end
17
17
 
18
18
  def ensure_job_id_removed(jid)
19
- Sidekiq.redis { |redis| redis.hdel(SidekiqUniqueJobs::HASH_KEY, jid) }
19
+ Sidekiq.redis { |conn| conn.hdel(SidekiqUniqueJobs::HASH_KEY, jid) }
20
20
  end
21
21
 
22
22
  def logger
@@ -1,19 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SidekiqUniqueJobs
2
- module Util
3
- SCAN_PATTERN ||= '*'.freeze
4
- DEFAULT_COUNT ||= 1_000
5
- KEYS_METHOD ||= 'keys'.freeze
6
- SCAN_METHOD ||= 'scan'.freeze
4
+ module Util # rubocop:disable Metrics/ModuleLength
5
+ COUNT = 'COUNT'
6
+ DEFAULT_COUNT = 1_000
7
+ EXPIRE_BATCH_SIZE = 100
8
+ MATCH = 'MATCH'
9
+ KEYS_METHOD = 'keys'
10
+ SCAN_METHOD = 'scan'
11
+ SCAN_PATTERN = '*'
7
12
 
8
- module_function
13
+ extend self # rubocop:disable Style/ModuleFunction
9
14
 
10
15
  def keys(pattern = SCAN_PATTERN, count = DEFAULT_COUNT)
11
16
  send("keys_by_#{redis_keys_method}", pattern, count)
12
17
  end
13
18
 
14
19
  def unique_key(jid)
15
- connection do |redis|
16
- redis.hget(SidekiqUniqueJobs::HASH_KEY, jid)
20
+ connection do |conn|
21
+ conn.hget(SidekiqUniqueJobs::HASH_KEY, jid)
17
22
  end
18
23
  end
19
24
 
@@ -23,7 +28,7 @@ module SidekiqUniqueJobs
23
28
  keys, time = timed { keys(pattern, count) }
24
29
  logger.debug { "#{keys.size} matching keys found in #{time} sec." }
25
30
  keys = dry_run(keys)
26
- logger.debug { "#{keys.size} matching keys after postprocessing" }
31
+ logger.debug { "#{keys.size} matching keys after post-processing" }
27
32
  unless dry_run
28
33
  logger.debug { "deleting #{keys}..." }
29
34
  _, time = timed { batch_delete(keys) }
@@ -38,18 +43,33 @@ module SidekiqUniqueJobs
38
43
  end
39
44
  end
40
45
 
41
- def expire
46
+ def expire # rubocop:disable Metrics/MethodLength
42
47
  removed_keys = {}
43
48
  connection do |conn|
44
- conn.hgetall(SidekiqUniqueJobs::HASH_KEY).each do |jid, unique_key|
45
- next if conn.get(unique_key)
46
- conn.hdel(SidekiqUniqueJobs::HASH_KEY, jid)
47
- removed_keys[jid] = unique_key
49
+ cursor = '0'
50
+ loop do
51
+ cursor, jobs = get_jobs(conn, cursor)
52
+ jobs.each do |job_array|
53
+ jid, unique_key = job_array
54
+
55
+ next if conn.get(unique_key)
56
+ conn.hdel(SidekiqUniqueJobs::HASH_KEY, jid)
57
+ removed_keys[jid] = unique_key
58
+ end
59
+
60
+ break if cursor == '0'
48
61
  end
49
62
  end
63
+
50
64
  removed_keys
51
65
  end
52
66
 
67
+ private
68
+
69
+ def get_jobs(conn, cursor)
70
+ conn.hscan(SidekiqUniqueJobs::HASH_KEY, [cursor, MATCH, SCAN_PATTERN, COUNT, EXPIRE_BATCH_SIZE])
71
+ end
72
+
53
73
  def keys_by_scan(pattern, count)
54
74
  connection { |conn| conn.scan_each(match: prefix(pattern), count: count).to_a }
55
75
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SidekiqUniqueJobs
2
- VERSION = '5.0.2'.freeze
4
+ VERSION = '5.0.11'
3
5
  end
@@ -12,7 +12,7 @@ if stored_jid then
12
12
  end
13
13
 
14
14
  if redis.pcall('set', unique_key, job_id, 'nx', 'ex', expires) then
15
- redis.pcall('hsetnx', 'uniquejobs', job_id, unique_key)
15
+ -- redis.pcall('hsetnx', 'uniquejobs', job_id, unique_key)
16
16
  return 1
17
17
  else
18
18
  return 0