resque-unique_at_runtime 4.0.0 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,6 +15,16 @@ module Resque
15
15
  # end
16
16
  #
17
17
  module UniqueAtRuntime
18
+ ATOMIC_LOCK_SCRIPT = <<~LUA
19
+ local previous_timeout = redis.call("HGET", KEYS[1], ARGV[1])
20
+ if previous_timeout and tonumber(previous_timeout) > tonumber(ARGV[3]) then
21
+ return 0
22
+ end
23
+ redis.call("HSET", KEYS[1], ARGV[1], ARGV[2])
24
+ return 1
25
+ LUA
26
+ LEGACY_LOCK_MUTEX = Mutex.new
27
+
18
28
  def self.included(base)
19
29
  base.extend ClassMethods
20
30
  end
@@ -26,24 +36,25 @@ module Resque
26
36
 
27
37
  def runtime_lock_timeout
28
38
  instance_variable_get(:@runtime_lock_timeout) ||
29
- instance_variable_set(:@runtime_lock_timeout, Resque::UniqueAtRuntime.configuration&.lock_timeout)
39
+ instance_variable_set(:@runtime_lock_timeout, Resque::UniqueAtRuntime.configuration&.lock_timeout)
30
40
  end
31
41
 
32
42
  def runtime_requeue_interval
33
43
  instance_variable_get(:@runtime_requeue_interval) ||
34
- instance_variable_set(:@runtime_requeue_interval, Resque::UniqueAtRuntime.configuration&.requeue_interval)
44
+ instance_variable_set(:@runtime_requeue_interval, Resque::UniqueAtRuntime.configuration&.requeue_interval)
35
45
  end
36
46
 
37
47
  def unique_at_runtime_key_base
38
48
  instance_variable_get(:@unique_at_runtime_key_base) ||
39
- instance_variable_set(:@unique_at_runtime_key_base, Resque::UniqueAtRuntime.configuration&.unique_at_runtime_key_base)
49
+ instance_variable_set(:@unique_at_runtime_key_base, Resque::UniqueAtRuntime.configuration&.unique_at_runtime_key_base)
40
50
  end
41
51
 
42
52
  # Overwrite this method to uniquely identify which mutex should be used
43
53
  # for a resque worker.
44
54
  def unique_at_runtime_redis_key(*_)
45
- Resque::UniqueAtRuntime.debug("getting key for #{@queue}!")
46
- "#{unique_at_runtime_key_base}:#{@queue}"
55
+ queue = Resque.queue_from_class(self)
56
+ Resque::UniqueAtRuntime.debug("getting key for #{queue}!")
57
+ queue
47
58
  end
48
59
 
49
60
  # returns true if the job signature can be locked (is not currently locked)
@@ -59,18 +70,56 @@ module Resque
59
70
 
60
71
  Resque::UniqueAtRuntime.debug("attempting to lock queue with #{key}")
61
72
 
62
- # Per http://redis.io/commands/setnx
63
- return false if Resque.redis.setnx(key, timeout)
64
- return key if Resque.redis.get(key).to_i > now
65
- return false if Resque.redis.getset(key, timeout).to_i <= now
73
+ case atomic_lock_result(key, timeout, now)
74
+ when 1
75
+ false
76
+ when 0
77
+ key
78
+ else
79
+ legacy_queue_locked(key, timeout, now)
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def atomic_lock_result(key, timeout, now)
86
+ result = Resque.redis.eval(
87
+ Resque::Plugins::UniqueAtRuntime::ATOMIC_LOCK_SCRIPT,
88
+ [unique_at_runtime_key_base],
89
+ [key, timeout, now]
90
+ )
91
+ result&.to_i
92
+ rescue NoMethodError, NotImplementedError, Redis::CommandError
93
+ nil
94
+ end
95
+
96
+ # FakeRedis 0.9 exposes EVAL without implementing it. Keep the old
97
+ # command path for that test double and older Redis clients; the mutex
98
+ # only makes this compatibility path deterministic within one process.
99
+ def legacy_queue_locked(key, timeout, now)
100
+ Resque::Plugins::UniqueAtRuntime::LEGACY_LOCK_MUTEX.synchronize do
101
+ return false if Resque.redis.hsetnx(unique_at_runtime_key_base, key, timeout)
102
+
103
+ previous_timeout = Resque.redis.hget(unique_at_runtime_key_base, key).to_i
104
+ return key if previous_timeout > now
66
105
 
67
- key
106
+ Resque.redis.hset(unique_at_runtime_key_base, key, timeout)
107
+ false
108
+ end
68
109
  end
69
110
 
111
+ public
112
+
70
113
  def unlock_queue(*args)
114
+ if @unlock_queue_executed
115
+ Resque::UniqueAtRuntime.debug("unlock queue already executed")
116
+ return
117
+ end
118
+
71
119
  key = unique_at_runtime_redis_key(*args)
72
120
  Resque::UniqueAtRuntime.debug("unlock queue with #{key}")
73
- Resque.redis.del(key)
121
+ Resque.redis.hdel(unique_at_runtime_key_base, key)
122
+ @unlock_queue_executed = true
74
123
  end
75
124
 
76
125
  def reenqueue(*args)
@@ -90,12 +139,14 @@ module Resque
90
139
  # and don't perform
91
140
  raise Resque::Job::DontPerform
92
141
  else
93
- Resque::UniqueAtRuntime.debug('check passed will perform')
142
+ Resque::UniqueAtRuntime.debug("check passed will perform")
94
143
  true
95
144
  end
96
145
  end
97
146
 
98
147
  def around_perform_unlock_runtime(*args)
148
+ @unlock_queue_executed = false
149
+
99
150
  yield
100
151
  ensure
101
152
  unlock_queue(*args)
@@ -105,7 +156,7 @@ module Resque
105
156
  # duplicates the on_failure unlock, but that's a small price to pay for
106
157
  # uniqueness.
107
158
  def on_failure_unlock_runtime(*args)
108
- Resque::UniqueAtRuntime.debug('on failure unlock')
159
+ Resque::UniqueAtRuntime.debug("on failure unlock")
109
160
  unlock_queue(*args)
110
161
  end
111
162
  end
@@ -1,22 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'logger'
3
+ require "logger"
4
4
  module Resque
5
5
  module UniqueAtRuntime
6
6
  class Configuration
7
7
  DEFAULT_LOCK_TIMEOUT = 60 * 60 * 24 * 5
8
8
  DEFAULT_REQUEUE_INTERVAL = 1
9
- DEFAULT_UNIQUE_AT_RUNTIME_KEY_BASE = 'r-uar'.freeze
9
+ DEFAULT_UNIQUE_AT_RUNTIME_KEY_BASE = "r-uar"
10
10
  DEFAULT_LOG_LEVEL = :debug
11
11
 
12
12
  include Singleton
13
13
 
14
14
  attr_accessor :debug_mode,
15
- :lock_timeout,
16
- :log_level,
17
- :logger,
18
- :requeue_interval,
19
- :unique_at_runtime_key_base
15
+ :lock_timeout,
16
+ :log_level,
17
+ :logger,
18
+ :requeue_interval,
19
+ :unique_at_runtime_key_base
20
20
 
21
21
  def initialize
22
22
  debug_mode_from_env
@@ -49,8 +49,8 @@ module Resque
49
49
  private
50
50
 
51
51
  def debug_mode_from_env
52
- env_debug = ENV['RESQUE_DEBUG']
53
- @debug_mode = !!(env_debug == 'true' || (env_debug.is_a?(String) && env_debug.match?(/runtime/)))
52
+ env_debug = ENV["RESQUE_DEBUG"]
53
+ @debug_mode = !!(env_debug == "true" || (env_debug.is_a?(String) && env_debug.match(/runtime/)))
54
54
  end
55
55
  end
56
56
  end
@@ -2,6 +2,12 @@
2
2
 
3
3
  module Resque
4
4
  module UniqueAtRuntime
5
- VERSION = '4.0.0'
5
+ # Version namespace for this gem.
6
+ module Version
7
+ # Current gem version.
8
+ VERSION = "4.0.2"
9
+ end
10
+ # Current gem version exposed at the traditional constant location.
11
+ VERSION = Version::VERSION # Traditional Constant Location
6
12
  end
7
13
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "unique_at_runtime/version"
4
+
5
+ module Resque
6
+ module UniqueAtRuntime
7
+ end
8
+ end
@@ -1,18 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'resque/unique_at_runtime/version'
3
+ require "resque/unique_at_runtime/version"
4
4
 
5
5
  # Ruby Std Lib
6
- require 'digest/md5'
6
+ require "digest/md5"
7
7
 
8
8
  # External Gems
9
- require 'colorized_string'
10
- require 'resque'
9
+ require "resque"
11
10
 
12
11
  # This Gem
13
- require 'resque/plugins/unique_at_runtime'
14
- require 'resque/unique_at_runtime/resque_ext/resque'
15
- require 'resque/unique_at_runtime/configuration'
12
+ require "resque/plugins/unique_at_runtime"
13
+ require "resque/unique_at_runtime/resque_ext/resque"
14
+ require "resque/unique_at_runtime/configuration"
16
15
 
17
16
  # See lib/resque/plugins/unique_at_runtime.rb for the actual plugin
18
17
  #
@@ -21,7 +20,7 @@ require 'resque/unique_at_runtime/configuration'
21
20
  # Resque, Resque::Job, or Resque::Queue.
22
21
  module Resque
23
22
  module UniqueAtRuntime
24
- PLUGIN_TAG = (ColorizedString['[R-UAR] '].blue).freeze
23
+ PLUGIN_TAG = "[R-UAR] "
25
24
 
26
25
  def log(message)
27
26
  configuration.logger&.send(configuration.log_level, message) if configuration.logger
@@ -44,6 +43,6 @@ module Resque
44
43
  self.configuration = Configuration.instance # setup defaults
45
44
 
46
45
  module_function(:log,
47
- :debug)
46
+ :debug)
48
47
  end
49
48
  end
@@ -0,0 +1,8 @@
1
+ module Resque
2
+ module UniqueAtRuntime
3
+ module Version
4
+ VERSION: String
5
+ end
6
+ VERSION: String
7
+ end
8
+ end
data.tar.gz.sig ADDED
Binary file