resque-workers-lock 1.9 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +4 -2
- data/lib/resque/plugins/workers/lock.rb +21 -5
- data/test/lock_mkey_test.rb +137 -0
- data/test/unique_job_mkey.rb +26 -0
- metadata +19 -24
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 46137f8bebbbe0ced3b3c8bc255ecdc72fb718e3
|
4
|
+
data.tar.gz: d2185948f051bbc073ae22a1f41782f985dd3ad5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3f0aa3cc230357b85e8ba84e4fb8230dceb3bee78d39c7eb764dd93c35267b587f2f39f38efe83561fff19e027c988ed8c6ab7071d9cbd2ff4ac06b414884251
|
7
|
+
data.tar.gz: 4f4920fcfc473b25589cfa07e75d7cd5c473fa49baad5827f0f94a88f93e7d031e07e1053d5e698ef7e9aa46eb36cf7fe0e81d83ed851d464d24ccfd9bacc076
|
data/README.md
CHANGED
@@ -5,13 +5,14 @@ This is a [resque](https://github.com/defunkt/resque) plugin inspired by [resque
|
|
5
5
|
gem 'resque-workers-lock'
|
6
6
|
```
|
7
7
|
|
8
|
-
**Important notice** - As of this gem version 1.7, Resque Workers Lock no longer includes
|
8
|
+
**Important notice** - As of this gem version 1.7, Resque Workers Lock no longer includes an enqueue lock but focusses solely on a workers lock. If you're also looking for enqueue lock functionality, just add [resque-lock](https://github.com/defunkt/resque-lock) or another plugin in the mix.
|
9
9
|
|
10
10
|
## What does it do?
|
11
|
-
If resque jobs have the same lock applied this means that those jobs cannot be processed simultaneously by two or more workers. When this situation occurs the second job gets pushed back to the queue.
|
11
|
+
If resque jobs have the same lock(s) applied this means that those jobs cannot be processed simultaneously by two or more workers. When this situation occurs the second job gets pushed back to the queue.
|
12
12
|
|
13
13
|
## What is the default lock?
|
14
14
|
By default the lock is the instance name + arguments (just like the classic resque-lock). Override this lock to lock on specific arguments.
|
15
|
+
You can specify only one lock or an array of locks. A job with an array of locks will only be processed when all the (individual) locks are not being processed by workers.
|
15
16
|
|
16
17
|
## How does it differ from resque-lock?
|
17
18
|
Resque-lock will not let you enqueue jobs when you locked them. Resque-workers-lock locks on a workers-level and will requeue the locked 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!
|
@@ -66,3 +67,4 @@ Do a delayed resque (re)queue. However, this will have approximately the same re
|
|
66
67
|
## Authors/Contributors
|
67
68
|
[nicholaides](https://github.com/nicholaides)
|
68
69
|
[jgarber](https://github.com/jgarber)
|
70
|
+
[mohawke](https://github.com/mohawke)
|
@@ -19,13 +19,21 @@ module Resque
|
|
19
19
|
3600
|
20
20
|
end
|
21
21
|
|
22
|
-
# Override in your job to control the workers lock key.
|
22
|
+
# Override in your job to control the workers lock key(s).
|
23
23
|
def lock_workers(*args)
|
24
24
|
"#{name}-#{args.to_s}"
|
25
25
|
end
|
26
26
|
|
27
27
|
def get_lock_workers(*args)
|
28
|
-
|
28
|
+
lock_result = lock_workers(*args)
|
29
|
+
|
30
|
+
if lock_result.kind_of?(Array)
|
31
|
+
lock_result.map do |lock|
|
32
|
+
"workerslock:#{lock}"
|
33
|
+
end
|
34
|
+
else
|
35
|
+
["workerslock:#{lock_result}"]
|
36
|
+
end
|
29
37
|
end
|
30
38
|
|
31
39
|
# Override in your job to change the perform requeue delay
|
@@ -37,8 +45,12 @@ module Resque
|
|
37
45
|
# If it raises Resque::Job::DontPerform, the job is aborted.
|
38
46
|
def before_perform_workers_lock(*args)
|
39
47
|
if lock_workers(*args)
|
40
|
-
|
41
|
-
|
48
|
+
lock_result = get_lock_workers(*args)
|
49
|
+
|
50
|
+
if Resque.redis.msetnx lock_result.zip([true]*lock_result.size).flatten
|
51
|
+
lock_result.each do |lock|
|
52
|
+
Resque.redis.expire(lock, worker_lock_timeout(*args))
|
53
|
+
end
|
42
54
|
else
|
43
55
|
sleep(requeue_perform_delay)
|
44
56
|
Resque.enqueue(self, *args)
|
@@ -48,7 +60,11 @@ module Resque
|
|
48
60
|
end
|
49
61
|
|
50
62
|
def clear_workers_lock(*args)
|
51
|
-
|
63
|
+
lock_result = get_lock_workers(*args)
|
64
|
+
|
65
|
+
lock_result.each do |lock|
|
66
|
+
Resque.redis.del(lock)
|
67
|
+
end
|
52
68
|
end
|
53
69
|
|
54
70
|
def around_perform_workers_lock(*args)
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.expand_path('../../lib/resque/plugins/workers/lock', __FILE__)
|
3
|
+
require 'tempfile'
|
4
|
+
require 'timeout'
|
5
|
+
|
6
|
+
require_relative 'unique_job_mkey'
|
7
|
+
|
8
|
+
class LockMKeyTest < Test::Unit::TestCase
|
9
|
+
|
10
|
+
|
11
|
+
def setup
|
12
|
+
Resque.redis.keys('UniqueJobMkey*').each do |key|
|
13
|
+
Resque.redis.del key
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_lint
|
18
|
+
assert_nothing_raised do
|
19
|
+
Resque::Plugin.lint(Resque::Plugins::Workers::Lock)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_workers_dont_work_simultaneously
|
24
|
+
assert_locking_works_with jobs: 2, workers: 2
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_worker_locks_timeout
|
28
|
+
output_file = Tempfile.new 'output_file'
|
29
|
+
|
30
|
+
Resque.enqueue UniqueJobMKey, job: 'interrupted-job', output_file: output_file.path, sleep: 1000
|
31
|
+
|
32
|
+
worker_pid = start_worker
|
33
|
+
wait_until(10){ lock_has_been_acquired }
|
34
|
+
kill_worker(worker_pid)
|
35
|
+
|
36
|
+
Resque.enqueue UniqueJobMKey, job: 'completing-job', output_file: output_file.path, sleep: 0
|
37
|
+
process_jobs workers: 1, timeout: UniqueJobMKey.worker_lock_timeout + 2
|
38
|
+
|
39
|
+
lines = File.readlines(output_file).map(&:chomp)
|
40
|
+
assert_equal ['starting interrupted-job', 'starting completing-job', 'finished completing-job'], lines
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def lock_has_been_acquired
|
46
|
+
Resque.redis.exists(UniqueJobMKey.get_lock_workers.first)
|
47
|
+
end
|
48
|
+
|
49
|
+
def kill_worker(worker_pid)
|
50
|
+
Process.kill("TERM", worker_pid)
|
51
|
+
Process.waitpid(worker_pid)
|
52
|
+
end
|
53
|
+
|
54
|
+
def start_worker
|
55
|
+
fork.tap do |pid|
|
56
|
+
if !pid
|
57
|
+
worker = Resque::Worker.new('*')
|
58
|
+
worker.term_child = true
|
59
|
+
worker.reconnect
|
60
|
+
worker.work(0.5)
|
61
|
+
exit!
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def assert_worker_lock_exists(job_class, *args)
|
67
|
+
assert Resque.redis.exists(job_class.get_lock_workers(*args).first, "lock does not exist")
|
68
|
+
end
|
69
|
+
|
70
|
+
def assert_locking_works_with options
|
71
|
+
jobs = (1..options[:jobs]).map{|job| "Job #{job}" }
|
72
|
+
output_file = Tempfile.new 'output_file'
|
73
|
+
|
74
|
+
jobs.each do |job|
|
75
|
+
Resque.enqueue UniqueJobMKey, job: job, output_file: output_file.path
|
76
|
+
end
|
77
|
+
|
78
|
+
process_jobs workers: options[:workers], timeout: 10
|
79
|
+
|
80
|
+
lines = File.readlines(output_file).map(&:chomp)
|
81
|
+
lines.each_slice(2) do |a,b|
|
82
|
+
assert_equal a.split.last,b.split.last, "#{a} was interrupted by #{b}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def process_jobs options
|
87
|
+
with_workers options[:workers] do
|
88
|
+
wait_until(options[:timeout]) do
|
89
|
+
no_busy_workers && no_queued_jobs
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def with_workers n
|
95
|
+
pids = []
|
96
|
+
n.times do
|
97
|
+
if pid = fork
|
98
|
+
pids << pid
|
99
|
+
else
|
100
|
+
pids = [] # Don't kill from child's ensure
|
101
|
+
worker = Resque::Worker.new('*')
|
102
|
+
worker.term_child = true
|
103
|
+
worker.reconnect
|
104
|
+
worker.work(0.5)
|
105
|
+
exit!
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
yield
|
110
|
+
|
111
|
+
ensure
|
112
|
+
pids.each do |pid|
|
113
|
+
Process.kill("QUIT", pid)
|
114
|
+
end
|
115
|
+
|
116
|
+
pids.each do |pid|
|
117
|
+
Process.waitpid(pid)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def no_busy_workers
|
122
|
+
Resque::Worker.working.size == 0
|
123
|
+
end
|
124
|
+
|
125
|
+
def no_queued_jobs
|
126
|
+
Resque.redis.llen("queue:mlock_test") == 0
|
127
|
+
end
|
128
|
+
|
129
|
+
def wait_until(timeout)
|
130
|
+
Timeout::timeout(timeout) do
|
131
|
+
loop do
|
132
|
+
return if yield
|
133
|
+
sleep 1
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path('../../lib/resque/plugins/workers/lock', __FILE__)
|
2
|
+
|
3
|
+
class UniqueJobMKey
|
4
|
+
extend Resque::Plugins::Workers::Lock
|
5
|
+
@queue = :mlock_test
|
6
|
+
|
7
|
+
def self.worker_lock_timeout(*)
|
8
|
+
5
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.lock_workers(*args)
|
12
|
+
[self.name + '1', "#{self.name}#{args.to_s}"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.append_output filename, string
|
16
|
+
File.open(filename, 'a') do |output_file|
|
17
|
+
output_file.puts string
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.perform params
|
22
|
+
append_output params['output_file'], "starting #{params['job']}"
|
23
|
+
sleep(params['sleep'] || 1)
|
24
|
+
append_output params['output_file'], "finished #{params['job']}"
|
25
|
+
end
|
26
|
+
end
|
metadata
CHANGED
@@ -1,90 +1,85 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque-workers-lock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 2.0.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Bart Olsthoorn
|
9
8
|
- Mike Nicholaides
|
10
9
|
- Jason Garber
|
10
|
+
- Tijs Planckaert
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2014-05-30 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: resque
|
18
18
|
requirement: !ruby/object:Gem::Requirement
|
19
|
-
none: false
|
20
19
|
requirements:
|
21
|
-
- -
|
20
|
+
- - ">="
|
22
21
|
- !ruby/object:Gem::Version
|
23
22
|
version: '0'
|
24
23
|
type: :runtime
|
25
24
|
prerelease: false
|
26
25
|
version_requirements: !ruby/object:Gem::Requirement
|
27
|
-
none: false
|
28
26
|
requirements:
|
29
|
-
- -
|
27
|
+
- - ">="
|
30
28
|
- !ruby/object:Gem::Version
|
31
29
|
version: '0'
|
32
30
|
- !ruby/object:Gem::Dependency
|
33
31
|
name: rake
|
34
32
|
requirement: !ruby/object:Gem::Requirement
|
35
|
-
none: false
|
36
33
|
requirements:
|
37
|
-
- -
|
34
|
+
- - ">="
|
38
35
|
- !ruby/object:Gem::Version
|
39
36
|
version: '0'
|
40
37
|
type: :development
|
41
38
|
prerelease: false
|
42
39
|
version_requirements: !ruby/object:Gem::Requirement
|
43
|
-
none: false
|
44
40
|
requirements:
|
45
|
-
- -
|
41
|
+
- - ">="
|
46
42
|
- !ruby/object:Gem::Version
|
47
43
|
version: '0'
|
48
|
-
description:
|
49
|
-
simultaneously by multiple workers.
|
50
|
-
|
44
|
+
description: |
|
45
|
+
A Resque plugin. Two or more jobs with the same lock cannot be processed simultaneously by multiple workers.
|
51
46
|
When this situation occurs the second job gets pushed back to the queue.
|
52
|
-
|
53
|
-
'
|
54
47
|
email: bartolsthoorn@gmail.com
|
55
48
|
executables: []
|
56
49
|
extensions: []
|
57
50
|
extra_rdoc_files: []
|
58
51
|
files:
|
52
|
+
- LICENSE
|
59
53
|
- README.md
|
60
54
|
- Rakefile
|
61
|
-
- LICENSE
|
62
55
|
- lib/resque/plugins/workers/lock.rb
|
56
|
+
- test/lock_mkey_test.rb
|
63
57
|
- test/lock_test.rb
|
64
58
|
- test/unique_job.rb
|
59
|
+
- test/unique_job_mkey.rb
|
65
60
|
homepage: http://github.com/bartolsthoorn/resque-workers-lock
|
66
|
-
licenses:
|
61
|
+
licenses:
|
62
|
+
- MIT
|
63
|
+
metadata: {}
|
67
64
|
post_install_message:
|
68
65
|
rdoc_options: []
|
69
66
|
require_paths:
|
70
67
|
- lib
|
71
68
|
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
-
none: false
|
73
69
|
requirements:
|
74
|
-
- -
|
70
|
+
- - ">="
|
75
71
|
- !ruby/object:Gem::Version
|
76
72
|
version: '0'
|
77
73
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
-
none: false
|
79
74
|
requirements:
|
80
|
-
- -
|
75
|
+
- - ">="
|
81
76
|
- !ruby/object:Gem::Version
|
82
77
|
version: '0'
|
83
78
|
requirements: []
|
84
79
|
rubyforge_project:
|
85
|
-
rubygems_version:
|
80
|
+
rubygems_version: 2.2.2
|
86
81
|
signing_key:
|
87
|
-
specification_version:
|
82
|
+
specification_version: 4
|
88
83
|
summary: Resque plugin, prevent specific jobs to be processed simultaneously by multiple
|
89
84
|
workers.
|
90
85
|
test_files: []
|