sidekiq-unique-jobs 3.0.14 → 4.0.0

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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +14 -0
  3. data/.gitignore +2 -0
  4. data/.rubocop.yml +8 -0
  5. data/.simplecov +12 -0
  6. data/.travis.yml +16 -8
  7. data/Appraisals +10 -10
  8. data/CHANGELOG.md +11 -0
  9. data/Gemfile +11 -1
  10. data/README.md +58 -4
  11. data/Rakefile +2 -1
  12. data/circle.yml +36 -0
  13. data/gemfiles/sidekiq_2.17.gemfile +8 -1
  14. data/gemfiles/sidekiq_3.0.gemfile +8 -1
  15. data/gemfiles/sidekiq_3.1.gemfile +8 -1
  16. data/gemfiles/sidekiq_3.2.gemfile +8 -1
  17. data/gemfiles/sidekiq_3.3.gemfile +8 -1
  18. data/gemfiles/sidekiq_develop.gemfile +8 -1
  19. data/lib/sidekiq-unique-jobs.rb +44 -9
  20. data/lib/sidekiq_unique_jobs/client/middleware.rb +47 -0
  21. data/lib/sidekiq_unique_jobs/config.rb +9 -33
  22. data/lib/sidekiq_unique_jobs/core_ext.rb +46 -0
  23. data/lib/sidekiq_unique_jobs/lock.rb +10 -0
  24. data/lib/sidekiq_unique_jobs/lock/time_calculator.rb +44 -0
  25. data/lib/sidekiq_unique_jobs/lock/until_executed.rb +56 -0
  26. data/lib/sidekiq_unique_jobs/lock/until_executing.rb +6 -0
  27. data/lib/sidekiq_unique_jobs/lock/until_timeout.rb +10 -0
  28. data/lib/sidekiq_unique_jobs/lock/while_executing.rb +31 -0
  29. data/lib/sidekiq_unique_jobs/middleware.rb +30 -14
  30. data/lib/sidekiq_unique_jobs/normalizer.rb +7 -0
  31. data/lib/sidekiq_unique_jobs/options_with_fallback.rb +36 -0
  32. data/lib/sidekiq_unique_jobs/run_lock_failed.rb +1 -0
  33. data/lib/sidekiq_unique_jobs/scripts.rb +50 -0
  34. data/lib/sidekiq_unique_jobs/server/middleware.rb +73 -0
  35. data/lib/sidekiq_unique_jobs/sidekiq_unique_ext.rb +71 -9
  36. data/lib/sidekiq_unique_jobs/testing.rb +34 -0
  37. data/lib/sidekiq_unique_jobs/testing/sidekiq_overrides.rb +63 -0
  38. data/lib/sidekiq_unique_jobs/unique_args.rb +132 -0
  39. data/lib/sidekiq_unique_jobs/unlockable.rb +26 -0
  40. data/lib/sidekiq_unique_jobs/version.rb +1 -1
  41. data/redis/aquire_lock.lua +9 -0
  42. data/redis/release_lock.lua +14 -0
  43. data/redis/synchronize.lua +15 -0
  44. data/sidekiq-unique-jobs.gemspec +2 -4
  45. data/spec/lib/sidekiq_unique_jobs/client/middleware_spec.rb +195 -0
  46. data/spec/lib/sidekiq_unique_jobs/core_ext_spec.rb +25 -0
  47. data/spec/lib/sidekiq_unique_jobs/lock/time_calculator_spec.rb +81 -0
  48. data/spec/lib/sidekiq_unique_jobs/lock/while_executing_spec.rb +48 -0
  49. data/spec/lib/sidekiq_unique_jobs/normalizer_spec.rb +21 -0
  50. data/spec/lib/sidekiq_unique_jobs/scripts_spec.rb +74 -0
  51. data/spec/lib/sidekiq_unique_jobs/server/middleware_spec.rb +100 -0
  52. data/spec/lib/{sidekiq_testing_enabled_spec.rb → sidekiq_unique_jobs/sidekiq_testing_enabled_spec.rb} +29 -68
  53. data/spec/lib/sidekiq_unique_jobs/sidekiq_unique_ext_spec.rb +79 -0
  54. data/spec/lib/sidekiq_unique_jobs/sidekiq_unique_jobs_spec.rb +36 -0
  55. data/spec/lib/sidekiq_unique_jobs/unique_args_spec.rb +106 -0
  56. data/spec/spec_helper.rb +40 -10
  57. data/spec/support/matchers/redis_matchers.rb +19 -0
  58. data/spec/support/ruby_meta.rb +10 -0
  59. data/spec/support/sidekiq_meta.rb +11 -2
  60. data/spec/support/unique_macros.rb +52 -0
  61. data/spec/workers/after_unlock_worker.rb +13 -0
  62. data/spec/{support → workers}/after_yield_worker.rb +6 -2
  63. data/spec/{support → workers}/another_unique_worker.rb +1 -1
  64. data/spec/workers/before_yield_worker.rb +9 -0
  65. data/spec/workers/expiring_worker.rb +4 -0
  66. data/spec/workers/inline_expiration_worker.rb +8 -0
  67. data/spec/workers/inline_unlock_order_worker.rb +8 -0
  68. data/spec/workers/inline_worker.rb +8 -0
  69. data/spec/workers/just_a_worker.rb +8 -0
  70. data/spec/workers/main_job.rb +8 -0
  71. data/spec/workers/my_unique_worker.rb +8 -0
  72. data/spec/{support → workers}/my_worker.rb +0 -0
  73. data/spec/workers/plain_class.rb +4 -0
  74. data/spec/workers/queue_worker.rb +6 -0
  75. data/spec/workers/queue_worker_with_filter_method.rb +7 -0
  76. data/spec/workers/queue_worker_with_filter_proc.rb +11 -0
  77. data/spec/workers/run_lock_with_retries_worker.rb +12 -0
  78. data/spec/workers/run_lock_worker.rb +7 -0
  79. data/spec/workers/test_class.rb +4 -0
  80. data/spec/workers/unique_job_with_filter_method.rb +18 -0
  81. data/spec/workers/unique_on_all_queues_worker.rb +13 -0
  82. data/spec/{support → workers}/unique_worker.rb +1 -1
  83. data/spec/workers/while_executing_worker.rb +13 -0
  84. metadata +65 -39
  85. data/lib/sidekiq_unique_jobs/connectors.rb +0 -16
  86. data/lib/sidekiq_unique_jobs/connectors/redis_pool.rb +0 -11
  87. data/lib/sidekiq_unique_jobs/connectors/sidekiq_redis.rb +0 -9
  88. data/lib/sidekiq_unique_jobs/connectors/testing.rb +0 -11
  89. data/lib/sidekiq_unique_jobs/inline_testing.rb +0 -12
  90. data/lib/sidekiq_unique_jobs/middleware/client/strategies/testing_inline.rb +0 -25
  91. data/lib/sidekiq_unique_jobs/middleware/client/strategies/unique.rb +0 -105
  92. data/lib/sidekiq_unique_jobs/middleware/client/unique_jobs.rb +0 -43
  93. data/lib/sidekiq_unique_jobs/middleware/server/unique_jobs.rb +0 -69
  94. data/lib/sidekiq_unique_jobs/payload_helper.rb +0 -42
  95. data/lib/sidekiq_unique_jobs/sidekiq_test_overrides.rb +0 -101
  96. data/spec/lib/client_spec.rb +0 -193
  97. data/spec/lib/middleware/server/unique_jobs_spec.rb +0 -112
  98. data/spec/lib/sidekiq_unique_ext_spec.rb +0 -70
  99. data/spec/lib/unlock_order_spec.rb +0 -64
@@ -1,16 +0,0 @@
1
- require 'sidekiq_unique_jobs/connectors/testing'
2
- require 'sidekiq_unique_jobs/connectors/redis_pool'
3
- require 'sidekiq_unique_jobs/connectors/sidekiq_redis'
4
-
5
- module SidekiqUniqueJobs
6
- module Connectors
7
- CONNECTOR_TYPES = [Testing, RedisPool, SidekiqRedis]
8
-
9
- def self.connection(redis_pool = nil, &block)
10
- CONNECTOR_TYPES.each do |connector|
11
- had_connection = connector.connection(redis_pool, &block)
12
- break if had_connection
13
- end
14
- end
15
- end
16
- end
@@ -1,11 +0,0 @@
1
- module SidekiqUniqueJobs
2
- module Connectors
3
- class RedisPool
4
- def self.connection(redis_pool = nil, &block)
5
- return if redis_pool.nil?
6
- redis_pool.with(&block)
7
- true
8
- end
9
- end
10
- end
11
- end
@@ -1,9 +0,0 @@
1
- module SidekiqUniqueJobs
2
- module Connectors
3
- class SidekiqRedis
4
- def self.connection(_redis_pool = nil, &block)
5
- Sidekiq.redis(&block)
6
- end
7
- end
8
- end
9
- end
@@ -1,11 +0,0 @@
1
- module SidekiqUniqueJobs
2
- module Connectors
3
- class Testing
4
- def self.connection(_redis_pool = nil)
5
- return unless SidekiqUniqueJobs.config.inline_testing_enabled?
6
- yield SidekiqUniqueJobs.redis_mock
7
- true
8
- end
9
- end
10
- end
11
- end
@@ -1,12 +0,0 @@
1
- begin
2
- require 'mock_redis'
3
- rescue LoadError
4
- raise 'To test using Sidekiq::Testing.inline!' \
5
- ' Please add `gem "mock_redis" to your gemfile.'
6
- end
7
-
8
- module SidekiqUniqueJobs
9
- def self.redis_mock
10
- @redis_mock ||= MockRedis.new
11
- end
12
- end
@@ -1,25 +0,0 @@
1
- require 'sidekiq_unique_jobs/middleware/server/unique_jobs'
2
-
3
- module SidekiqUniqueJobs
4
- module Middleware
5
- module Client
6
- module Strategies
7
- class TestingInline < Unique
8
- def self.elegible?
9
- SidekiqUniqueJobs.config.inline_testing_enabled?
10
- end
11
-
12
- def review
13
- _middleware.call(worker_class.new, item, queue, redis_pool) do
14
- super
15
- end
16
- end
17
-
18
- def _middleware
19
- SidekiqUniqueJobs::Middleware::Server::UniqueJobs.new
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,105 +0,0 @@
1
- require 'digest'
2
- require 'sidekiq_unique_jobs/connectors'
3
-
4
- REQUIRE_FILES = lambda do
5
- if SidekiqUniqueJobs.config.testing_enabled? && Sidekiq::Testing.fake?
6
- require 'sidekiq_unique_jobs/sidekiq_test_overrides'
7
- end
8
- end
9
-
10
- module SidekiqUniqueJobs
11
- module Middleware
12
- module Client
13
- module Strategies
14
- class Unique
15
- def self.elegible?
16
- true
17
- end
18
-
19
- def self.review(worker_class, item, queue, redis_pool = nil, log_duplicate_payload = false)
20
- new(worker_class, item, queue, redis_pool, log_duplicate_payload).review do
21
- yield
22
- end
23
- end
24
-
25
- def initialize(worker_class, item, queue, redis_pool = nil, log_duplicate_payload = false)
26
- @worker_class = SidekiqUniqueJobs.worker_class_constantize(worker_class)
27
- @item = item
28
- @queue = queue
29
- @redis_pool = redis_pool
30
- @log_duplicate_payload = log_duplicate_payload
31
- REQUIRE_FILES.call
32
- end
33
-
34
- def review
35
- item['unique_hash'] = payload_hash
36
-
37
- unless unique_for_connection?
38
- Sidekiq.logger.warn "payload is not unique #{item}" if @log_duplicate_payload
39
- return
40
- end
41
-
42
- yield
43
- end
44
-
45
- private
46
-
47
- attr_reader :item, :worker_class, :redis_pool, :queue, :log_duplicate_payload
48
-
49
- def unique_for_connection?
50
- send("#{storage_method}_unique_for?")
51
- end
52
-
53
- def storage_method
54
- SidekiqUniqueJobs.config.unique_storage_method
55
- end
56
-
57
- def old_unique_for?
58
- unique = nil
59
- connection do |conn|
60
- conn.watch(payload_hash)
61
- pid = conn.get(payload_hash).to_i
62
- if pid == 1 || (pid == 2 && item['at'])
63
- # if the job is already queued, or is already scheduled and
64
- # we're trying to schedule again, abort
65
- conn.unwatch
66
- else
67
- unique = conn.multi do
68
- # set value of 2 for scheduled jobs, 1 for queued jobs.
69
- conn.setex(payload_hash, expires_at, item['at'] ? 2 : 1)
70
- end
71
- end
72
- end
73
- unique
74
- end
75
-
76
- def new_unique_for?
77
- connection do |conn|
78
- return conn.set(payload_hash, item['at'] ? 2 : 1, nx: true, ex: expires_at)
79
- end
80
- end
81
-
82
- def expires_at
83
- # if the job was previously scheduled and is now being queued,
84
- # or we've never seen it before
85
- ex = unique_job_expiration || SidekiqUniqueJobs.config.default_expiration
86
- ex = ((Time.at(item['at']) - Time.now.utc) + ex).to_i if item['at']
87
- ex
88
- end
89
-
90
- def connection(&block)
91
- SidekiqUniqueJobs::Connectors.connection(redis_pool, &block)
92
- end
93
-
94
- def payload_hash
95
- SidekiqUniqueJobs::PayloadHelper.get_payload(item['class'], item['queue'], item['args'])
96
- end
97
-
98
- def unique_job_expiration
99
- worker_class.get_sidekiq_options['unique_job_expiration']
100
- end
101
- end
102
- end
103
- end
104
- end
105
- end
@@ -1,43 +0,0 @@
1
- require 'sidekiq_unique_jobs/middleware/client/strategies/unique'
2
- require 'sidekiq_unique_jobs/middleware/client/strategies/testing_inline'
3
-
4
- module SidekiqUniqueJobs
5
- module Middleware
6
- module Client
7
- class UniqueJobs
8
- STRATEGIES = [
9
- Strategies::TestingInline,
10
- Strategies::Unique
11
- ]
12
-
13
- attr_reader :item, :worker_class, :redis_pool
14
-
15
- def call(worker_class, item, queue, redis_pool = nil)
16
- @worker_class = SidekiqUniqueJobs.worker_class_constantize(worker_class)
17
- @item = item
18
- @redis_pool = redis_pool
19
-
20
- if unique_enabled?
21
- strategy.review(worker_class, item, queue, redis_pool, log_duplicate_payload?) { yield }
22
- else
23
- yield
24
- end
25
- end
26
-
27
- private
28
-
29
- def unique_enabled?
30
- worker_class.get_sidekiq_options['unique'] || item['unique']
31
- end
32
-
33
- def log_duplicate_payload?
34
- worker_class.get_sidekiq_options['log_duplicate_payload'] || item['log_duplicate_payload']
35
- end
36
-
37
- def strategy
38
- STRATEGIES.detect(&:elegible?)
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,69 +0,0 @@
1
- require 'digest'
2
- require 'sidekiq_unique_jobs/connectors'
3
-
4
- module SidekiqUniqueJobs
5
- module Middleware
6
- module Server
7
- class UniqueJobs
8
- attr_reader :unlock_order, :redis_pool
9
-
10
- def call(worker, item, _queue, redis_pool = nil)
11
- operative = true
12
- @redis_pool = redis_pool
13
- decide_unlock_order(worker.class)
14
- lock_key = payload_hash(item)
15
- unlock(lock_key) if before_yield?
16
- yield
17
- rescue Sidekiq::Shutdown
18
- operative = false
19
- raise
20
- ensure
21
- unlock(lock_key) if after_yield? && operative
22
- end
23
-
24
- def decide_unlock_order(klass)
25
- @unlock_order = if unlock_order_configured?(klass)
26
- klass.get_sidekiq_options['unique_unlock_order']
27
- else
28
- default_unlock_order
29
- end
30
- end
31
-
32
- def unlock_order_configured?(klass)
33
- klass.respond_to?(:get_sidekiq_options) &&
34
- !klass.get_sidekiq_options['unique_unlock_order'].nil?
35
- end
36
-
37
- def default_unlock_order
38
- SidekiqUniqueJobs.config.default_unlock_order
39
- end
40
-
41
- def before_yield?
42
- unlock_order == :before_yield
43
- end
44
-
45
- def after_yield?
46
- unlock_order == :after_yield
47
- end
48
-
49
- protected
50
-
51
- def payload_hash(item)
52
- SidekiqUniqueJobs::PayloadHelper.get_payload(item['class'], item['queue'], item['args'])
53
- end
54
-
55
- def unlock(payload_hash)
56
- connection { |c| c.del(payload_hash) }
57
- end
58
-
59
- def logger
60
- Sidekiq.logger
61
- end
62
-
63
- def connection(&block)
64
- SidekiqUniqueJobs::Connectors.connection(redis_pool, &block)
65
- end
66
- end
67
- end
68
- end
69
- end
@@ -1,42 +0,0 @@
1
- module SidekiqUniqueJobs
2
- class PayloadHelper
3
- def self.config
4
- SidekiqUniqueJobs.config
5
- end
6
-
7
- def self.get_payload(klass, queue, *args)
8
- unique_on_all_queues = false
9
- if config.unique_args_enabled
10
- worker_class = klass.constantize
11
- args = yield_unique_args(worker_class, *args)
12
- unique_on_all_queues =
13
- worker_class.get_sidekiq_options['unique_on_all_queues']
14
- end
15
- md5_arguments = { class: klass, args: args }
16
- md5_arguments[:queue] = queue unless unique_on_all_queues
17
- "#{config.unique_prefix}:" \
18
- "#{Digest::MD5.hexdigest(Sidekiq.dump_json(md5_arguments))}"
19
- end
20
-
21
- def self.yield_unique_args(worker_class, args)
22
- unique_args = worker_class.get_sidekiq_options['unique_args']
23
- filtered_args(worker_class, unique_args, args)
24
- rescue NameError
25
- # fallback to not filtering args when class can't be instantiated
26
- args
27
- end
28
-
29
- def self.filtered_args(worker_class, unique_args, args)
30
- case unique_args
31
- when Proc
32
- unique_args.call(args)
33
- when Symbol
34
- if worker_class.respond_to?(unique_args)
35
- worker_class.send(unique_args, *args)
36
- end
37
- else
38
- args
39
- end
40
- end
41
- end
42
- end
@@ -1,101 +0,0 @@
1
- require 'sidekiq/testing'
2
-
3
- module Sidekiq
4
- module Worker
5
- module ClassMethods
6
- module Overrides
7
- def self.included(base)
8
- override_methods(base) unless base.method_defined?(:execute_job)
9
-
10
- base.class_eval do
11
- alias_method :execute_job_orig, :execute_job
12
- alias_method :execute_job, :execute_job_ext
13
-
14
- alias_method :clear_orig, :clear
15
- alias_method :clear, :clear_ext
16
- end
17
- end
18
-
19
- def execute_job_ext(worker, args)
20
- execute_job_orig(worker, args)
21
- payload_hash = SidekiqUniqueJobs::PayloadHelper.get_payload(
22
- worker.class.name,
23
- get_sidekiq_options['queue'],
24
- args
25
- )
26
- Sidekiq.redis { |conn| conn.del(payload_hash) }
27
- end
28
-
29
- def clear_ext
30
- payload_hashes = jobs.map { |job| job['unique_hash'] }
31
- clear_orig
32
- return if payload_hashes.empty?
33
-
34
- Sidekiq.redis { |conn| conn.del(*payload_hashes) }
35
- end
36
-
37
- # Disable rubocop because methods are lifted directly out of Sidekiq
38
- # rubocop:disable all
39
- def override_methods(base)
40
- base.class_eval do
41
- define_method(:drain) do
42
- while job = jobs.shift do
43
- worker = new
44
- worker.jid = job['jid']
45
- execute_job(worker, job['args'])
46
- end
47
- end
48
-
49
- define_method(:perform_one) do
50
- raise(EmptyQueueError, "perform_one called with empty job queue") if jobs.empty?
51
- job = jobs.shift
52
- worker = new
53
- worker.jid = job['jid']
54
- execute_job(worker, job['args'])
55
- end
56
-
57
- define_method(:execute_job) do |worker, args|
58
- worker.perform(*args)
59
- end
60
- end
61
- end
62
- # rubocop:enable all
63
-
64
- module_function :override_methods
65
- private_class_method :override_methods
66
- end
67
-
68
- include Overrides
69
- end
70
- end
71
- end
72
-
73
- module Sidekiq
74
- module Worker
75
- module Overrides
76
- def self.included(base)
77
- base.extend ClassMethods
78
-
79
- base.class_eval do
80
- class << self
81
- alias_method :clear_all_orig, :clear_all
82
- alias_method :clear_all, :clear_all_ext
83
- end
84
- end
85
- end
86
-
87
- module ClassMethods
88
- def clear_all_ext
89
- clear_all_orig
90
- unique_prefix = SidekiqUniqueJobs.config.unique_prefix
91
- unique_keys = Sidekiq.redis { |conn| conn.keys("#{unique_prefix}*") }
92
- return if unique_keys.empty?
93
-
94
- Sidekiq.redis { |conn| conn.del(*unique_keys) }
95
- end
96
- end
97
- end
98
-
99
- include Overrides
100
- end
101
- end