resque-unique_at_runtime 2.0.4 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d2a39f3df5617304a84c059e96c8fc073ceb0ab4d7b0108bdd0382ab8914098
4
- data.tar.gz: b5044b3d977f372f50f9d2186986ac64ca61c7a056ad01070b6b78e79af34dd6
3
+ metadata.gz: 645ac4f759c2b0721d782071985fdec3d14de968eeef88ae760c0a6e1deb0c67
4
+ data.tar.gz: f2a99e565e17ae87b290b8a834cfb9f6d5d9474fa7ae158eba361344f33c7850
5
5
  SHA512:
6
- metadata.gz: b556a0c053fb7fb04c887e1eaf5788138a118c5e8e1b9dc6511772eea91f03fcce9d8b6b43304b884eadf443e6cd0b4afd5e2830e49eed5fe830d3b0c3503a30
7
- data.tar.gz: 86d93234dd71c3aaa75da1956458217e422e4010c79ba876805ef52cc81f77bab02668ee57d12311a29d88dd9676f2a6ed12927215fd9c3c4335372476a7321a
6
+ metadata.gz: acbfec52b562d31dc543fa4de471700d485cc3654a0ab7813e07a68f029f7d6e43e953e3de849cca9f39f62359ea29b216b2dec3d0e9b3c8a1e9264c595ac777
7
+ data.tar.gz: 7941f8e4a0eabd5a222fd832b46f32f73b52b14ec4a94aa842ba948f26037cbc8b1d1c3e115dad91beba494aa40dd925aa26a8f598cb57e88227642ba337d68e
@@ -0,0 +1,2 @@
1
+ inherit_from: .rubocop_todo.yml
2
+ TargetRubyVersion: 2.5
@@ -0,0 +1,75 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2018-11-07 04:06:03 -0800 using RuboCop version 0.59.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Configuration parameters: Include.
11
+ # Include: **/*.gemspec
12
+ Gemspec/RequiredRubyVersion:
13
+ Exclude:
14
+ - 'resque-unique_at_runtime.gemspec'
15
+
16
+ # Offense count: 1
17
+ Metrics/AbcSize:
18
+ Max: 17
19
+
20
+ # Offense count: 3
21
+ # Configuration parameters: CountComments, ExcludedMethods.
22
+ # ExcludedMethods: refine
23
+ Metrics/BlockLength:
24
+ Max: 102
25
+
26
+ # Offense count: 2
27
+ # Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms.
28
+ # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
29
+ Naming/FileName:
30
+ Exclude:
31
+ - 'lib/resque-unique_at_runtime.rb'
32
+ - 'resque-unique_at_runtime.gemspec'
33
+
34
+ # Offense count: 1
35
+ # Configuration parameters: EnforcedStyle.
36
+ # SupportedStyles: lowercase, uppercase
37
+ Naming/HeredocDelimiterCase:
38
+ Exclude:
39
+ - 'resque-unique_at_runtime.gemspec'
40
+
41
+ # Offense count: 1
42
+ # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
43
+ # AllowedNames: io, id, to, by, on, in, at, ip, db
44
+ Naming/UncommunicativeMethodParamName:
45
+ Exclude:
46
+ - 'lib/resque/plugins/unique_at_runtime.rb'
47
+
48
+ # Offense count: 1
49
+ # Configuration parameters: AllowedChars.
50
+ Style/AsciiComments:
51
+ Exclude:
52
+ - 'lib/resque/plugins/unique_at_runtime.rb'
53
+
54
+ # Offense count: 4
55
+ Style/Documentation:
56
+ Exclude:
57
+ - 'spec/**/*'
58
+ - 'test/**/*'
59
+ - 'lib/resque-unique_at_runtime.rb'
60
+ - 'lib/resque/plugins/unique_at_runtime.rb'
61
+ - 'lib/resque/unique_at_runtime/configuration.rb'
62
+ - 'lib/resque/unique_at_runtime/resque_ext/resque.rb'
63
+
64
+ # Offense count: 1
65
+ # Cop supports --auto-correct.
66
+ # Configuration parameters: AllowAsExpressionSeparator.
67
+ Style/Semicolon:
68
+ Exclude:
69
+ - 'spec/lib/unique_at_runtime_spec.rb'
70
+
71
+ # Offense count: 26
72
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
73
+ # URISchemes: http, https
74
+ Metrics/LineLength:
75
+ Max: 120
data/Gemfile CHANGED
@@ -1,15 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
6
 
5
7
  group :test do
6
- gem 'rubocop', '~> 0.59.0'
7
- gem 'rubocop-rspec', '~> 1.24.0'
8
- gem 'byebug', '~> 10', platform: :mri, require: false
9
- gem 'pry', '~> 0', platform: :mri, require: false
10
- gem 'pry-byebug', '~> 3', platform: :mri, require: false
8
+ unless ENV['TRAVIS']
9
+ gem 'byebug', '~> 10', platform: :mri, require: false
10
+ gem 'pry', '~> 0', platform: :mri, require: false
11
+ gem 'pry-byebug', '~> 3', platform: :mri, require: false
12
+ end
13
+ gem 'rubocop', '~> 0.60.0'
14
+ gem 'rubocop-rspec', '~> 1.30.0'
11
15
  gem 'simplecov', '~> 0', require: false
12
16
  end
13
17
 
14
- # Specify your gem's dependencies in resque-unique_at_runtime.gemspec
18
+ # Specify your gem's dependencies in unique_at_runtime.gemspec
15
19
  gemspec
data/README.md CHANGED
@@ -63,7 +63,7 @@ Or install it yourself as:
63
63
  require 'resque-unique_at_runtime'
64
64
 
65
65
  class StrictlySerialJob
66
- extend Resque::Plugins::UniqueAtRuntime
66
+ include Resque::Plugins::UniqueAtRuntime
67
67
 
68
68
  @queue = :serial_work
69
69
 
@@ -82,7 +82,7 @@ method.
82
82
  require 'resque-unique_at_runtime'
83
83
 
84
84
  class StrictlySerialJob
85
- extend Resque::Plugins::UniqueAtRuntime
85
+ include Resque::Plugins::UniqueAtRuntime
86
86
 
87
87
  @queue = :serial_work
88
88
 
@@ -171,7 +171,7 @@ for its job (data x, data y, data z).
171
171
 
172
172
  #### Example #4 -- Requeue interval
173
173
 
174
- The behavior when multiple jobs exist in a queue protected by resque-unique_at_runtime
174
+ The behavior when multiple jobs exist in a queue protected by `resque-unique_at_runtime`
175
175
  is for one job to be worked, while the other is continuously dequeued and
176
176
  requeued until the first job is finished. This can result in that worker
177
177
  process pegging a CPU/core on a worker server. To guard against this, the
@@ -185,7 +185,7 @@ in your job like so:
185
185
  require 'resque-unique_at_runtime'
186
186
 
187
187
  class StrictlySerialJob
188
- extend Resque::Plugins::UniqueAtRuntime
188
+ include Resque::Plugins::UniqueAtRuntime
189
189
 
190
190
  @queue = :serial_work
191
191
  @runtime_requeue_interval = 5 # sleep for 5 seconds before requeueing
@@ -195,18 +195,26 @@ in your job like so:
195
195
  end
196
196
  end
197
197
 
198
+ #### Example #5 -- Check if a job is running
199
+
200
+ `resque-unique_at_runtime` extends `Resque` with a new method `running?`.
201
+
202
+ It can tell you if a job with a specific signature is running, based on the
203
+ presence of the runtime uniqueness key.
204
+
205
+ Caveat: It can be difficult to get the signature right, especially if you have
206
+ tools that enhance job signatures with options.
207
+
198
208
  ## Contributing
199
209
 
210
+ Bug reports and pull requests are welcome on GitHub at https://github.com/pboling/resque-unique_at_runtime. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
211
+
200
212
  1. Fork it
201
213
  2. Create your feature branch (`git checkout -b my-new-feature`)
202
214
  3. Commit your changes (`git commit -am 'Added some feature'`)
203
215
  4. Push to the branch (`git push origin my-new-feature`)
204
216
  5. Create new Pull Request
205
217
 
206
- ## Contributing
207
-
208
- Bug reports and pull requests are welcome on GitHub at https://github.com/pboling/resque-unique_at_runtime. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
209
-
210
218
  ## Code of Conduct
211
219
 
212
220
  Everyone interacting in the Resque::Plugins::UniqueAtRuntime project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/pboling/resque-unique_at_runtime/blob/master/CODE_OF_CONDUCT.md).
@@ -226,10 +234,9 @@ dependency on this gem using the [Pessimistic Version Constraint][pvc] with two
226
234
  For example:
227
235
 
228
236
  ```ruby
229
- spec.add_dependency 'resque-unique_at_runtime', '~> 0.0'
237
+ spec.add_dependency 'resque-unique_at_runtime', '~> 1.0'
230
238
  ```
231
239
 
232
-
233
240
  ## License
234
241
 
235
242
  * Copyright (c) 2012 Jonathan R. Wallace
data/Rakefile CHANGED
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env rake
2
- require "bundler/gem_tasks"
3
- require "rspec/core/rake_task"
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/gem_tasks'
5
+ require 'rspec/core/rake_task'
4
6
 
5
7
  RSpec::Core::RakeTask.new
6
8
 
7
- task :default => :spec
8
- task :test => :spec
9
+ task default: :spec
10
+ task test: :spec
@@ -1,91 +1,74 @@
1
- require 'resque-unique_at_runtime/version'
1
+ # frozen_string_literal: true
2
2
 
3
- module Resque
4
- module Plugins
5
- module UniqueAtRuntime
6
- LOCK_TIMEOUT = 60 * 60 * 24 * 5
7
- REQUEUE_INTERVAL = 1
8
-
9
- def runtime_lock_timeout_at(now)
10
- now + runtime_lock_timeout + 1
11
- end
3
+ require 'resque/unique_at_runtime/version'
12
4
 
13
- def runtime_lock_timeout
14
- self.instance_variable_get(:@runtime_lock_timeout) || LOCK_TIMEOUT
15
- end
5
+ # Ruby Std Lib
6
+ require 'digest/md5'
16
7
 
17
- def runtime_requeue_interval
18
- self.instance_variable_get(:@runtime_requeue_interval) || REQUEUE_INTERVAL
19
- end
8
+ # External Gems
9
+ require 'colorized_string'
10
+ require 'resque'
20
11
 
21
- # Overwrite this method to uniquely identify which mutex should be used
22
- # for a resque worker.
23
- def unique_at_runtime_redis_key(*_)
24
- puts "resque-unique_at_runtime: getting key for #{@queue}!" if ENV['RESQUE_DEBUG']
25
- "resque-unique_at_runtime:#{@queue}"
26
- end
12
+ # 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'
27
16
 
28
- def can_lock_queue?(*args)
29
- queue_locked?(*args) == false ? true : false
30
- end
31
-
32
- def queue_locked?(*args)
33
- now = Time.now.to_i
34
- key = unique_at_runtime_redis_key(*args)
35
- timeout = runtime_lock_timeout_at(now)
17
+ # See lib/resque/plugins/unique_at_runtime.rb for the actual plugin
18
+ #
19
+ # This is not that ^. Rather, it is an API used by the plugin or as tools by a
20
+ # developer. These methods are not intended to be included/extended into
21
+ # Resque, Resque::Job, or Resque::Queue.
22
+ module Resque
23
+ module UniqueAtRuntime
24
+ PLUGIN_TAG = (ColorizedString['[R-UAR] '].blue).freeze
36
25
 
37
- puts "resque-unique_at_runtime: attempting to lock queue with #{key}" if ENV['RESQUE_DEBUG']
26
+ def runtime_unique_log(message, config_proxy = nil)
27
+ config_proxy ||= uniqueness_configuration
28
+ config_proxy.unique_logger&.send(config_proxy.unique_log_level, message)
29
+ end
38
30
 
39
- # Per http://redis.io/commands/setnx
40
- return false if Resque.redis.setnx(key, timeout)
41
- return key if Resque.redis.get(key).to_i > now
42
- return false if Resque.redis.getset(key, timeout).to_i <= now
43
- return key
44
- end
31
+ def runtime_unique_debug(message, config_proxy = nil)
32
+ config_proxy ||= uniqueness_configuration
33
+ config_proxy.unique_logger&.debug("#{PLUGIN_TAG}#{message}") if config_proxy.debug_mode
34
+ end
45
35
 
46
- def unlock_queue(*args)
47
- key = unique_at_runtime_redis_key(*args)
48
- puts "resque-unique_at_runtime: unlock queue with #{key}" if ENV['RESQUE_DEBUG']
49
- Resque.redis.del(key)
50
- end
36
+ # There are times when the class will need access to the configuration object,
37
+ # such as to override it per instance method
38
+ def uniq_config
39
+ @uniqueness_configuration
40
+ end
51
41
 
52
- def reenqueue(*args)
53
- Resque.enqueue(self, *args)
54
- end
42
+ # For per-class config with a block
43
+ def uniqueness_configure
44
+ @uniqueness_configuration ||= Configuration.new
45
+ yield(@uniqueness_configuration)
46
+ end
55
47
 
56
- def before_perform_lock_runtime(*args)
57
- if (key = queue_locked?(*args))
58
- puts "resque-unique_at_runtime: failed to lock queue with #{key}" if ENV['RESQUE_DEBUG']
48
+ #### CONFIG ####
49
+ class << self
50
+ attr_accessor :uniqueness_configuration
51
+ end
52
+ def uniqueness_config_reset(config = Configuration.new)
53
+ @uniqueness_configuration = config
54
+ end
59
55
 
60
- # Sleep so the CPU's rest
61
- sleep(runtime_requeue_interval)
56
+ def uniqueness_log_level
57
+ @uniqueness_configuration.log_level
58
+ end
62
59
 
63
- # can't get the lock, so re-enqueue the task
64
- reenqueue(*args)
60
+ def uniqueness_log_level=(log_level)
61
+ @uniqueness_configuration.log_level = log_level
62
+ end
65
63
 
66
- # and don't perform
67
- raise Resque::Job::DontPerform
68
- else
69
- puts "will perform"
70
- true
71
- end
72
- end
64
+ self.uniqueness_configuration = Configuration.new # setup defaults
73
65
 
74
- def around_perform_unlock_runtime(*args)
75
- begin
76
- yield
77
- ensure
78
- unlock_queue(*args)
79
- end
80
- end
81
-
82
- # There may be scenarios where the around_perform's ensure unlock
83
- # duplicates the on_failure unlock, but that's a small price to pay for
84
- # uniqueness.
85
- def on_failure_unlock_runtime(*args)
86
- puts "resque-unique_at_runtime: on failure unlock" if ENV['RESQUE_DEBUG']
87
- unlock_queue(*args)
88
- end
89
- end
66
+ module_function(:runtime_unique_log,
67
+ :runtime_unique_debug,
68
+ :uniq_config,
69
+ :uniqueness_configure,
70
+ :uniqueness_config_reset,
71
+ :uniqueness_log_level,
72
+ :uniqueness_log_level=)
90
73
  end
91
74
  end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Resque
4
+ module Plugins
5
+ # If you want your job to support uniqueness at runtime, simply include
6
+ # this module into your job class.
7
+ #
8
+ # class RunAlone
9
+ # @queue = :run_alone
10
+ # include Resque::Plugins::UniqueAtRuntime
11
+ #
12
+ # def self.perform(arg1, arg2)
13
+ # alone_stuff
14
+ # end
15
+ # end
16
+ #
17
+ module UniqueAtRuntime
18
+ def self.included(base)
19
+ base.extend ClassMethods
20
+ end
21
+
22
+ module ClassMethods
23
+ def runtime_lock_timeout_at(now)
24
+ now + runtime_lock_timeout + 1
25
+ end
26
+
27
+ def runtime_lock_timeout
28
+ instance_variable_get(:@runtime_lock_timeout) || Resque::UniqueAtRuntime.uniq_config&.lock_timeout
29
+ end
30
+
31
+ def runtime_requeue_interval
32
+ instance_variable_get(:@runtime_requeue_interval) || Resque::UniqueAtRuntime.uniq_config&.requeue_interval
33
+ end
34
+
35
+ # Overwrite this method to uniquely identify which mutex should be used
36
+ # for a resque worker.
37
+ def unique_at_runtime_redis_key(*_)
38
+ Resque::UniqueAtRuntime.runtime_unique_debug("getting key for #{@queue}!")
39
+ "#{unique_at_runtime_key_base}:#{@queue}"
40
+ end
41
+
42
+ # returns true if the job signature can be locked (is not currently locked)
43
+ def can_lock_queue?(*args)
44
+ !queue_locked?(*args)
45
+ end
46
+
47
+ # returns the locking key if locked, otherwise false
48
+ def queue_locked?(*args)
49
+ now = Time.now.to_i
50
+ key = unique_at_runtime_redis_key(*args)
51
+ timeout = runtime_lock_timeout_at(now)
52
+
53
+ Resque::UniqueAtRuntime.runtime_unique_debug("attempting to lock queue with #{key}")
54
+
55
+ # Per http://redis.io/commands/setnx
56
+ return false if Resque.redis.setnx(key, timeout)
57
+ return key if Resque.redis.get(key).to_i > now
58
+ return false if Resque.redis.getset(key, timeout).to_i <= now
59
+
60
+ key
61
+ end
62
+
63
+ def unlock_queue(*args)
64
+ key = unique_at_runtime_redis_key(*args)
65
+ Resque::UniqueAtRuntime.runtime_unique_debug("unlock queue with #{key}")
66
+ Resque.redis.del(key)
67
+ end
68
+
69
+ def reenqueue(*args)
70
+ Resque.enqueue(self, *args)
71
+ end
72
+
73
+ def before_perform_lock_runtime(*args)
74
+ if (key = queue_locked?(*args))
75
+ Resque::UniqueAtRuntime.runtime_unique_debug("failed to lock queue with #{key}")
76
+
77
+ # Sleep so the CPU's rest
78
+ sleep(runtime_requeue_interval)
79
+
80
+ # can't get the lock, so re-enqueue the task
81
+ reenqueue(*args)
82
+
83
+ # and don't perform
84
+ raise Resque::Job::DontPerform
85
+ else
86
+ Resque::UniqueAtRuntime.runtime_unique_debug('check passed will perform')
87
+ true
88
+ end
89
+ end
90
+
91
+ def around_perform_unlock_runtime(*args)
92
+ yield
93
+ ensure
94
+ unlock_queue(*args)
95
+ end
96
+
97
+ # There may be scenarios where the around_perform's ensure unlock±
98
+ # duplicates the on_failure unlock, but that's a small price to pay for
99
+ # uniqueness.
100
+ def on_failure_unlock_runtime(*args)
101
+ Resque::UniqueAtRuntime.runtime_unique_debug('on failure unlock')
102
+ unlock_queue(*args)
103
+ end
104
+
105
+ def unique_at_runtime_key_base
106
+ instance_variable_get(:@unique_at_runtime_key_base) || Resque::UniqueAtRuntime.uniq_config&.unique_at_runtime_key_base
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ module Resque
5
+ module UniqueAtRuntime
6
+ class Configuration
7
+ DEFAULT_AT_RUNTIME_KEY_BASE = 'r-uae'
8
+ DEFAULT_LOCK_TIMEOUT = 60 * 60 * 24 * 5
9
+ DEFAULT_REQUEUE_INTERVAL = 1
10
+
11
+ attr_accessor :logger,
12
+ :log_level,
13
+ :unique_at_runtime_key_base,
14
+ :lock_timeout,
15
+ :requeue_interval,
16
+ :debug_mode
17
+ def initialize(**options)
18
+ @logger = options.key?(:logger) ? options[:logger] : Logger.new(STDOUT)
19
+ @log_level = options.key?(:log_level) ? options[:log_level] : :debug
20
+ @unique_at_runtime_key_base = options.key?(:unique_at_runtime_key_base) ? options[:unique_at_runtime_key_base] : DEFAULT_AT_RUNTIME_KEY_BASE
21
+ @lock_timeout = options.key?(:lock_timeout) ? options[:lock_timeout] : DEFAULT_LOCK_TIMEOUT
22
+ @requeue_interval = options.key?(:requeue_interval) ? options[:requeue_interval] : DEFAULT_REQUEUE_INTERVAL
23
+ env_debug = ENV['RESQUE_DEBUG']
24
+ @debug_mode = options.key?(:debug_mode) ? options[:debug_mode] : env_debug == 'true' || (env_debug.is_a?(String) && env_debug.match?(/runtime/))
25
+ end
26
+
27
+ def unique_logger
28
+ logger
29
+ end
30
+
31
+ def unique_log_level
32
+ log_level
33
+ end
34
+
35
+ def log(msg)
36
+ Resque::UniqueAtRuntime.runtime_unique_log(msg, self)
37
+ end
38
+
39
+ def to_hash
40
+ {
41
+ logger: logger,
42
+ log_level: log_level
43
+ }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Resque
4
+ class << self
5
+ def running?(klass, *args)
6
+ klass.queue_locked?(*args)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Resque
4
+ module UniqueAtRuntime
5
+ VERSION = '3.0.0'
6
+ end
7
+ end
@@ -1,43 +1,54 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/resque-unique_at_runtime/version', __FILE__)
3
-
4
- Gem::Specification.new do |gem|
5
- gem.authors = ["Peter H. Boling","Jonathan R. Wallace"]
6
- gem.email = ["peter.boling@gmail.com","jonathan.wallace@gmail.com"]
7
- gem.summary = %q{A resque plugin that ensures job uniqueness at runtime.}
8
- gem.homepage = "http://github.com/pboling/resque-unique_at_runtime"
9
-
10
- gem.files = `git ls-files`.split($\)
11
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
12
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
13
- gem.name = "resque-unique_at_runtime"
14
- gem.require_paths = ["lib"]
15
- gem.version = Resque::Plugins::UniqueAtRuntime::VERSION
16
- gem.license = "MIT"
17
- gem.required_ruby_version = ">= 1.9.3"
18
-
19
- gem.add_dependency 'resque', '>= 1.2'
20
- gem.add_development_dependency 'mock_redis'
21
- gem.add_development_dependency 'rake'
22
- gem.add_development_dependency 'rspec', '>= 3.0'
23
- gem.add_development_dependency 'timecop'
24
-
25
- gem.description = <<desc
26
- Ensures that for a given queue, only one worker is working on a job at any given time.
27
-
28
- Example:
29
-
30
- require 'resque/plugins/unique_at_runtime'
31
-
32
- class StrictlySerialJob
33
- extend Resque::Plugins::UniqueAtRuntime
34
-
35
- @queue = :serial_work
36
-
37
- def self.perform
38
- # only one at a time in this block, no parallelism allowed for this
39
- # particular queue
40
- end
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path('lib/resque/unique_at_runtime/version', __dir__)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'resque-unique_at_runtime'
7
+ spec.version = Resque::UniqueAtRuntime::VERSION
8
+ spec.authors = ['Peter H. Boling', 'Jonathan R. Wallace']
9
+ spec.email = ['peter.boling@gmail.com', 'jonathan.wallace@gmail.com']
10
+ spec.license = 'MIT'
11
+
12
+ spec.summary = 'A resque plugin that ensures job uniqueness at runtime.'
13
+ spec.homepage = 'http://github.com/pboling/resque-unique_at_runtime'
14
+ spec.required_ruby_version = '>= 2.3'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
41
18
  end
42
- desc
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_runtime_dependency 'colorize', '~> 0.8'
22
+ spec.add_runtime_dependency 'resque', '>= 1.2'
23
+
24
+ spec.add_development_dependency 'fakeredis', '~> 0.7'
25
+ spec.add_development_dependency 'rake', '~> 12.3'
26
+ spec.add_development_dependency 'rspec', '>= 3.0'
27
+ spec.add_development_dependency 'rspec-block_is_expected', '~> 1.0'
28
+ spec.add_development_dependency 'rspec-stubbed_env', '~> 1.0'
29
+ spec.add_development_dependency 'timecop'
30
+
31
+ spec.add_development_dependency 'pry', '~> 0.11'
32
+ spec.add_development_dependency 'pry-byebug', '~> 3.6'
33
+ spec.add_development_dependency 'rubocop', '~> 0.60'
34
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.30'
35
+
36
+ spec.description = <<~desc
37
+ Ensures that for a given queue, only one worker is working on a job at any given time.
38
+
39
+ Example:
40
+
41
+ require 'resque/plugins/unique_at_runtime'
42
+
43
+ class StrictlySerialJob
44
+ include Resque::Plugins::UniqueAtRuntime
45
+
46
+ @queue = :serial_work
47
+
48
+ def self.perform
49
+ # only one at a time in this block, no parallelism allowed for this
50
+ # particular queue
51
+ end
52
+ end
53
+ desc
43
54
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-unique_at_runtime
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.4
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -9,8 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-09-10 00:00:00.000000000 Z
12
+ date: 2018-11-08 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: colorize
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '0.8'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '0.8'
14
28
  - !ruby/object:Gem::Dependency
15
29
  name: resque
16
30
  requirement: !ruby/object:Gem::Requirement
@@ -26,33 +40,33 @@ dependencies:
26
40
  - !ruby/object:Gem::Version
27
41
  version: '1.2'
28
42
  - !ruby/object:Gem::Dependency
29
- name: mock_redis
43
+ name: fakeredis
30
44
  requirement: !ruby/object:Gem::Requirement
31
45
  requirements:
32
- - - ">="
46
+ - - "~>"
33
47
  - !ruby/object:Gem::Version
34
- version: '0'
48
+ version: '0.7'
35
49
  type: :development
36
50
  prerelease: false
37
51
  version_requirements: !ruby/object:Gem::Requirement
38
52
  requirements:
39
- - - ">="
53
+ - - "~>"
40
54
  - !ruby/object:Gem::Version
41
- version: '0'
55
+ version: '0.7'
42
56
  - !ruby/object:Gem::Dependency
43
57
  name: rake
44
58
  requirement: !ruby/object:Gem::Requirement
45
59
  requirements:
46
- - - ">="
60
+ - - "~>"
47
61
  - !ruby/object:Gem::Version
48
- version: '0'
62
+ version: '12.3'
49
63
  type: :development
50
64
  prerelease: false
51
65
  version_requirements: !ruby/object:Gem::Requirement
52
66
  requirements:
53
- - - ">="
67
+ - - "~>"
54
68
  - !ruby/object:Gem::Version
55
- version: '0'
69
+ version: '12.3'
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: rspec
58
72
  requirement: !ruby/object:Gem::Requirement
@@ -67,6 +81,34 @@ dependencies:
67
81
  - - ">="
68
82
  - !ruby/object:Gem::Version
69
83
  version: '3.0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rspec-block_is_expected
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '1.0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1.0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: rspec-stubbed_env
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '1.0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '1.0'
70
112
  - !ruby/object:Gem::Dependency
71
113
  name: timecop
72
114
  requirement: !ruby/object:Gem::Requirement
@@ -81,6 +123,62 @@ dependencies:
81
123
  - - ">="
82
124
  - !ruby/object:Gem::Version
83
125
  version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: pry
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: '0.11'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: '0.11'
140
+ - !ruby/object:Gem::Dependency
141
+ name: pry-byebug
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - "~>"
145
+ - !ruby/object:Gem::Version
146
+ version: '3.6'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - "~>"
152
+ - !ruby/object:Gem::Version
153
+ version: '3.6'
154
+ - !ruby/object:Gem::Dependency
155
+ name: rubocop
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - "~>"
159
+ - !ruby/object:Gem::Version
160
+ version: '0.60'
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - "~>"
166
+ - !ruby/object:Gem::Version
167
+ version: '0.60'
168
+ - !ruby/object:Gem::Dependency
169
+ name: rubocop-rspec
170
+ requirement: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - "~>"
173
+ - !ruby/object:Gem::Version
174
+ version: '1.30'
175
+ type: :development
176
+ prerelease: false
177
+ version_requirements: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - "~>"
180
+ - !ruby/object:Gem::Version
181
+ version: '1.30'
84
182
  description: |
85
183
  Ensures that for a given queue, only one worker is working on a job at any given time.
86
184
 
@@ -89,7 +187,7 @@ description: |
89
187
  require 'resque/plugins/unique_at_runtime'
90
188
 
91
189
  class StrictlySerialJob
92
- extend Resque::Plugins::UniqueAtRuntime
190
+ include Resque::Plugins::UniqueAtRuntime
93
191
 
94
192
  @queue = :serial_work
95
193
 
@@ -106,6 +204,8 @@ extensions: []
106
204
  extra_rdoc_files: []
107
205
  files:
108
206
  - ".gitignore"
207
+ - ".rubocop.yml"
208
+ - ".rubocop_todo.yml"
109
209
  - ".ruby-version"
110
210
  - ".travis.yml"
111
211
  - CODE_OF_CONDUCT.md
@@ -114,10 +214,11 @@ files:
114
214
  - README.md
115
215
  - Rakefile
116
216
  - lib/resque-unique_at_runtime.rb
117
- - lib/resque-unique_at_runtime/version.rb
217
+ - lib/resque/plugins/unique_at_runtime.rb
218
+ - lib/resque/unique_at_runtime/configuration.rb
219
+ - lib/resque/unique_at_runtime/resque_ext/resque.rb
220
+ - lib/resque/unique_at_runtime/version.rb
118
221
  - resque-unique_at_runtime.gemspec
119
- - spec/lib/unique_at_runtime_spec.rb
120
- - spec/spec_helper.rb
121
222
  homepage: http://github.com/pboling/resque-unique_at_runtime
122
223
  licenses:
123
224
  - MIT
@@ -130,7 +231,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
130
231
  requirements:
131
232
  - - ">="
132
233
  - !ruby/object:Gem::Version
133
- version: 1.9.3
234
+ version: '2.3'
134
235
  required_rubygems_version: !ruby/object:Gem::Requirement
135
236
  requirements:
136
237
  - - ">="
@@ -142,6 +243,4 @@ rubygems_version: 2.7.7
142
243
  signing_key:
143
244
  specification_version: 4
144
245
  summary: A resque plugin that ensures job uniqueness at runtime.
145
- test_files:
146
- - spec/lib/unique_at_runtime_spec.rb
147
- - spec/spec_helper.rb
246
+ test_files: []
@@ -1,7 +0,0 @@
1
- module Resque
2
- module Plugins
3
- module UniqueAtRuntime
4
- VERSION = "2.0.4"
5
- end
6
- end
7
- end
@@ -1,180 +0,0 @@
1
- require 'spec_helper'
2
-
3
- class SerialJob
4
- extend Resque::Plugins::UniqueAtRuntime
5
- @queue = :serial_work
6
-
7
- def self.perform(*args); end
8
- end
9
-
10
- class SerialJobWithCustomRedisKey
11
- extend Resque::Plugins::UniqueAtRuntime
12
- @queue = :serial_work
13
-
14
- def self.unique_at_runtime_redis_key(account_id, *args)
15
- "unique_at_runtime:#{@queue}:#{account_id}"
16
- end
17
-
18
- def self.perform(account_id, *args); end
19
- end
20
-
21
- describe Resque::Plugins::UniqueAtRuntime do
22
- before do
23
- Resque.redis.flushall
24
- end
25
-
26
- describe ".runtime_requeue_interval" do
27
- it "should default to 5" do
28
- expect(SerialJob.runtime_requeue_interval).to eql(1)
29
- end
30
-
31
- it "should be overridable with a class instance var" do
32
- SerialJob.instance_variable_set(:@runtime_requeue_interval, 5)
33
- expect(SerialJob.runtime_requeue_interval).to eql(5)
34
- end
35
- end
36
-
37
- describe ".can_lock_queue?" do
38
- it 'can lock a queue' do
39
- expect(SerialJob.can_lock_queue?(:serial_work)).to eql(true)
40
- end
41
-
42
- it 'cannot lock an already locked queue' do
43
- expect(SerialJob.can_lock_queue?(:serial_work)).to eql(true)
44
- expect(SerialJob.can_lock_queue?(:serial_work)).to eql(false)
45
- end
46
-
47
- it 'cannot lock a queue with active lock' do
48
- expect(SerialJob.can_lock_queue?(:serial_work)).to eql(true)
49
- Timecop.travel(Date.today + 1) do
50
- expect(SerialJob.can_lock_queue?(:serial_work)).to eql(false)
51
- end
52
- end
53
-
54
- it 'can relock a queue with expired lock' do
55
- expect(SerialJob.can_lock_queue?(:serial_work)).to eql(true)
56
-
57
- Timecop.travel(Date.today + 10) do
58
- expect(SerialJob.can_lock_queue?(:serial_work)).to eql(true)
59
- end
60
- end
61
-
62
- it 'solves race condition with getset' do
63
- expect(SerialJob.can_lock_queue?(:serial_work)).to eql(true)
64
-
65
- Timecop.travel(Date.today + 10) do
66
- threads = (1..10).to_a.map {
67
- Thread.new {
68
- Thread.current[:locked] = SerialJob.can_lock_queue?(:serial_work)
69
- }
70
- }
71
-
72
- # Only one worker should acquire lock
73
- locks = threads.map {|t| t.join; t[:locked] }
74
- expect(locks.count(true)).to eql(1)
75
- end
76
- end
77
- end
78
-
79
- describe ".perform" do
80
- before do
81
- SerialJob.instance_variable_set(:@runtime_requeue_interval, 0)
82
- end
83
-
84
- describe "using the default redis key" do
85
- it 'should lock and unlock the queue' do
86
- job = Resque::Job.new(:serial_work, { 'class' => 'SerialJob', 'args' => %w[account_one job_one] })
87
-
88
- # job is the first SerialJob to run so it can lock the queue and perform
89
- expect(SerialJob).to receive(:queue_locked?).and_return(false)
90
-
91
- # but it should also clean up after itself
92
- expect(SerialJob).to receive(:unlock_queue)
93
-
94
- job.perform
95
- end
96
-
97
- it 'should clean up lock even with catastrophic job failure' do
98
- job = Resque::Job.new(:serial_work, { 'class' => 'SerialJob', 'args' => %w[account_one job_one] })
99
-
100
- # job is the first SerialJob to run so it can lock the queue and perform
101
- expect(SerialJob).to receive(:queue_locked?).and_return(false)
102
-
103
- # but we have a catastrophic job failure
104
- expect(SerialJob).to receive(:perform).and_raise(Exception)
105
-
106
- # and still it should clean up after itself
107
- expect(SerialJob).to receive(:unlock_queue).at_least(1).times
108
-
109
- # unfortunately, the job will be lost but resque doesn't guarantee jobs
110
- # aren't lost
111
- expect { job.perform }.to raise_error(Exception)
112
- end
113
-
114
- it 'should place self at the end of the queue if unable to acquire the lock' do
115
- job1_payload = %w[account_one job_one]
116
- job2_payload = %w[account_one job_two]
117
- Resque::Job.create(:serial_work, 'SerialJob', job1_payload)
118
- Resque::Job.create(:serial_work, 'SerialJob', job2_payload)
119
-
120
- expect(SerialJob).to receive(:queue_locked?).and_return(true)
121
-
122
- # perform returns false when DontPerform exception is raised in
123
- # before_perform callback
124
- job1 = Resque.reserve(:serial_work)
125
- expect(job1.perform).to eql(false)
126
-
127
- first_queue_element = Resque.reserve(:serial_work)
128
- expect(first_queue_element.payload["args"]).to eql([job2_payload])
129
- end
130
- end
131
-
132
- describe "with a custom unique_at_runtime_redis_key" do
133
- it 'should lock and unlock the queue' do
134
- job = Resque::Job.new(:serial_work, { 'class' => 'SerialJobWithCustomRedisKey', 'args' => %w[account_one job_one] })
135
-
136
- # job is the first SerialJobWithCustomRedisKey to run so it can lock the queue and perform
137
- expect(SerialJobWithCustomRedisKey).to receive(:queue_locked?).and_return(false)
138
-
139
- # but it should also clean up after itself
140
- expect(SerialJobWithCustomRedisKey).to receive(:unlock_queue)
141
-
142
- job.perform
143
- end
144
-
145
- it 'should clean up lock even with catastrophic job failure' do
146
- job = Resque::Job.new(:serial_work, { 'class' => 'SerialJobWithCustomRedisKey', 'args' => %w[account_one job_one] })
147
-
148
- # job is the first SerialJobWithCustomRedisKey to run so it can lock the queue and perform
149
- expect(SerialJobWithCustomRedisKey).to receive(:queue_locked?).and_return(false)
150
-
151
- # but we have a catastrophic job failure
152
- expect(SerialJobWithCustomRedisKey).to receive(:perform).and_raise(Exception)
153
-
154
- # and still it should clean up after itself
155
- expect(SerialJobWithCustomRedisKey).to receive(:unlock_queue).at_least(1).times
156
-
157
- # unfortunately, the job will be lost but resque doesn't guarantee jobs
158
- # aren't lost
159
- expect { job.perform }.to raise_error(Exception)
160
- end
161
-
162
- it 'should place self at the end of the queue if unable to acquire the lock' do
163
- job1_payload = %w[account_one job_one]
164
- job2_payload = %w[account_one job_two]
165
- Resque::Job.create(:serial_work, 'SerialJobWithCustomRedisKey', job1_payload)
166
- Resque::Job.create(:serial_work, 'SerialJobWithCustomRedisKey', job2_payload)
167
-
168
- expect(SerialJobWithCustomRedisKey).to receive(:queue_locked?).and_return(true)
169
-
170
- # perform returns false when DontPerform exception is raised in
171
- # before_perform callback
172
- job1 = Resque.reserve(:serial_work)
173
- expect(job1.perform).to eql(false)
174
-
175
- first_queue_element = Resque.reserve(:serial_work)
176
- expect(first_queue_element.payload["args"]).to eql([job2_payload])
177
- end
178
- end
179
- end
180
- end
@@ -1,19 +0,0 @@
1
- require 'rspec'
2
-
3
- require 'mock_redis'
4
- require 'resque'
5
- require 'timecop'
6
-
7
- require 'byebug' if RbConfig::CONFIG['RUBY_INSTALL_NAME'] == 'ruby'
8
-
9
- require 'simplecov'
10
- SimpleCov.start
11
-
12
- # This gem
13
- require 'resque-unique_at_runtime'
14
-
15
- RSpec.configure do |config|
16
- config.before(:suite) do
17
- Resque.redis = MockRedis.new
18
- end
19
- end