sidekiq-unique-jobs 6.0.25 → 7.0.0.beta2

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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +155 -20
  3. data/README.md +349 -112
  4. data/lib/sidekiq-unique-jobs.rb +2 -0
  5. data/lib/sidekiq_unique_jobs.rb +43 -6
  6. data/lib/sidekiq_unique_jobs/batch_delete.rb +121 -0
  7. data/lib/sidekiq_unique_jobs/changelog.rb +71 -0
  8. data/lib/sidekiq_unique_jobs/cli.rb +20 -29
  9. data/lib/sidekiq_unique_jobs/config.rb +193 -0
  10. data/lib/sidekiq_unique_jobs/connection.rb +5 -4
  11. data/lib/sidekiq_unique_jobs/constants.rb +36 -24
  12. data/lib/sidekiq_unique_jobs/core_ext.rb +38 -0
  13. data/lib/sidekiq_unique_jobs/digests.rb +78 -93
  14. data/lib/sidekiq_unique_jobs/exceptions.rb +152 -8
  15. data/lib/sidekiq_unique_jobs/job.rb +3 -3
  16. data/lib/sidekiq_unique_jobs/json.rb +34 -0
  17. data/lib/sidekiq_unique_jobs/key.rb +93 -0
  18. data/lib/sidekiq_unique_jobs/lock.rb +295 -0
  19. data/lib/sidekiq_unique_jobs/lock/base_lock.rb +49 -43
  20. data/lib/sidekiq_unique_jobs/lock/client_validator.rb +28 -0
  21. data/lib/sidekiq_unique_jobs/lock/server_validator.rb +27 -0
  22. data/lib/sidekiq_unique_jobs/lock/until_and_while_executing.rb +8 -17
  23. data/lib/sidekiq_unique_jobs/lock/until_executed.rb +5 -5
  24. data/lib/sidekiq_unique_jobs/lock/until_expired.rb +1 -23
  25. data/lib/sidekiq_unique_jobs/lock/validator.rb +65 -0
  26. data/lib/sidekiq_unique_jobs/lock/while_executing.rb +12 -8
  27. data/lib/sidekiq_unique_jobs/lock/while_executing_reject.rb +1 -1
  28. data/lib/sidekiq_unique_jobs/lock_config.rb +95 -0
  29. data/lib/sidekiq_unique_jobs/lock_info.rb +68 -0
  30. data/lib/sidekiq_unique_jobs/locksmith.rb +255 -99
  31. data/lib/sidekiq_unique_jobs/logging.rb +148 -22
  32. data/lib/sidekiq_unique_jobs/logging/middleware_context.rb +44 -0
  33. data/lib/sidekiq_unique_jobs/lua/delete.lua +51 -0
  34. data/lib/sidekiq_unique_jobs/lua/delete_by_digest.lua +46 -0
  35. data/lib/sidekiq_unique_jobs/lua/delete_job_by_digest.lua +38 -0
  36. data/lib/sidekiq_unique_jobs/lua/find_digest_in_queues.lua +26 -0
  37. data/lib/sidekiq_unique_jobs/lua/find_digest_in_sorted_set.lua +24 -0
  38. data/lib/sidekiq_unique_jobs/lua/lock.lua +91 -0
  39. data/lib/sidekiq_unique_jobs/lua/locked.lua +35 -0
  40. data/lib/sidekiq_unique_jobs/lua/queue.lua +83 -0
  41. data/lib/sidekiq_unique_jobs/lua/reap_orphans.lua +86 -0
  42. data/lib/sidekiq_unique_jobs/lua/shared/_common.lua +40 -0
  43. data/lib/sidekiq_unique_jobs/lua/shared/_current_time.lua +8 -0
  44. data/lib/sidekiq_unique_jobs/lua/shared/_delete_from_queue.lua +19 -0
  45. data/lib/sidekiq_unique_jobs/lua/shared/_delete_from_sorted_set.lua +18 -0
  46. data/lib/sidekiq_unique_jobs/lua/shared/_find_digest_in_queues.lua +46 -0
  47. data/lib/sidekiq_unique_jobs/lua/shared/_find_digest_in_sorted_set.lua +24 -0
  48. data/lib/sidekiq_unique_jobs/lua/shared/_hgetall.lua +13 -0
  49. data/lib/sidekiq_unique_jobs/lua/shared/_upgrades.lua +3 -0
  50. data/lib/sidekiq_unique_jobs/lua/shared/find_digest_in_sorted_set.lua +24 -0
  51. data/lib/sidekiq_unique_jobs/lua/unlock.lua +99 -0
  52. data/lib/sidekiq_unique_jobs/lua/update_version.lua +40 -0
  53. data/lib/sidekiq_unique_jobs/lua/upgrade.lua +68 -0
  54. data/lib/sidekiq_unique_jobs/middleware.rb +62 -31
  55. data/lib/sidekiq_unique_jobs/middleware/client.rb +42 -0
  56. data/lib/sidekiq_unique_jobs/middleware/server.rb +27 -0
  57. data/lib/sidekiq_unique_jobs/normalizer.rb +3 -3
  58. data/lib/sidekiq_unique_jobs/on_conflict.rb +22 -9
  59. data/lib/sidekiq_unique_jobs/on_conflict/log.rb +8 -4
  60. data/lib/sidekiq_unique_jobs/on_conflict/reject.rb +59 -13
  61. data/lib/sidekiq_unique_jobs/on_conflict/replace.rb +42 -13
  62. data/lib/sidekiq_unique_jobs/on_conflict/reschedule.rb +4 -4
  63. data/lib/sidekiq_unique_jobs/on_conflict/strategy.rb +24 -5
  64. data/lib/sidekiq_unique_jobs/options_with_fallback.rb +47 -23
  65. data/lib/sidekiq_unique_jobs/orphans/manager.rb +100 -0
  66. data/lib/sidekiq_unique_jobs/orphans/observer.rb +42 -0
  67. data/lib/sidekiq_unique_jobs/orphans/reaper.rb +201 -0
  68. data/lib/sidekiq_unique_jobs/profiler.rb +51 -0
  69. data/lib/sidekiq_unique_jobs/redis.rb +11 -0
  70. data/lib/sidekiq_unique_jobs/redis/entity.rb +94 -0
  71. data/lib/sidekiq_unique_jobs/redis/hash.rb +56 -0
  72. data/lib/sidekiq_unique_jobs/redis/list.rb +32 -0
  73. data/lib/sidekiq_unique_jobs/redis/set.rb +32 -0
  74. data/lib/sidekiq_unique_jobs/redis/sorted_set.rb +59 -0
  75. data/lib/sidekiq_unique_jobs/redis/string.rb +49 -0
  76. data/lib/sidekiq_unique_jobs/rspec/matchers.rb +19 -0
  77. data/lib/sidekiq_unique_jobs/rspec/matchers/have_valid_sidekiq_options.rb +43 -0
  78. data/lib/sidekiq_unique_jobs/{scripts.rb → script.rb} +43 -29
  79. data/lib/sidekiq_unique_jobs/script/caller.rb +125 -0
  80. data/lib/sidekiq_unique_jobs/script/template.rb +41 -0
  81. data/lib/sidekiq_unique_jobs/sidekiq_unique_ext.rb +92 -65
  82. data/lib/sidekiq_unique_jobs/sidekiq_unique_jobs.rb +166 -28
  83. data/lib/sidekiq_unique_jobs/sidekiq_worker_methods.rb +10 -11
  84. data/lib/sidekiq_unique_jobs/testing.rb +47 -15
  85. data/lib/sidekiq_unique_jobs/time_calculator.rb +103 -0
  86. data/lib/sidekiq_unique_jobs/timing.rb +58 -0
  87. data/lib/sidekiq_unique_jobs/unique_args.rb +19 -21
  88. data/lib/sidekiq_unique_jobs/unlockable.rb +11 -2
  89. data/lib/sidekiq_unique_jobs/update_version.rb +25 -0
  90. data/lib/sidekiq_unique_jobs/upgrade_locks.rb +151 -0
  91. data/lib/sidekiq_unique_jobs/version.rb +3 -1
  92. data/lib/sidekiq_unique_jobs/version_check.rb +1 -1
  93. data/lib/sidekiq_unique_jobs/web.rb +25 -19
  94. data/lib/sidekiq_unique_jobs/web/helpers.rb +98 -6
  95. data/lib/sidekiq_unique_jobs/web/views/lock.erb +108 -0
  96. data/lib/sidekiq_unique_jobs/web/views/locks.erb +52 -0
  97. data/lib/tasks/changelog.rake +4 -3
  98. metadata +70 -35
  99. data/lib/sidekiq_unique_jobs/client/middleware.rb +0 -56
  100. data/lib/sidekiq_unique_jobs/server/middleware.rb +0 -46
  101. data/lib/sidekiq_unique_jobs/timeout.rb +0 -8
  102. data/lib/sidekiq_unique_jobs/timeout/calculator.rb +0 -63
  103. data/lib/sidekiq_unique_jobs/util.rb +0 -103
  104. data/lib/sidekiq_unique_jobs/web/views/unique_digest.erb +0 -28
  105. data/lib/sidekiq_unique_jobs/web/views/unique_digests.erb +0 -46
  106. data/redis/acquire_lock.lua +0 -21
  107. data/redis/convert_legacy_lock.lua +0 -13
  108. data/redis/delete.lua +0 -14
  109. data/redis/delete_by_digest.lua +0 -23
  110. data/redis/delete_job_by_digest.lua +0 -60
  111. data/redis/lock.lua +0 -62
  112. data/redis/release_stale_locks.lua +0 -90
  113. data/redis/unlock.lua +0 -35
@@ -1,3 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sidekiq_unique_jobs"
4
+
5
+ SidekiqUniqueJobs::Middleware.configure
@@ -1,25 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "forwardable"
3
+ require "concurrent/future"
4
+ require "concurrent/promises"
5
+ require "concurrent/timer_task"
6
+ require "concurrent/map"
4
7
  require "concurrent/mutable_struct"
8
+ require "digest"
9
+ require "digest/sha1"
10
+ require "erb"
11
+ require "forwardable"
12
+ require "json"
13
+ require "pathname"
14
+ require "sidekiq"
5
15
 
6
16
  require "sidekiq_unique_jobs/version"
7
17
  require "sidekiq_unique_jobs/version_check"
8
18
  require "sidekiq_unique_jobs/constants"
19
+ require "sidekiq_unique_jobs/json"
9
20
  require "sidekiq_unique_jobs/logging"
21
+ require "sidekiq_unique_jobs/logging/middleware_context"
22
+ require "sidekiq_unique_jobs/timing"
10
23
  require "sidekiq_unique_jobs/sidekiq_worker_methods"
11
24
  require "sidekiq_unique_jobs/connection"
12
25
  require "sidekiq_unique_jobs/exceptions"
26
+ require "sidekiq_unique_jobs/script"
27
+ require "sidekiq_unique_jobs/script/caller"
28
+ require "sidekiq_unique_jobs/script/template"
29
+ require "sidekiq_unique_jobs/json"
30
+ require "sidekiq_unique_jobs/normalizer"
13
31
  require "sidekiq_unique_jobs/job"
14
- require "sidekiq_unique_jobs/util"
15
- require "sidekiq_unique_jobs/digests"
32
+ require "sidekiq_unique_jobs/redis"
33
+ require "sidekiq_unique_jobs/redis/entity"
34
+ require "sidekiq_unique_jobs/redis/hash"
35
+ require "sidekiq_unique_jobs/redis/list"
36
+ require "sidekiq_unique_jobs/redis/set"
37
+ require "sidekiq_unique_jobs/redis/sorted_set"
38
+ require "sidekiq_unique_jobs/redis/string"
39
+ require "sidekiq_unique_jobs/batch_delete"
40
+ require "sidekiq_unique_jobs/orphans/reaper"
41
+ require "sidekiq_unique_jobs/orphans/observer"
42
+ require "sidekiq_unique_jobs/orphans/manager"
16
43
  require "sidekiq_unique_jobs/cli"
17
44
  require "sidekiq_unique_jobs/core_ext"
18
- require "sidekiq_unique_jobs/timeout"
19
- require "sidekiq_unique_jobs/scripts"
45
+ require "sidekiq_unique_jobs/time_calculator"
20
46
  require "sidekiq_unique_jobs/unique_args"
21
47
  require "sidekiq_unique_jobs/unlockable"
48
+ require "sidekiq_unique_jobs/key"
22
49
  require "sidekiq_unique_jobs/locksmith"
50
+ require "sidekiq_unique_jobs/options_with_fallback"
51
+ require "sidekiq_unique_jobs/lock"
52
+ require "sidekiq_unique_jobs/lock_config"
53
+ require "sidekiq_unique_jobs/lock_info"
23
54
  require "sidekiq_unique_jobs/lock/base_lock"
24
55
  require "sidekiq_unique_jobs/lock/until_executed"
25
56
  require "sidekiq_unique_jobs/lock/until_executing"
@@ -27,9 +58,15 @@ require "sidekiq_unique_jobs/lock/until_expired"
27
58
  require "sidekiq_unique_jobs/lock/while_executing"
28
59
  require "sidekiq_unique_jobs/lock/while_executing_reject"
29
60
  require "sidekiq_unique_jobs/lock/until_and_while_executing"
30
- require "sidekiq_unique_jobs/options_with_fallback"
31
61
  require "sidekiq_unique_jobs/middleware"
62
+ require "sidekiq_unique_jobs/middleware/client"
63
+ require "sidekiq_unique_jobs/middleware/server"
32
64
  require "sidekiq_unique_jobs/sidekiq_unique_ext"
33
65
  require "sidekiq_unique_jobs/on_conflict"
66
+ require "sidekiq_unique_jobs/changelog"
67
+ require "sidekiq_unique_jobs/digests"
34
68
 
69
+ require "sidekiq_unique_jobs/config"
35
70
  require "sidekiq_unique_jobs/sidekiq_unique_jobs"
71
+ require "sidekiq_unique_jobs/update_version"
72
+ require "sidekiq_unique_jobs/upgrade_locks"
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqUniqueJobs
4
+ #
5
+ # Class BatchDelete provides batch deletion of digests
6
+ #
7
+ # @author Mikael Henriksson <mikael@zoolutions.se>
8
+ #
9
+ class BatchDelete
10
+ #
11
+ # @return [Integer] the default batch size
12
+ BATCH_SIZE = 100
13
+
14
+ SUFFIXES = %w[
15
+ QUEUED
16
+ PRIMED
17
+ LOCKED
18
+ INFO
19
+ ].freeze
20
+
21
+ # includes "SidekiqUniqueJobs::Connection"
22
+ # @!parse include SidekiqUniqueJobs::Connection
23
+ include SidekiqUniqueJobs::Connection
24
+ # includes "SidekiqUniqueJobs::Logging"
25
+ # @!parse include SidekiqUniqueJobs::Logging
26
+ include SidekiqUniqueJobs::Logging
27
+
28
+ #
29
+ # @!attribute [r] digests
30
+ # @return [Array<String>] a collection of digests to be deleted
31
+ attr_reader :digests
32
+ #
33
+ # @!attribute [r] conn
34
+ # @return [Redis, RedisConnection, ConnectionPool] a redis connection
35
+ attr_reader :conn
36
+
37
+ #
38
+ # Executes a batch deletion of the provided digests
39
+ #
40
+ # @param [Array<String>] digests the digests to delete
41
+ # @param [Redis] conn the connection to use for deletion
42
+ #
43
+ # @return [void]
44
+ #
45
+ def self.call(digests, conn)
46
+ new(digests, conn).call
47
+ end
48
+
49
+ #
50
+ # Initialize a new batch delete instance
51
+ #
52
+ # @param [Array<String>] digests the digests to delete
53
+ # @param [Redis] conn the connection to use for deletion
54
+ #
55
+ def initialize(digests, conn)
56
+ @count = 0
57
+ @digests = digests
58
+ @conn = conn
59
+ @digests ||= []
60
+ @digests.compact!
61
+ redis_version # Avoid pipelined calling redis_version and getting a future.
62
+ end
63
+
64
+ #
65
+ # Executes a batch deletion of the provided digests
66
+ # @note Just wraps batch_delete to be able to provide no connection
67
+ #
68
+ #
69
+ def call
70
+ return log_info("Nothing to delete; exiting.") if digests.none?
71
+
72
+ log_info("Deleting batch with #{digests.size} digests")
73
+ return batch_delete(conn) if conn
74
+
75
+ redis { |rcon| batch_delete(rcon) }
76
+ end
77
+
78
+ private
79
+
80
+ #
81
+ # Does the actual batch deletion
82
+ #
83
+ #
84
+ # @return [Integer] the number of deleted digests
85
+ #
86
+ def batch_delete(conn)
87
+ digests.each_slice(BATCH_SIZE) do |chunk|
88
+ conn.pipelined do
89
+ chunk.each do |digest|
90
+ del_digest(conn, digest)
91
+ conn.zrem(SidekiqUniqueJobs::DIGESTS, digest)
92
+ @count += 1
93
+ end
94
+ end
95
+ end
96
+
97
+ @count
98
+ end
99
+
100
+ def del_digest(conn, digest)
101
+ removable_keys = keys_for_digest(digest)
102
+
103
+ if VersionCheck.satisfied?(redis_version, ">= 4.0.0")
104
+ conn.unlink(*removable_keys)
105
+ else
106
+ conn.del(*removable_keys)
107
+ end
108
+ end
109
+
110
+ def keys_for_digest(digest)
111
+ [digest, "#{digest}:RUN"].each_with_object([]) do |key, digest_keys|
112
+ digest_keys.concat([key])
113
+ digest_keys.concat(SUFFIXES.map { |suffix| "#{key}:#{suffix}" })
114
+ end
115
+ end
116
+
117
+ def redis_version
118
+ @redis_version ||= SidekiqUniqueJobs.config.redis_version
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqUniqueJobs
4
+ #
5
+ # Class Changelogs provides access to the changelog entries
6
+ #
7
+ # @author Mikael Henriksson <mikael@zoolutions.se>
8
+ #
9
+ class Changelog < Redis::SortedSet
10
+ def initialize
11
+ super(CHANGELOGS)
12
+ end
13
+
14
+ #
15
+ # Adds a new changelog entry
16
+ #
17
+ # @param [String] message a descriptive message about the entry
18
+ # @param [String] digest a unique digest
19
+ # @param [String] job_id a Sidekiq JID
20
+ # @param [String] script the name of the script adding the entry
21
+ #
22
+ # @return [void]
23
+ #
24
+ def add(message:, digest:, job_id:, script:)
25
+ message = dump_json(message: message, digest: digest, job_id: job_id, script: script)
26
+ redis { |conn| conn.zadd(key, now_f, message) }
27
+ end
28
+
29
+ #
30
+ # The change log entries
31
+ #
32
+ # @param [String] pattern the pattern to match
33
+ # @param [Integer] count the number of matches to return
34
+ #
35
+ # @return [Array<Hash>] an array of entries
36
+ #
37
+ def entries(pattern: "*", count: nil)
38
+ options = {}
39
+ options[:match] = pattern if pattern
40
+ options[:count] = count if count
41
+
42
+ redis do |conn|
43
+ conn.zscan_each(key, options).to_a.map { |entry| load_json(entry[0]) }
44
+ end
45
+ end
46
+
47
+ #
48
+ # Paginate the changelog entries
49
+ #
50
+ # @param [Integer] cursor the cursor for this iteration
51
+ # @param [String] pattern "*" the pattern to match
52
+ # @param [Integer] page_size 100 the number of matches to return
53
+ #
54
+ # @return [Array<Integer, Integer, Array<Hash>] the total size, next cursor and changelog entries
55
+ #
56
+ def page(cursor, pattern: "*", page_size: 100)
57
+ redis do |conn|
58
+ total_size, result = conn.multi do
59
+ conn.zcard(key)
60
+ conn.zscan(key, cursor, match: pattern, count: page_size)
61
+ end
62
+
63
+ [
64
+ total_size,
65
+ result[0], # next_cursor
66
+ result[1].map { |entry| load_json(entry[0]) }, # entries
67
+ ]
68
+ end
69
+ end
70
+ end
71
+ end
@@ -13,54 +13,45 @@ module SidekiqUniqueJobs
13
13
  "jobs #{@package_name} #{command.usage}"
14
14
  end
15
15
 
16
- desc "keys PATTERN", "list all unique keys and their expiry time"
17
- option :count, aliases: :c, type: :numeric, default: 1000, desc: "The max number of keys to return"
18
- def keys(pattern = "*")
19
- keys = Util.keys(pattern, options[:count])
20
- say "Found #{keys.size} keys matching '#{pattern}':"
21
- print_in_columns(keys.sort) if keys.any?
16
+ desc "list PATTERN", "list all unique digests and their expiry time"
17
+ option :count, aliases: :c, type: :numeric, default: 1000, desc: "The max number of digests to return"
18
+ def list(pattern = "*")
19
+ digests = SidekiqUniqueJobs::Digests.new.entries(pattern: pattern, count: options[:count])
20
+ say "Found #{digests.size} digests matching '#{pattern}':"
21
+ print_in_columns(digests.sort) if digests.any?
22
22
  end
23
23
 
24
- desc "del PATTERN", "deletes unique keys from redis by pattern"
24
+ desc "del PATTERN", "deletes unique digests from redis by pattern"
25
25
  option :dry_run, aliases: :d, type: :boolean, desc: "set to false to perform deletion"
26
- option :count, aliases: :c, type: :numeric, default: 1000, desc: "The max number of keys to return"
26
+ option :count, aliases: :c, type: :numeric, default: 1000, desc: "The max number of digests to return"
27
27
  def del(pattern)
28
28
  max_count = options[:count]
29
29
  if options[:dry_run]
30
- keys = Util.keys(pattern, max_count)
31
- say "Would delete #{keys.size} keys matching '#{pattern}'"
30
+ digests = SidekiqUniqueJobs::Digests.new.entries(pattern: pattern, count: max_count)
31
+ say "Would delete #{digests.size} digests matching '#{pattern}'"
32
32
  else
33
- deleted_count = Util.del(pattern, max_count)
34
- say "Deleted #{deleted_count} keys matching '#{pattern}'"
33
+ deleted_count = SidekiqUniqueJobs::Digests.new.del(pattern: pattern, count: max_count)
34
+ say "Deleted #{deleted_count} digests matching '#{pattern}'"
35
35
  end
36
36
  end
37
37
 
38
38
  desc "console", "drop into a console with easy access to helper methods"
39
39
  def console
40
- say "Use `keys '*', 1000 to display the first 1000 unique keys matching '*'"
41
- say "Use `del '*', 1000, true (default) to see how many keys would be deleted for the pattern '*'"
42
- say "Use `del '*', 1000, false to delete the first 1000 keys matching '*'"
43
- Object.include SidekiqUniqueJobs::Util
40
+ say "Use `list '*', 1000 to display the first 1000 unique digests matching '*'"
41
+ say "Use `del '*', 1000, true (default) to see how many digests would be deleted for the pattern '*'"
42
+ say "Use `del '*', 1000, false to delete the first 1000 digests matching '*'"
43
+
44
+ # Object.include SidekiqUniqueJobs::Api
44
45
  console_class.start
45
46
  end
46
47
 
47
48
  no_commands do
48
49
  def console_class
49
- return irb if RUBY_PLATFORM == "JAVA"
50
-
51
- pry
52
- end
53
-
54
- def irb
55
- require "irb"
56
- IRB
57
- end
58
-
59
- def pry
60
50
  require "pry"
61
51
  Pry
62
- rescue LoadError, NameError
63
- irb
52
+ rescue NameError, LoadError
53
+ require "irb"
54
+ IRB
64
55
  end
65
56
  end
66
57
  end
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SidekiqUniqueJobs
4
+ # ThreadSafe config exists to be able to document the config class without errors
5
+ ThreadSafeConfig = Concurrent::MutableStruct.new("ThreadSafeConfig",
6
+ :default_lock_timeout,
7
+ :default_lock_ttl,
8
+ :enabled,
9
+ :unique_prefix,
10
+ :logger,
11
+ :locks,
12
+ :strategies,
13
+ :debug_lua,
14
+ :max_history,
15
+ :reaper,
16
+ :reaper_count,
17
+ :reaper_interval,
18
+ :reaper_timeout,
19
+ :lock_info,
20
+ :raise_on_config_error,
21
+ :current_redis_version)
22
+
23
+ #
24
+ # Shared class for dealing with gem configuration
25
+ #
26
+ # @author Mauro Berlanda <mauro.berlanda@gmail.com>
27
+ class Config < ThreadSafeConfig
28
+ LOCKS_WHILE_ENQUEUED = {
29
+ until_executing: SidekiqUniqueJobs::Lock::UntilExecuting,
30
+ while_enqueued: SidekiqUniqueJobs::Lock::UntilExecuting,
31
+ }.freeze
32
+
33
+ LOCKS_FROM_PUSH_TO_PROCESSED = {
34
+ until_completed: SidekiqUniqueJobs::Lock::UntilExecuted,
35
+ until_executed: SidekiqUniqueJobs::Lock::UntilExecuted,
36
+ until_performed: SidekiqUniqueJobs::Lock::UntilExecuted,
37
+ until_processed: SidekiqUniqueJobs::Lock::UntilExecuted,
38
+ until_and_while_executing: SidekiqUniqueJobs::Lock::UntilAndWhileExecuting,
39
+ until_successfully_completed: SidekiqUniqueJobs::Lock::UntilExecuted,
40
+ }.freeze
41
+
42
+ LOCKS_WITHOUT_UNLOCK = {
43
+ until_expired: SidekiqUniqueJobs::Lock::UntilExpired,
44
+ }.freeze
45
+
46
+ LOCKS_WHEN_BUSY = {
47
+ around_perform: SidekiqUniqueJobs::Lock::WhileExecuting,
48
+ while_busy: SidekiqUniqueJobs::Lock::WhileExecuting,
49
+ while_executing: SidekiqUniqueJobs::Lock::WhileExecuting,
50
+ while_working: SidekiqUniqueJobs::Lock::WhileExecuting,
51
+ while_executing_reject: SidekiqUniqueJobs::Lock::WhileExecutingReject,
52
+ }.freeze
53
+
54
+ LOCKS =
55
+ LOCKS_WHEN_BUSY.dup
56
+ .merge(LOCKS_WHILE_ENQUEUED.dup)
57
+ .merge(LOCKS_WITHOUT_UNLOCK.dup)
58
+ .merge(LOCKS_FROM_PUSH_TO_PROCESSED.dup)
59
+ .freeze
60
+
61
+ STRATEGIES = {
62
+ log: SidekiqUniqueJobs::OnConflict::Log,
63
+ raise: SidekiqUniqueJobs::OnConflict::Raise,
64
+ reject: SidekiqUniqueJobs::OnConflict::Reject,
65
+ replace: SidekiqUniqueJobs::OnConflict::Replace,
66
+ reschedule: SidekiqUniqueJobs::OnConflict::Reschedule,
67
+ }.freeze
68
+
69
+ PREFIX = "uniquejobs"
70
+ LOCK_TIMEOUT = 0
71
+ LOCK_TTL = nil
72
+ ENABLED = true
73
+ DEBUG_LUA = false
74
+ MAX_HISTORY = 1_000
75
+ REAPER = :ruby # The type of cleanup to run. Possible values are [:ruby, :lua]
76
+ REAPER_COUNT = 1_000
77
+ REAPER_INTERVAL = 600 # Every 10 minutes
78
+ REAPER_TIMEOUT = 10 # 10 seconds
79
+ USE_LOCK_INFO = false
80
+ RAISE_ON_CONFIG_ERROR = false
81
+ REDIS_VERSION = "0.0.0"
82
+
83
+ #
84
+ # Returns a default configuration
85
+ #
86
+ # @example
87
+ # SidekiqUniqueJobs::Config.default => <concurrent/mutable_struct/thread_safe_config SidekiqUniqueJobs::Config {
88
+ # default_lock_timeout: 0,
89
+ # default_lock_ttl: nil,
90
+ # enabled: true,
91
+ # unique_prefix: "uniquejobs",
92
+ # logger: #<Sidekiq::Logger:0x00007f81e096b0e0 @level=1 ...>,
93
+ # locks: {
94
+ # around_perform: SidekiqUniqueJobs::Lock::WhileExecuting,
95
+ # while_busy: SidekiqUniqueJobs::Lock::WhileExecuting,
96
+ # while_executing: SidekiqUniqueJobs::Lock::WhileExecuting,
97
+ # while_working: SidekiqUniqueJobs::Lock::WhileExecuting,
98
+ # while_executing_reject: SidekiqUniqueJobs::Lock::WhileExecutingReject,
99
+ # until_executing: SidekiqUniqueJobs::Lock::UntilExecuting,
100
+ # while_enqueued: SidekiqUniqueJobs::Lock::UntilExecuting,
101
+ # until_expired: SidekiqUniqueJobs::Lock::UntilExpired,
102
+ # until_completed: SidekiqUniqueJobs::Lock::UntilExecuted,
103
+ # until_executed: SidekiqUniqueJobs::Lock::UntilExecuted,
104
+ # until_performed: SidekiqUniqueJobs::Lock::UntilExecuted,
105
+ # until_processed: SidekiqUniqueJobs::Lock::UntilExecuted,
106
+ # until_and_while_executing: SidekiqUniqueJobs::Lock::UntilAndWhileExecuting,
107
+ # until_successfully_completed: SidekiqUniqueJobs::Lock::UntilExecuted
108
+ # },
109
+ # strategies: {
110
+ # log: SidekiqUniqueJobs::OnConflict::Log,
111
+ # raise: SidekiqUniqueJobs::OnConflict::Raise,
112
+ # reject: SidekiqUniqueJobs::OnConflict::Reject,
113
+ # replace: SidekiqUniqueJobs::OnConflict::Replace,
114
+ # reschedule: SidekiqUniqueJobs::OnConflict::Reschedule
115
+ # },
116
+ # debug_lua: false,
117
+ # max_history: 1000,
118
+ # reaper:: ruby,
119
+ # reaper_count: 1000,
120
+ # lock_info: false,
121
+ # raise_on_config_error: false,
122
+ # }>
123
+ #
124
+ #
125
+ # @return [SidekiqUniqueJobs::Config] a default configuration
126
+ #
127
+ def self.default # rubocop:disable Metrics/MethodLength
128
+ new(
129
+ LOCK_TIMEOUT,
130
+ LOCK_TTL,
131
+ ENABLED,
132
+ PREFIX,
133
+ Sidekiq.logger,
134
+ LOCKS,
135
+ STRATEGIES,
136
+ DEBUG_LUA,
137
+ MAX_HISTORY,
138
+ REAPER,
139
+ REAPER_COUNT,
140
+ REAPER_INTERVAL,
141
+ REAPER_TIMEOUT,
142
+ USE_LOCK_INFO,
143
+ RAISE_ON_CONFIG_ERROR,
144
+ REDIS_VERSION,
145
+ )
146
+ end
147
+
148
+ #
149
+ # Adds a lock type to the configuration. It will raise if the lock exists already
150
+ #
151
+ # @example Add a custom lock
152
+ # add_lock(:my_lock, CustomLocks::MyLock)
153
+ #
154
+ # @raise DuplicateLock when the name already exists
155
+ #
156
+ # @param [String, Symbol] name the name of the lock
157
+ # @param [Class] klass the class describing the lock
158
+ #
159
+ # @return [void]
160
+ #
161
+ def add_lock(name, klass)
162
+ lock_sym = name.to_sym
163
+ raise DuplicateLock, ":#{name} already defined, please use another name" if locks.key?(lock_sym)
164
+
165
+ new_locks = locks.dup.merge(lock_sym => klass).freeze
166
+ self.locks = new_locks
167
+ end
168
+
169
+ #
170
+ # Adds an on_conflict strategy to the configuration.
171
+ #
172
+ # @example Add a custom strategy
173
+ # add_lock(:my_strategy, CustomStrategies::MyStrategy)
174
+ #
175
+ # @raise [DuplicateStrategy] when the name already exists
176
+ #
177
+ # @param [String] name the name of the custom strategy
178
+ # @param [Class] klass the class describing the strategy
179
+ #
180
+ def add_strategy(name, klass)
181
+ strategy_sym = name.to_sym
182
+ raise DuplicateStrategy, ":#{name} already defined, please use another name" if strategies.key?(strategy_sym)
183
+
184
+ new_strategies = strategies.dup.merge(strategy_sym => klass).freeze
185
+ self.strategies = new_strategies
186
+ end
187
+
188
+ def redis_version
189
+ self.current_redis_version = SidekiqUniqueJobs.fetch_redis_version if current_redis_version == REDIS_VERSION
190
+ current_redis_version
191
+ end
192
+ end
193
+ end