resque-uniq 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source :rubygems
2
+
3
+ gem "resque"
4
+
5
+ group :development do
6
+ gem "rake"
7
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Trung Duc Tran
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # resque-uniq
2
+
3
+ A Resque plugin to ensure only one job instance is queued or running at a time
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'resque-uniq'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install resque-uniq
18
+
19
+ ## Usage
20
+
21
+ Make your job class extend `Resque::Plugins::UniqueJob`, like this
22
+
23
+ class BigJob
24
+ extend Resque::Plugins::UniqueJob # <--- add this line
25
+ @queue = :big_job
26
+
27
+ def self.perform
28
+ # ...
29
+ end
30
+ end
31
+
32
+ ## How it works
33
+
34
+ _resque-uniq_ associates a unique lock with each job instance being enqueued. A lock is a simple Redis _key/value_ pair.
35
+ The key name is derived uniquely from the job class name and job args. The value is `Time.now.to_i`. If the lock already
36
+ exists a new job instance is not enqueued. If not a new lock is created and a job is enqueued. The lock is removed after
37
+ its job's `perform` method has finished.
38
+
39
+ There is another lock, called _run lock_, which is being held during the execution of `perform` method. Before enqueuing
40
+ a new job instance, _resque-uniq_ checks if there is any orphaned _run lock_. This way it can detect if Resque workers
41
+ have crashed during job execution and left behind stale locks.
42
+
43
+ You can tell _resque-uniq_ to auto-expire job locks by setting `@unique_lock_autoexpire`
44
+
45
+ class BigJob
46
+ extend Resque::Plugins::UniqueJob
47
+ @queue = :big_job
48
+ @unique_lock_autoexpire = 6 * 3600 # TTL = 6 hours
49
+
50
+ def self.perform
51
+ # ...
52
+ end
53
+ end
54
+
55
+ This is usually not necessary, but you can if you want.
56
+
57
+ ## Credits
58
+
59
+ There are several similar Resque plugins. We tried them all but for one reason or another they don't work reliably
60
+ for us. We wrote our own version which we think works better for us. Nonetheless we would like to thank the authors
61
+ of those plugins for inspiration.
62
+
63
+ ## Contributing
64
+
65
+ 1. Fork it
66
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
67
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
68
+ 4. Push to the branch (`git push origin my-new-feature`)
69
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,85 @@
1
+ module Resque
2
+ module Plugins
3
+ module UniqueJob
4
+ LOCK_NAME_PREFIX = 'lock'
5
+ RUN_LOCK_NAME_PREFIX = 'running_'
6
+
7
+ def lock(*args)
8
+ "#{LOCK_NAME_PREFIX}:#{name}-#{obj_to_string(args)}"
9
+ end
10
+
11
+ def run_lock(*args)
12
+ run_lock_from_lock(lock(*args))
13
+ end
14
+
15
+ def run_lock_from_lock(lock)
16
+ "#{RUN_LOCK_NAME_PREFIX}#{lock}"
17
+ end
18
+
19
+ def lock_from_run_lock(rlock)
20
+ rlock.sub(/^#{RUN_LOCK_NAME_PREFIX}/, '')
21
+ end
22
+
23
+ def stale_lock?(lock)
24
+ return false unless Resque.redis.get(lock)
25
+
26
+ rlock = run_lock_from_lock(lock)
27
+ return false unless Resque.redis.get(rlock)
28
+
29
+ Resque.working.map {|w| w.job }.map do |item|
30
+ payload = item['payload']
31
+ klass = Resque::Job.constantize(payload['class'])
32
+ args = payload['args']
33
+ return false if rlock == klass.run_lock(*args)
34
+ end
35
+ true
36
+ end
37
+
38
+ def before_enqueue_lock(*args)
39
+ lock_name = lock(*args)
40
+ if stale_lock? lock_name
41
+ Resque.redis.del lock_name
42
+ Resque.redis.del "#{RUN_LOCK_NAME_PREFIX}#{lock_name}"
43
+ end
44
+ nx = Resque.redis.setnx(lock_name, Time.now.to_i)
45
+ if defined?(@unique_lock_autoexpire) && @unique_lock_autoexpire > 0
46
+ Resque.redis.expire(lock_name, @unique_lock_autoexpire)
47
+ end
48
+ nx
49
+ end
50
+
51
+ def around_perform_lock(*args)
52
+ rlock = run_lock(*args)
53
+ Resque.redis.set(rlock, Time.now.to_i)
54
+
55
+ begin
56
+ yield
57
+ ensure
58
+ Resque.redis.del(rlock)
59
+ Resque.redis.del(lock(*args))
60
+ end
61
+ end
62
+
63
+
64
+ private
65
+
66
+ def obj_to_string(obj)
67
+ case obj
68
+ when Hash
69
+ s = []
70
+ obj.keys.sort.each do |k|
71
+ s << obj_to_string(k)
72
+ s << obj_to_string(obj[k])
73
+ end
74
+ s.to_s
75
+ when Array
76
+ s = []
77
+ obj.each { |a| s << obj_to_string(a) }
78
+ s.to_s
79
+ else
80
+ obj.to_s
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,3 @@
1
+ module ResqueUniq
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1 @@
1
+ require 'resque/plugins/unique_job'
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ lib = File.expand_path('../lib/', __FILE__)
4
+ $:.unshift lib unless $:.include?(lib)
5
+
6
+ require 'resque-uniq/version'
7
+
8
+ Gem::Specification.new do |gem|
9
+ gem.authors = ["Trung Duc Tran"]
10
+ gem.email = ["trung@tdtran.org"]
11
+ gem.summary = "A Resque plugin to ensure only one job instance is queued or running at a time."
12
+ gem.homepage = "http://github.com/tdtran/resque-uniq"
13
+
14
+ gem.files = `git ls-files`.split($\)
15
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.name = "resque-uniq"
18
+ gem.require_paths = ["lib"]
19
+ gem.version = ResqueUniq::VERSION
20
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resque-uniq
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 3
10
+ version: 0.0.3
11
+ platform: ruby
12
+ authors:
13
+ - Trung Duc Tran
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-07-22 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description:
22
+ email:
23
+ - trung@tdtran.org
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - .gitignore
32
+ - Gemfile
33
+ - LICENSE
34
+ - README.md
35
+ - Rakefile
36
+ - lib/resque-uniq.rb
37
+ - lib/resque-uniq/version.rb
38
+ - lib/resque/plugins/unique_job.rb
39
+ - resque-uniq.gemspec
40
+ homepage: http://github.com/tdtran/resque-uniq
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ hash: 3
54
+ segments:
55
+ - 0
56
+ version: "0"
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.8.24
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: A Resque plugin to ensure only one job instance is queued or running at a time.
73
+ test_files: []
74
+