resque-workers-lock 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +22 -0
- data/README.md +48 -0
- data/Rakefile +24 -0
- data/lib/resque/plugins/workers/lock.rb +34 -0
- data/test/lock_test.rb +44 -0
- metadata +102 -0
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Bart Olsthoorn, website: bartolsthoorn.nl
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Resque Workers Lock
|
2
|
+
This is a [resque](https://github.com/defunkt/resque) plugin inspired by [resque-lock](https://github.com/defunkt/resque-lock) and requires Resque 1.7.0.
|
3
|
+
|
4
|
+
``` ruby
|
5
|
+
gem 'resque-workers-lock'
|
6
|
+
```
|
7
|
+
|
8
|
+
## What does it do?
|
9
|
+
If resque jobs have the same lock applied this means that those jobs cannot be processed simultaneously by two or more workers.
|
10
|
+
|
11
|
+
## What is the default lock?
|
12
|
+
By default the lock is the instance name + arguments (just like the classic resque-lock). Override this lock to lock on specific arguments.
|
13
|
+
|
14
|
+
## How does it differ from resque-lock?
|
15
|
+
Resque-lock will not let you queue jobs when you locked them. Resque-workers-lock locks on a workers-level and will requeue the locked jobs. Resque workers lock will not prevent you to queue jobs. If a worker takes on a job that is already being processed by another worker it will put the job back up in the queue!
|
16
|
+
|
17
|
+
## Example
|
18
|
+
This example shows how you can use the workers-lock to prevent two jobs with the same domain to be processed simultaneously.
|
19
|
+
``` ruby
|
20
|
+
require 'resque/plugins/workers/lock'
|
21
|
+
|
22
|
+
class Scraper
|
23
|
+
extend Resque::Plugins::Workers::Lock
|
24
|
+
|
25
|
+
def self.lock(domain)
|
26
|
+
return domain
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.perform(domain)
|
30
|
+
# do the work
|
31
|
+
end
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
## Requeue loop
|
36
|
+
When a job is requeue'ed there is a small delay (1 second by default) before the worker places the job actually back in the queue. Let's say you have two jobs left, and one job is taking 15 seconds on the first worker and the other similar job is being blocked by the second worker. The second worker will continuously try to put the job back in the queue and it will try to process it again (racing for 15 seconds untill the other job has finished). This only happens when there are no other (not locked) jobs in the queue.
|
37
|
+
|
38
|
+
To overwrite this delay in your class:
|
39
|
+
``` ruby
|
40
|
+
def self.requeue_perform_delay
|
41
|
+
5.0
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
Please note that setting this value to 5 seconds will keep the worker idle for 5 seconds when the job is locked.
|
46
|
+
|
47
|
+
## Possibilities to prevent the loop
|
48
|
+
Do a delayed resque (re)queue. However, this will have approximately the same results and will require a large extra chunk of code and rake configurations.
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
require 'rdoc/task'
|
3
|
+
|
4
|
+
def command?(command)
|
5
|
+
system("type #{command} > /dev/null")
|
6
|
+
end
|
7
|
+
|
8
|
+
# Tests
|
9
|
+
|
10
|
+
task :default => :test
|
11
|
+
|
12
|
+
if command? :turn
|
13
|
+
desc "Run tests"
|
14
|
+
task :test do
|
15
|
+
suffix = "-n #{ENV['TEST']}" if ENV['TEST']
|
16
|
+
sh "turn test/*.rb #{suffix}"
|
17
|
+
end
|
18
|
+
else
|
19
|
+
Rake::TestTask.new do |t|
|
20
|
+
t.libs << 'lib'
|
21
|
+
t.pattern = 'test/**/*_test.rb'
|
22
|
+
t.verbose = false
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Resque
|
2
|
+
module Plugins
|
3
|
+
module Workers
|
4
|
+
module Lock
|
5
|
+
# Override in your job to control the lock key.
|
6
|
+
def lock(*args)
|
7
|
+
"lock:#{name}-#{args.to_s}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def requeue_perform_delay
|
11
|
+
1.0
|
12
|
+
end
|
13
|
+
|
14
|
+
def before_perform_lock(*args)
|
15
|
+
nx = Resque.redis.setnx(lock(*args), true)
|
16
|
+
if nx == false
|
17
|
+
sleep(requeue_perform_delay)
|
18
|
+
Resque.enqueue(self, *args)
|
19
|
+
raise Resque::Job::DontPerform
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def around_perform_lock(*args)
|
24
|
+
begin
|
25
|
+
yield
|
26
|
+
ensure
|
27
|
+
# Clear the lock. (even with errors)
|
28
|
+
Resque.redis.del(lock(*args))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/test/lock_test.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'resque'
|
3
|
+
require 'resque/plugins/workers/lock'
|
4
|
+
|
5
|
+
$counter = 0
|
6
|
+
|
7
|
+
class LockTest < Test::Unit::TestCase
|
8
|
+
class Job
|
9
|
+
extend Resque::Plugins::Workers::Lock
|
10
|
+
@queue = :lock_test
|
11
|
+
|
12
|
+
def self.perform
|
13
|
+
raise "This should not have not happened"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup
|
18
|
+
Resque.redis.del('queue:lock_test')
|
19
|
+
Resque.redis.del(Job.lock)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_lint
|
23
|
+
assert_nothing_raised do
|
24
|
+
Resque::Plugin.lint(Resque::Plugins::Workers::Lock)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_version
|
29
|
+
major, minor, patch = Resque::Version.split('.')
|
30
|
+
assert_equal 1, major.to_i
|
31
|
+
assert minor.to_i >= 17
|
32
|
+
assert Resque::Plugin.respond_to?(:before_enqueue_hooks)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_enqueue
|
36
|
+
3.times { Resque.enqueue(Job) }
|
37
|
+
|
38
|
+
assert_equal 3, Resque.redis.llen('queue:lock_test')
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_lock
|
42
|
+
# TODO: test that two workers are not processing two jobs
|
43
|
+
end
|
44
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: resque-workers-lock
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bart Olsthoorn
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-24 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: resque
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: turn
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: ! "A Resque plugin. If you want to prevent specific jobs to be processed
|
63
|
+
simultaneously, \nextend it with this module. It locks on the first argument in
|
64
|
+
the perform method.\n\nFor example:\n\n class Scraper\n extend Resque::Plugins::Workers::Lock\n\n
|
65
|
+
\ def self.lock(domain)\n return domain\n end\n\n def
|
66
|
+
self.perform(domain)\n # do the work\n end\n end\n"
|
67
|
+
email: bartolsthoorn@gmail.com
|
68
|
+
executables: []
|
69
|
+
extensions: []
|
70
|
+
extra_rdoc_files: []
|
71
|
+
files:
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- LICENSE
|
75
|
+
- lib/resque/plugins/workers/lock.rb
|
76
|
+
- test/lock_test.rb
|
77
|
+
homepage: http://github.com/bartolsthoorn/resque-workers-lock
|
78
|
+
licenses: []
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 1.8.24
|
98
|
+
signing_key:
|
99
|
+
specification_version: 3
|
100
|
+
summary: Resque plugin, prevent specific jobs to be processed simultaneously by multiple
|
101
|
+
workers.
|
102
|
+
test_files: []
|