resque-lock-timeout 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.md +9 -1
- data/README.md +18 -0
- data/Rakefile +0 -1
- data/lib/resque/plugins/lock_timeout.rb +39 -15
- data/test/lock_test.rb +21 -0
- data/test/redis-test.conf +1 -18
- data/test/test_jobs.rb +22 -0
- metadata +20 -20
data/HISTORY.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## 0.3.0 (2011-07-16)
|
2
|
+
|
3
|
+
* Ability to customize redis connection used for storing locks.
|
4
|
+
(thanks Richie Vos =))
|
5
|
+
* Added Bundler `Gemfile`.
|
6
|
+
* Added abstract stub methods for callback methods:
|
7
|
+
`lock_failed`, `lock_expired_before_release`
|
8
|
+
|
1
9
|
## 0.2.1 (2010-06-16)
|
2
10
|
|
3
11
|
* Relax gemspec dependancies.
|
@@ -5,4 +13,4 @@
|
|
5
13
|
## 0.2.0 (2010-05-05)
|
6
14
|
|
7
15
|
* Initial release as `resque-lock-timeout`, forked from Chris Wanstrath'
|
8
|
-
|
16
|
+
`resque-lock`.
|
data/README.md
CHANGED
@@ -92,6 +92,24 @@ repo_id.
|
|
92
92
|
|
93
93
|
It's lock key would be: `resque-lock-timeout:UpdateNetworkGraph`.
|
94
94
|
|
95
|
+
### Redis Connection Used for Locking
|
96
|
+
|
97
|
+
By default all locks are stored via Resque's redis connection. If you wish to
|
98
|
+
change this you may override `lock_redis`.
|
99
|
+
|
100
|
+
class UpdateNetworkGraph
|
101
|
+
extend Resque::Plugins::LockTimeout
|
102
|
+
@queue = :network_graph
|
103
|
+
|
104
|
+
def self.lock_redis
|
105
|
+
@lock_redis ||= Redis.new
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.perform(repo_id)
|
109
|
+
heavy_lifting
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
95
113
|
### Callbacks
|
96
114
|
|
97
115
|
Several callbacks are available to override and implement your own logic, e.g.
|
data/Rakefile
CHANGED
@@ -43,6 +43,16 @@ module Resque
|
|
43
43
|
args.join('-')
|
44
44
|
end
|
45
45
|
|
46
|
+
# Override to fully control the redis object used for storing
|
47
|
+
# the locks.
|
48
|
+
#
|
49
|
+
# The default is Resque.redis
|
50
|
+
#
|
51
|
+
# @return [Redis] redis object
|
52
|
+
def lock_redis
|
53
|
+
Resque.redis
|
54
|
+
end
|
55
|
+
|
46
56
|
# Override to fully control the lock key used. It is passed
|
47
57
|
# the job arguments.
|
48
58
|
#
|
@@ -62,6 +72,27 @@ module Resque
|
|
62
72
|
@lock_timeout ||= 0
|
63
73
|
end
|
64
74
|
|
75
|
+
# Convenience method, not used internally.
|
76
|
+
#
|
77
|
+
# @return [Boolean] true if the job is locked by someone
|
78
|
+
def locked?(*args)
|
79
|
+
lock_redis.exists(redis_lock_key(*args))
|
80
|
+
end
|
81
|
+
|
82
|
+
# @abstract
|
83
|
+
# Hook method; called when a were unable to aquire the lock.
|
84
|
+
#
|
85
|
+
# @param [Array] args job arguments
|
86
|
+
def lock_failed(*args)
|
87
|
+
end
|
88
|
+
|
89
|
+
# @abstract
|
90
|
+
# Hook method; called when the lock expired before we released it.
|
91
|
+
#
|
92
|
+
# @param [Array] args job arguments
|
93
|
+
def lock_expired_before_release(*args)
|
94
|
+
end
|
95
|
+
|
65
96
|
# Try to acquire a lock.
|
66
97
|
#
|
67
98
|
# * Returns false; when unable to acquire the lock.
|
@@ -76,13 +107,13 @@ module Resque
|
|
76
107
|
|
77
108
|
unless lock_timeout > 0
|
78
109
|
# Acquire without using a timeout.
|
79
|
-
acquired = true if
|
110
|
+
acquired = true if lock_redis.setnx(lock_key, true)
|
80
111
|
else
|
81
112
|
# Acquire using the timeout algorithm.
|
82
113
|
acquired, lock_until = acquire_lock_algorithm!(lock_key)
|
83
114
|
end
|
84
115
|
|
85
|
-
lock_failed(*args) if !acquired
|
116
|
+
lock_failed(*args) if !acquired
|
86
117
|
lock_until && acquired ? lock_until : acquired
|
87
118
|
end
|
88
119
|
|
@@ -94,18 +125,18 @@ module Resque
|
|
94
125
|
lock_until = now + lock_timeout
|
95
126
|
acquired = false
|
96
127
|
|
97
|
-
return [true, lock_until] if
|
128
|
+
return [true, lock_until] if lock_redis.setnx(lock_key, lock_until)
|
98
129
|
# Can't acquire the lock, see if it has expired.
|
99
|
-
lock_expiration =
|
130
|
+
lock_expiration = lock_redis.get(lock_key)
|
100
131
|
if lock_expiration && lock_expiration.to_i < now
|
101
132
|
# expired, try to acquire.
|
102
|
-
lock_expiration =
|
133
|
+
lock_expiration = lock_redis.getset(lock_key, lock_until)
|
103
134
|
if lock_expiration.nil? || lock_expiration.to_i < now
|
104
135
|
acquired = true
|
105
136
|
end
|
106
137
|
else
|
107
138
|
# Try once more...
|
108
|
-
acquired = true if
|
139
|
+
acquired = true if lock_redis.setnx(lock_key, lock_until)
|
109
140
|
end
|
110
141
|
|
111
142
|
[acquired, lock_until]
|
@@ -113,14 +144,7 @@ module Resque
|
|
113
144
|
|
114
145
|
# Release the lock.
|
115
146
|
def release_lock!(*args)
|
116
|
-
|
117
|
-
end
|
118
|
-
|
119
|
-
# Convenience method, not used internally.
|
120
|
-
#
|
121
|
-
# @return [Boolean] true if the job is locked by someone
|
122
|
-
def locked?(*args)
|
123
|
-
Resque.redis.exists(redis_lock_key(*args))
|
147
|
+
lock_redis.del(redis_lock_key(*args))
|
124
148
|
end
|
125
149
|
|
126
150
|
# Where the magic happens.
|
@@ -135,7 +159,7 @@ module Resque
|
|
135
159
|
# used, then we need to be more careful before releasing the lock.
|
136
160
|
unless lock_until === true
|
137
161
|
now = Time.now.to_i
|
138
|
-
if lock_until < now
|
162
|
+
if lock_until < now
|
139
163
|
# Eeek! Lock expired before perform finished. Trigger callback.
|
140
164
|
lock_expired_before_release(*args)
|
141
165
|
return # dont relase lock.
|
data/test/lock_test.rb
CHANGED
@@ -105,4 +105,25 @@ class LockTest < Test::Unit::TestCase
|
|
105
105
|
assert_equal true, $lock_expired, 'should be set by callback method'
|
106
106
|
assert_equal false, FastJob.locked?, 'lock should not release'
|
107
107
|
end
|
108
|
+
|
109
|
+
def test_lock_with_specific_redis
|
110
|
+
lock_redis = Redis.new(:host => Resque.redis.client.host,
|
111
|
+
:port => Resque.redis.client.port,
|
112
|
+
:db => 'locks',
|
113
|
+
:threadsafe => true)
|
114
|
+
SpecificRedisJob.lock_redis = lock_redis
|
115
|
+
Resque.enqueue(SpecificRedisJob)
|
116
|
+
|
117
|
+
thread = Thread.new { @worker.process }
|
118
|
+
|
119
|
+
sleep 0.1
|
120
|
+
# this is nil in Resque.redis since we make no attempt to add a resque:
|
121
|
+
# prefix to the key
|
122
|
+
assert_nil Resque.redis.get('specific_redis')
|
123
|
+
assert_not_nil lock_redis.get('specific_redis')
|
124
|
+
|
125
|
+
thread.join
|
126
|
+
assert_nil lock_redis.get('specific_redis')
|
127
|
+
assert_equal 1, $success, 'job should increment success'
|
128
|
+
end
|
108
129
|
end
|
data/test/redis-test.conf
CHANGED
@@ -112,21 +112,4 @@ databases 16
|
|
112
112
|
# Glue small output buffers together in order to send small replies in a
|
113
113
|
# single TCP packet. Uses a bit more CPU but most of the times it is a win
|
114
114
|
# in terms of number of queries per second. Use 'yes' if unsure.
|
115
|
-
glueoutputbuf yes
|
116
|
-
|
117
|
-
# Use object sharing. Can save a lot of memory if you have many common
|
118
|
-
# string in your dataset, but performs lookups against the shared objects
|
119
|
-
# pool so it uses more CPU and can be a bit slower. Usually it's a good
|
120
|
-
# idea.
|
121
|
-
#
|
122
|
-
# When object sharing is enabled (shareobjects yes) you can use
|
123
|
-
# shareobjectspoolsize to control the size of the pool used in order to try
|
124
|
-
# object sharing. A bigger pool size will lead to better sharing capabilities.
|
125
|
-
# In general you want this value to be at least the double of the number of
|
126
|
-
# very common strings you have in your dataset.
|
127
|
-
#
|
128
|
-
# WARNING: object sharing is experimental, don't enable this feature
|
129
|
-
# in production before of Redis 1.0-stable. Still please try this feature in
|
130
|
-
# your development environment so that we can test it better.
|
131
|
-
shareobjects no
|
132
|
-
shareobjectspoolsize 1024
|
115
|
+
glueoutputbuf yes
|
data/test/test_jobs.rb
CHANGED
@@ -59,4 +59,26 @@ class ExpireBeforeReleaseJob
|
|
59
59
|
def self.lock_expired_before_release
|
60
60
|
$lock_expired = true
|
61
61
|
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class SpecificRedisJob
|
65
|
+
extend Resque::Plugins::LockTimeout
|
66
|
+
@queue = :test
|
67
|
+
|
68
|
+
def self.lock_redis
|
69
|
+
@redis
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.lock_redis=(redis)
|
73
|
+
@redis = redis
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.redis_lock_key
|
77
|
+
'specific_redis'
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.perform
|
81
|
+
$success += 1
|
82
|
+
sleep 0.2
|
83
|
+
end
|
62
84
|
end
|
metadata
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque-lock-timeout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 2
|
8
|
-
- 1
|
9
|
-
version: 0.2.1
|
4
|
+
prerelease:
|
5
|
+
version: 0.3.0
|
10
6
|
platform: ruby
|
11
7
|
authors:
|
12
8
|
- Luke Antins
|
@@ -16,20 +12,17 @@ autorequire:
|
|
16
12
|
bindir: bin
|
17
13
|
cert_chain: []
|
18
14
|
|
19
|
-
date:
|
15
|
+
date: 2011-07-16 00:00:00 +01:00
|
20
16
|
default_executable:
|
21
17
|
dependencies:
|
22
18
|
- !ruby/object:Gem::Dependency
|
23
19
|
name: resque
|
24
20
|
prerelease: false
|
25
21
|
requirement: &id001 !ruby/object:Gem::Requirement
|
22
|
+
none: false
|
26
23
|
requirements:
|
27
24
|
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
segments:
|
30
|
-
- 1
|
31
|
-
- 8
|
32
|
-
- 0
|
33
26
|
version: 1.8.0
|
34
27
|
type: :runtime
|
35
28
|
version_requirements: *id001
|
@@ -37,11 +30,10 @@ dependencies:
|
|
37
30
|
name: turn
|
38
31
|
prerelease: false
|
39
32
|
requirement: &id002 !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
40
34
|
requirements:
|
41
35
|
- - ">="
|
42
36
|
- !ruby/object:Gem::Version
|
43
|
-
segments:
|
44
|
-
- 0
|
45
37
|
version: "0"
|
46
38
|
type: :development
|
47
39
|
version_requirements: *id002
|
@@ -49,14 +41,24 @@ dependencies:
|
|
49
41
|
name: yard
|
50
42
|
prerelease: false
|
51
43
|
requirement: &id003 !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
52
45
|
requirements:
|
53
46
|
- - ">="
|
54
47
|
- !ruby/object:Gem::Version
|
55
|
-
segments:
|
56
|
-
- 0
|
57
48
|
version: "0"
|
58
49
|
type: :development
|
59
50
|
version_requirements: *id003
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: rdiscount
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
type: :development
|
61
|
+
version_requirements: *id004
|
60
62
|
description: " A Resque plugin. Adds locking, with optional timeout/deadlock handling to\n resque jobs.\n\n Using a `lock_timeout` allows you to re-aquire the lock should your worker\n fail, crash, or is otherwise unable to relase the lock.\n \n i.e. Your server unexpectedly looses power. Very handy for jobs that are\n recurring or may be retried.\n"
|
61
63
|
email: luke@lividpenguin.com
|
62
64
|
executables: []
|
@@ -86,23 +88,21 @@ rdoc_options: []
|
|
86
88
|
require_paths:
|
87
89
|
- lib
|
88
90
|
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
89
92
|
requirements:
|
90
93
|
- - ">="
|
91
94
|
- !ruby/object:Gem::Version
|
92
|
-
segments:
|
93
|
-
- 0
|
94
95
|
version: "0"
|
95
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
96
98
|
requirements:
|
97
99
|
- - ">="
|
98
100
|
- !ruby/object:Gem::Version
|
99
|
-
segments:
|
100
|
-
- 0
|
101
101
|
version: "0"
|
102
102
|
requirements: []
|
103
103
|
|
104
104
|
rubyforge_project:
|
105
|
-
rubygems_version: 1.
|
105
|
+
rubygems_version: 1.6.2
|
106
106
|
signing_key:
|
107
107
|
specification_version: 3
|
108
108
|
summary: A Resque plugin adding locking, with optional timeout/deadlock handling to resque jobs.
|