resque-fine-grained-locks 1.0.0

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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) Chris Wanstrath, Ray Krueger
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ Software), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ Resque Fine-Grained Locks
2
+ ===========
3
+
4
+ A [Resque][rq] plugin. Requires Resque 1.7.0.
5
+
6
+ If you want only one instance of your job queued at a time, extend it
7
+ with Resque::Plugins::QueueLock. You will be able to enqueue another
8
+ job if one is currently executing.
9
+
10
+ For example:
11
+
12
+ require 'resque/plugins/queue_lock'
13
+
14
+ class UpdateNetworkGraph
15
+ extend Resque::Plugins::QueueLock
16
+
17
+ def self.perform(repo_id)
18
+ heavy_lifting
19
+ end
20
+ end
21
+
22
+ If you want only one instance of your job to execute at a time, extend
23
+ it with Resque::Plugins::ExecutionLock. You can optionally use
24
+ resque-retry if you want to retry locked jobs without them failing.
25
+
26
+ For example:
27
+
28
+ require 'resque/plugins/execution_lock'
29
+
30
+ class UpdateNetworkGraph
31
+ extend Resque::Plugins::Retry
32
+ extend Resque::Plugins::ExecutionLock
33
+
34
+ @retry_exceptions = [
35
+ Resque::Plugins::ExecutionLock::JobIsLocked
36
+ ]
37
+
38
+ def self.perform(repo_id)
39
+ heavy_lifting
40
+ end
41
+ end
42
+
43
+ And in your resque initializer (config/resque.rb):
44
+
45
+ require 'resque/failure/multiple'
46
+
47
+ Resque::Failure::MultipleWithRetrySuppression.classes = [Resque::Plugins::ExecutionLock::JobIsLocked]
48
+ Resque::Failure.backend = Resque::Failure::MultipleWithRetrySuppression
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require 'rake/testtask'
2
+ require 'rdoc/task'
3
+
4
+ def command?(command)
5
+ system("type #{command} > /dev/null")
6
+ end
7
+
8
+ #
9
+ # Tests
10
+ #
11
+
12
+ task :default => :test
13
+
14
+ if command? :turn
15
+ desc "Run tests"
16
+ task :test do
17
+ suffix = "-n #{ENV['TEST']}" if ENV['TEST']
18
+ sh "turn test/*.rb #{suffix}"
19
+ end
20
+ else
21
+ Rake::TestTask.new do |t|
22
+ t.libs << 'lib'
23
+ t.pattern = 'test/**/*_test.rb'
24
+ t.verbose = false
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ module Resque
2
+ module Plugins
3
+ module ExecutionLock
4
+ class JobIsLocked < RuntimeError; end
5
+
6
+ def execution_lock(*args)
7
+ "execution_lock:#{name}-#{args.to_s}"
8
+ end
9
+
10
+ def before_perform_execution_lock(*args)
11
+ raise JobIsLocked unless Resque.redis.setnx(execution_lock(*args), true)
12
+ end
13
+
14
+ def around_perform_execution_lock(*args)
15
+ begin
16
+ yield
17
+ ensure
18
+ Resque.redis.del(execution_lock(*args))
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,17 @@
1
+ module Resque
2
+ module Plugins
3
+ module QueueLock
4
+ def queue_lock(*args)
5
+ "queue_lock:#{name}-#{args.to_s}"
6
+ end
7
+
8
+ def before_enqueue_queue_lock(*args)
9
+ Resque.redis.setnx(queue_lock(*args), true)
10
+ end
11
+
12
+ def before_perform_queue_lock(*args)
13
+ Resque.redis.del(queue_lock(*args))
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,84 @@
1
+ require 'test/unit'
2
+ require 'resque'
3
+ require 'resque/plugins/execution_lock'
4
+
5
+ $execution_lock_do = nil
6
+ $execution_lock_results = nil
7
+
8
+ class ExecutionLockTestJob
9
+ extend Resque::Plugins::ExecutionLock
10
+ @queue = :execution_lock_test
11
+
12
+ def self.perform(success_type)
13
+ if $execution_lock_do
14
+ block, $execution_lock_do = $execution_lock_do, nil
15
+ block.call
16
+ end
17
+
18
+ case success_type.to_sym
19
+ when :success
20
+ $execution_lock_results << :success
21
+ when :fail
22
+ raise RuntimeError, "Purposeful fail!"
23
+ end
24
+ end
25
+ end
26
+
27
+ class ExecutionLockTest < Test::Unit::TestCase
28
+ def setup
29
+ Resque.redis.del('queue:execution_lock_test')
30
+ Resque.redis.del(ExecutionLockTestJob.execution_lock('success'))
31
+ Resque.redis.del(ExecutionLockTestJob.execution_lock('fail'))
32
+
33
+ $execution_lock_do = nil
34
+ $execution_lock_results = []
35
+ end
36
+
37
+ def perform_job(success_type, &block)
38
+ $execution_lock_do = block if block_given?
39
+ payload = {"class" => "ExecutionLockTestJob", "args" => success_type.to_s}
40
+ Resque::Job.new(:execution_lock_test, payload).perform
41
+ end
42
+
43
+ def test_lint
44
+ assert_nothing_raised do
45
+ Resque::Plugin.lint(Resque::Plugins::ExecutionLock)
46
+ end
47
+ end
48
+
49
+ def test_multiple_jobs_can_run_sequentially
50
+ perform_job(:success)
51
+ assert_equal [:success], $execution_lock_results
52
+
53
+ perform_job(:success)
54
+ assert_equal [:success, :success], $execution_lock_results
55
+ end
56
+
57
+ def test_subsequent_jobs_can_run_after_a_job_failure
58
+ assert_raise RuntimeError do
59
+ perform_job(:fail)
60
+ end
61
+
62
+ perform_job(:success)
63
+ assert_equal [:success], $execution_lock_results
64
+ end
65
+
66
+ def test_multiple_jobs_cannot_run_simultaneously
67
+ perform_job(:success) do
68
+ assert_raise Resque::Plugins::ExecutionLock::JobIsLocked do
69
+ perform_job(:success)
70
+ end
71
+ end
72
+ assert_equal [:success], $execution_lock_results
73
+ end
74
+
75
+ def test_locked_job_does_not_clear_lock
76
+ perform_job(:success) do
77
+ assert_raise Resque::Plugins::ExecutionLock::JobIsLocked do
78
+ perform_job(:success)
79
+ end
80
+
81
+ assert Resque.redis.exists(ExecutionLockTestJob.execution_lock('success'))
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,40 @@
1
+ require 'test/unit'
2
+ require 'resque'
3
+ require 'resque/plugins/queue_lock'
4
+
5
+ $counter = 0
6
+
7
+ class QueueLockTest < Test::Unit::TestCase
8
+ class Job
9
+ extend Resque::Plugins::QueueLock
10
+ @queue = :lock_test
11
+
12
+ def self.perform
13
+ raise "Woah woah woah, that wasn't supposed to happen"
14
+ end
15
+ end
16
+
17
+ def setup
18
+ Resque.redis.del('queue:lock_test')
19
+ Resque.redis.del(Job.queue_lock)
20
+ end
21
+
22
+ def test_lint
23
+ assert_nothing_raised do
24
+ Resque::Plugin.lint(Resque::Plugins::QueueLock)
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_lock
36
+ 3.times { Resque.enqueue(Job) }
37
+
38
+ assert_equal 1, Resque.redis.llen('queue:lock_test')
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resque-fine-grained-locks
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Efficiency 2.0
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-02-02 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: |+
22
+ A Resque plugin. If you want only one instance of your job
23
+ queued or executing at a time, extend it with Resque::Jobs::QueueLock
24
+ and/or Resque::Jobs::ExecutionLock.
25
+
26
+ email: tech@efficiency20.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - README.md
35
+ - Rakefile
36
+ - LICENSE
37
+ - lib/resque/plugins/execution_lock.rb
38
+ - lib/resque/plugins/queue_lock.rb
39
+ - test/execution_lock_test.rb
40
+ - test/queue_lock_test.rb
41
+ homepage: http://github.com/efficiency20/resque-fine-grained-locks
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ hash: 3
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.8.15
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: Resque plugins for ensuring only one instance of your job is queued and/or executing at a time.
74
+ test_files: []
75
+