resque-lock-timeout 0.3.3 → 0.4.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/HISTORY.md CHANGED
@@ -1,7 +1,11 @@
1
+ ## 0.4.0 (2012-11-09)
2
+
3
+ * Add `@loner` boolean option to prevent job being enqueued if already
4
+ running/enqueued. (Thanks to @ssaunier)
5
+
1
6
  ## 0.3.3 (2012-03-09)
2
- ## 0.3.2 (2012-03-09)
3
7
 
4
- * Release changes nothing of importance, tested against v1.20.0 of resque.
8
+ * Tested against v1.20.0 of resque.
5
9
 
6
10
  ## 0.3.1 (2011-07-16)
7
11
 
@@ -23,4 +27,4 @@
23
27
  ## 0.2.0 (2010-05-05)
24
28
 
25
29
  * Initial release as `resque-lock-timeout`, forked from Chris Wanstrath'
26
- `resque-lock`.
30
+ `resque-lock`.
data/README.md CHANGED
@@ -6,7 +6,7 @@ A [Resque][rq] plugin. Requires Resque >= v1.8.0.
6
6
  resque-lock-timeout adds locking, with optional timeout/deadlock handling to
7
7
  resque jobs.
8
8
 
9
- Using a `lock_timeout` allows you to re-aquire the lock should your worker
9
+ Using a `lock_timeout` allows you to re-acquire the lock should your worker
10
10
  fail, crash, or is otherwise unable to relase the lock. **i.e.** Your server
11
11
  unexpectedly looses power. Very handy for jobs that are recurring or may be
12
12
  retried.
@@ -37,7 +37,23 @@ Default behaviour...
37
37
 
38
38
  Please see below for more information about the identifer/lock key.
39
39
 
40
- ### With Lock Expiry/Timeout
40
+ ### Enqueued Exclusivity (Loner Option)
41
+
42
+ Setting the `@loner` boolean to `true` will ensure the job is not enqueued if
43
+ the job (identified by the `identifier` method) is already running/enqueued.
44
+
45
+ class LonelyJob
46
+ extend Resque::Plugins::LockTimeout
47
+ @queue = :loners
48
+
49
+ @loner = true
50
+
51
+ def self.perform(repo_id)
52
+ heavy_lifting
53
+ end
54
+ end
55
+
56
+ ### Lock Expiry/Timeout
41
57
 
42
58
  The locking algorithm used can be found in the [Redis SETNX][redis-setnx]
43
59
  documentation.
@@ -158,11 +174,19 @@ Several callbacks are available to override and implement your own logic, e.g.
158
174
  # Lock may be held for upto an hour.
159
175
  @lock_timeout = 3600
160
176
 
177
+ # No same job get enqueued if one already running/enqueued
178
+ @loner = true
179
+
161
180
  # Job failed to acquire lock. You may implement retry or other logic.
162
181
  def self.lock_failed(repo_id)
163
182
  raise LockFailed
164
183
  end
165
184
 
185
+ # Unable to enqueue job because its running or already enqueued.
186
+ def self.loner_enqueue_failed(repo_id)
187
+ raise EnqueueFailed
188
+ end
189
+
166
190
  # Job has complete; but the lock expired before we could relase it.
167
191
  # The lock wasn't released; as its *possible* the lock is now held
168
192
  # by another job.
@@ -3,32 +3,51 @@ module Resque
3
3
  # If you want only one instance of your job running at a time,
4
4
  # extend it with this module:
5
5
  #
6
- # require 'resque-lock-timeout'
6
+ # require 'resque-lock-timeout'
7
7
  #
8
- # class UpdateNetworkGraph
9
- # extend Resque::Plugins::LockTimeout
10
- # @queue = :network_graph
8
+ # class UpdateNetworkGraph
9
+ # extend Resque::Plugins::LockTimeout
10
+ # @queue = :network_graph
11
11
  #
12
- # def self.perform(repo_id)
13
- # heavy_lifting
12
+ # def self.perform(repo_id)
13
+ # heavy_lifting
14
+ # end
14
15
  # end
15
- # end
16
16
  #
17
- # If you wish to limit the durati on a lock may be held for, you can
17
+ # If you wish to limit the duration a lock may be held for, you can
18
18
  # set/override `lock_timeout`. e.g.
19
19
  #
20
- # class UpdateNetworkGraph
21
- # extend Resque::Plugins::LockTimeout
22
- # @queue = :network_graph
20
+ # class UpdateNetworkGraph
21
+ # extend Resque::Plugins::LockTimeout
22
+ # @queue = :network_graph
23
23
  #
24
- # # lock may be held for upto an hour.
25
- # @lock_timeout = 3600
24
+ # # lock may be held for upto an hour.
25
+ # @lock_timeout = 3600
26
26
  #
27
- # def self.perform(repo_id)
28
- # heavy_lifting
27
+ # def self.perform(repo_id)
28
+ # heavy_lifting
29
+ # end
29
30
  # end
30
- # end
31
31
  #
32
+ # If you wish that only one instance of the job defined by #identifier may be
33
+ # enqueued or running, you can set/override `loner`. e.g.
34
+ #
35
+ # class PdfExport
36
+ # extend Resque::Plugins::LockTimeout
37
+ # @queue = :exports
38
+ #
39
+ # # only one job can be running/enqueued at a time. For instance a button
40
+ # # to run a PDF export. If the user clicks several times on it, enqueue
41
+ # # the job if and only if
42
+ # # - the same export is not currently running
43
+ # # - the same export is not currently queued.
44
+ # # ('same' being defined by `identifier`)
45
+ # @loner = true
46
+ #
47
+ # def self.perform(repo_id)
48
+ # heavy_lifting
49
+ # end
50
+ # end
32
51
  module LockTimeout
33
52
  # @abstract You may override to implement a custom identifier,
34
53
  # you should consider doing this if your job arguments
@@ -56,8 +75,7 @@ module Resque
56
75
  # Override to fully control the lock key used. It is passed
57
76
  # the job arguments.
58
77
  #
59
- # The default looks like this:
60
- # `resque-lock-timeout:<class name>:<identifier>`
78
+ # The default looks like this: `lock:<class name>:<identifier>`
61
79
  #
62
80
  # @param [Array] args job arguments
63
81
  # @return [String] redis key
@@ -65,6 +83,16 @@ module Resque
65
83
  ['lock', name, identifier(*args)].compact.join(':')
66
84
  end
67
85
 
86
+ # Builds lock key used by `@loner` option. Passed job arguments.
87
+ #
88
+ # The default looks like this: `loner:lock:<class name>:<identifier>`
89
+ #
90
+ # @param [Array] args job arguments
91
+ # @return [String] redis key
92
+ def redis_loner_lock_key(*args)
93
+ ['loner', redis_lock_key(*args)].compact.join(':')
94
+ end
95
+
68
96
  # Number of seconds the lock may be held for.
69
97
  # A value of 0 or below will lock without a timeout.
70
98
  #
@@ -74,6 +102,14 @@ module Resque
74
102
  @lock_timeout ||= 0
75
103
  end
76
104
 
105
+ # Whether one instance of the job should be running or enqueued.
106
+ #
107
+ # @param [Array] args job arguments
108
+ # @return [TrueClass || FalseClass]
109
+ def loner(*args)
110
+ @loner ||= false
111
+ end
112
+
77
113
  # Convenience method, not used internally.
78
114
  #
79
115
  # @return [Boolean] true if the job is locked by someone
@@ -82,12 +118,19 @@ module Resque
82
118
  end
83
119
 
84
120
  # @abstract
85
- # Hook method; called when a were unable to aquire the lock.
121
+ # Hook method; called when a were unable to acquire the lock.
86
122
  #
87
123
  # @param [Array] args job arguments
88
124
  def lock_failed(*args)
89
125
  end
90
126
 
127
+ # @abstract
128
+ # Hook method; called when a were unable to enqueue loner job.
129
+ #
130
+ # @param [Array] args job arguments
131
+ def loner_enqueue_failed(*args)
132
+ end
133
+
91
134
  # @abstract
92
135
  # Hook method; called when the lock expired before we released it.
93
136
  #
@@ -95,17 +138,49 @@ module Resque
95
138
  def lock_expired_before_release(*args)
96
139
  end
97
140
 
98
- # Try to acquire a lock.
141
+ # @abstract
142
+ # if the job is a `loner`, enqueue only if no other same job
143
+ # is already running/enqueued
99
144
  #
100
- # * Returns false; when unable to acquire the lock.
145
+ # @param [Array] args job arguments
146
+ def before_enqueue_lock(*args)
147
+ if loner
148
+ if locked?(*args)
149
+ # Same job is currently running
150
+ loner_enqueue_failed(*args)
151
+ false
152
+ else
153
+ acquire_loner_lock!(*args)
154
+ end
155
+ end
156
+ end
157
+
158
+ # Try to acquire a lock for running the job.
159
+ # @return [Boolean, Fixnum]
160
+ def acquire_lock!(*args)
161
+ acquire_lock_impl!(:redis_lock_key, :lock_failed, *args)
162
+ end
163
+
164
+ # Try to acquire a lock to enqueue a loner job.
165
+ # @return [Boolean, Fixnum]
166
+ def acquire_loner_lock!(*args)
167
+ acquire_lock_impl!(:redis_loner_lock_key, :loner_enqueue_failed, *args)
168
+ end
169
+
170
+ # Generic implementation of the locking logic
171
+ #
172
+ # Returns false; when unable to acquire the lock.
101
173
  # * Returns true; when lock acquired, without a timeout.
102
174
  # * Returns timestamp; when lock acquired with a timeout, timestamp is
103
175
  # when the lock timeout expires.
104
176
  #
177
+ # @param [Symbol] lock_key_method the method returning redis key to lock
178
+ # @param [Symbol] failed_hook the method called if lock failed
179
+ # @param [Array] args job arguments
105
180
  # @return [Boolean, Fixnum]
106
- def acquire_lock!(*args)
181
+ def acquire_lock_impl!(lock_key_method, failed_hook, *args)
107
182
  acquired = false
108
- lock_key = redis_lock_key(*args)
183
+ lock_key = self.send lock_key_method, *args
109
184
 
110
185
  unless lock_timeout(*args) > 0
111
186
  # Acquire without using a timeout.
@@ -115,11 +190,11 @@ module Resque
115
190
  acquired, lock_until = acquire_lock_algorithm!(lock_key, *args)
116
191
  end
117
192
 
118
- lock_failed(*args) if !acquired
193
+ self.send(failed_hook, *args) if !acquired
119
194
  lock_until && acquired ? lock_until : acquired
120
195
  end
121
196
 
122
- # Attempts to aquire the lock using a timeout / deadlock algorithm.
197
+ # Attempts to acquire the lock using a timeout / deadlock algorithm.
123
198
  #
124
199
  # Locking algorithm: http://code.google.com/p/redis/wiki/SetnxCommand
125
200
  #
@@ -154,6 +229,13 @@ module Resque
154
229
  lock_redis.del(redis_lock_key(*args))
155
230
  end
156
231
 
232
+ # Release the enqueue lock for loner jobs
233
+ #
234
+ # @param [Array] args job arguments
235
+ def release_loner_lock!(*args)
236
+ lock_redis.del(redis_loner_lock_key(*args))
237
+ end
238
+
157
239
  # Refresh the lock.
158
240
  #
159
241
  # @param [Array] args job arguments
@@ -167,8 +249,13 @@ module Resque
167
249
  #
168
250
  # @param [Array] args job arguments
169
251
  def around_perform_lock(*args)
252
+ lock_until = acquire_lock!(*args)
253
+
254
+ # Release loner lock as job has been dequeued
255
+ release_loner_lock!(*args) if loner
256
+
170
257
  # Abort if another job holds the lock.
171
- return unless lock_until = acquire_lock!(*args)
258
+ return unless lock_until
172
259
 
173
260
  begin
174
261
  yield
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/test_helper'
2
2
 
3
3
  class LockTest < MiniTest::Unit::TestCase
4
4
  def setup
5
- $success = $lock_failed = $lock_expired = 0
5
+ $success = $lock_failed = $lock_expired = $enqueue_failed = 0
6
6
  Resque.redis.flushall
7
7
  @worker = Resque::Worker.new(:test)
8
8
  end
@@ -155,4 +155,41 @@ class LockTest < MiniTest::Unit::TestCase
155
155
  diff = latest_lock - initial_lock
156
156
  assert diff >= 1, 'diff between initial lock and refreshed lock should be at least 1 second'
157
157
  end
158
+
159
+ def test_cannot_enqueue_two_loner_jobs
160
+ Resque.enqueue(LonelyJob)
161
+ Resque.enqueue(LonelyJob)
162
+ assert_equal 1, Resque.size(:test), '1 job should be enqueued'
163
+
164
+ Resque.enqueue(LonelyTimeoutJob)
165
+ Resque.enqueue(LonelyTimeoutJob)
166
+ assert_equal 2, Resque.size(:test), '2 jobs should be enqueued'
167
+ end
168
+
169
+ def test_loner_job_should_not_be_enqued_if_already_running
170
+ Resque.enqueue(LonelyJob)
171
+ thread = Thread.new { @worker.process }
172
+
173
+ sleep 0.1 # The LonelyJob should be running (perfom is 0.2 seconds long)
174
+ Resque.enqueue(LonelyJob)
175
+ assert_equal 0, Resque.size(:test)
176
+ assert_equal 1, $enqueue_failed, 'One job callback should increment enqueue_failed'
177
+
178
+ thread.join
179
+ assert_equal 1, $success, 'One job should increment success'
180
+ end
181
+
182
+ def test_loner_job_with_timeout_should_not_be_enqued_if_already_running
183
+ Resque.enqueue(LonelyTimeoutJob)
184
+ thread = Thread.new { @worker.process }
185
+
186
+ sleep 0.1 # Job should be running (perfom is 0.2 seconds long)
187
+ Resque.enqueue(LonelyTimeoutJob)
188
+ assert_equal 0, Resque.size(:test)
189
+ assert_equal 1, $enqueue_failed, 'One job callback should increment enqueue_failed'
190
+
191
+ thread.join
192
+ assert_equal 1, $success, 'One job should increment success'
193
+ end
194
+
158
195
  end
@@ -6,10 +6,10 @@ daemonize yes
6
6
 
7
7
  # When run as a daemon, Redis write a pid file in /var/run/redis.pid by default.
8
8
  # You can specify a custom pid file location here.
9
- pidfile ./test/redis-test.pid
9
+ #pidfile ./test/redis-test.pid
10
10
 
11
11
  # Accept connections on the specified port, default is 6379
12
- port 9736
12
+ port 9737
13
13
 
14
14
  # If you want you can bind a single interface, if the bind option is not
15
15
  # specified all the interfaces will listen for connections.
@@ -30,16 +30,16 @@ timeout 300
30
30
  # after 900 sec (15 min) if at least 1 key changed
31
31
  # after 300 sec (5 min) if at least 10 keys changed
32
32
  # after 60 sec if at least 10000 keys changed
33
- save 900 1
34
- save 300 10
35
- save 60 10000
33
+ #save 900 1
34
+ #save 300 10
35
+ #save 60 10000
36
36
 
37
37
  # The filename where to dump the DB
38
- dbfilename dump.rdb
38
+ #dbfilename dump.rdb
39
39
 
40
40
  # For default save/load DB in/from the working directory
41
41
  # Note that you must specify a directory not a file name.
42
- dir ./test/
42
+ #dir ./test/
43
43
 
44
44
  # Set server verbosity to 'debug'
45
45
  # it can be one of:
@@ -112,4 +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
115
+ # glueoutputbuf yes
@@ -5,36 +5,39 @@ $TESTING = true
5
5
  require 'rubygems'
6
6
  require 'minitest/unit'
7
7
  require 'minitest/pride'
8
- require 'simplecov'
9
8
 
10
- SimpleCov.start do
11
- add_filter '/test/'
12
- end unless RUBY_PLATFORM == 'java'
9
+ # Run code coverage in MRI 1.9 only.
10
+ if RUBY_VERSION >= '1.9' && RUBY_ENGINE == 'ruby'
11
+ require 'simplecov'
12
+ SimpleCov.start do
13
+ add_filter '/test/'
14
+ end
15
+ end
13
16
 
14
17
  require 'resque-lock-timeout'
15
18
  require dir + '/test_jobs'
16
19
 
17
- # make sure we can run redis
20
+ # make sure we can run redis-server
18
21
  if !system('which redis-server')
19
- puts '', "** can't find `redis-server` in your path"
20
- puts '** try running `sudo rake install`'
22
+ puts '', "** `redis-server` was not found in your PATH"
21
23
  abort ''
22
24
  end
23
25
 
24
- # start our own redis when the tests start,
25
- # kill it when they end
26
+ # make sure we can shutdown the server using cli.
27
+ if !system('which redis-cli')
28
+ puts '', "** `redis-cli` was not found in your PATH"
29
+ abort ''
30
+ end
31
+
32
+ # This code is run `at_exit` to setup everything before running the tests.
33
+ # Redis server is started before this code block runs.
26
34
  at_exit do
27
35
  next if $!
28
36
 
29
37
  exit_code = MiniTest::Unit.new.run(ARGV)
30
-
31
- pid = `ps -e -o pid,command | grep [r]edis-test`.split(' ')[0]
32
- puts 'Killing test redis server...'
33
- `rm -f #{dir}/dump.rdb`
34
- `kill -9 #{pid}`
35
- exit(exit_code)
38
+ `redis-cli -p 9737 shutdown nosave`
36
39
  end
37
40
 
38
- puts 'Starting redis for testing at localhost:9736...'
41
+ puts "Starting redis for testing at localhost:9737..."
39
42
  `redis-server #{dir}/redis-test.conf`
40
- Resque.redis = '127.0.0.1:9736'
43
+ Resque.redis = '127.0.0.1:9737'
@@ -112,4 +112,37 @@ class RefreshLockJob
112
112
  extend Resque::Plugins::LockTimeout
113
113
  @queue = :test
114
114
  @lock_timeout = 60
115
- end
115
+ end
116
+
117
+ # Job that prevents the job being enqueued if already enqueued/running.
118
+ class LonelyJob
119
+ extend Resque::Plugins::LockTimeout
120
+ @queue = :test
121
+ @loner = true
122
+
123
+ def self.perform
124
+ $success += 1
125
+ sleep 0.2
126
+ end
127
+
128
+ def self.loner_enqueue_failed(*args)
129
+ $enqueue_failed += 1
130
+ end
131
+ end
132
+
133
+ # Exclusive job (only one queued/running) with a timeout.
134
+ class LonelyTimeoutJob
135
+ extend Resque::Plugins::LockTimeout
136
+ @queue = :test
137
+ @loner = true
138
+ @lock_timeout = 60
139
+
140
+ def self.perform
141
+ $success += 1
142
+ sleep 0.2
143
+ end
144
+
145
+ def self.loner_enqueue_failed(*args)
146
+ $enqueue_failed += 1
147
+ end
148
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-lock-timeout
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,11 +11,11 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-03-09 00:00:00.000000000Z
14
+ date: 2012-11-09 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: resque
18
- requirement: &2155990660 !ruby/object:Gem::Requirement
18
+ requirement: !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
21
  - - ! '>='
@@ -23,10 +23,15 @@ dependencies:
23
23
  version: 1.8.0
24
24
  type: :runtime
25
25
  prerelease: false
26
- version_requirements: *2155990660
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ! '>='
30
+ - !ruby/object:Gem::Version
31
+ version: 1.8.0
27
32
  - !ruby/object:Gem::Dependency
28
33
  name: rake
29
- requirement: &2155972620 !ruby/object:Gem::Requirement
34
+ requirement: !ruby/object:Gem::Requirement
30
35
  none: false
31
36
  requirements:
32
37
  - - ! '>='
@@ -34,10 +39,15 @@ dependencies:
34
39
  version: '0'
35
40
  type: :development
36
41
  prerelease: false
37
- version_requirements: *2155972620
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
38
48
  - !ruby/object:Gem::Dependency
39
49
  name: minitest
40
- requirement: &2155947800 !ruby/object:Gem::Requirement
50
+ requirement: !ruby/object:Gem::Requirement
41
51
  none: false
42
52
  requirements:
43
53
  - - ! '>='
@@ -45,10 +55,15 @@ dependencies:
45
55
  version: '0'
46
56
  type: :development
47
57
  prerelease: false
48
- version_requirements: *2155947800
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
49
64
  - !ruby/object:Gem::Dependency
50
65
  name: json
51
- requirement: &2155922920 !ruby/object:Gem::Requirement
66
+ requirement: !ruby/object:Gem::Requirement
52
67
  none: false
53
68
  requirements:
54
69
  - - ! '>='
@@ -56,10 +71,15 @@ dependencies:
56
71
  version: '0'
57
72
  type: :development
58
73
  prerelease: false
59
- version_requirements: *2155922920
74
+ version_requirements: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
60
80
  - !ruby/object:Gem::Dependency
61
81
  name: yard
62
- requirement: &2155919780 !ruby/object:Gem::Requirement
82
+ requirement: !ruby/object:Gem::Requirement
63
83
  none: false
64
84
  requirements:
65
85
  - - ! '>='
@@ -67,10 +87,15 @@ dependencies:
67
87
  version: '0'
68
88
  type: :development
69
89
  prerelease: false
70
- version_requirements: *2155919780
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
71
96
  - !ruby/object:Gem::Dependency
72
97
  name: rdiscount
73
- requirement: &2155916580 !ruby/object:Gem::Requirement
98
+ requirement: !ruby/object:Gem::Requirement
74
99
  none: false
75
100
  requirements:
76
101
  - - ! '>='
@@ -78,10 +103,15 @@ dependencies:
78
103
  version: '0'
79
104
  type: :development
80
105
  prerelease: false
81
- version_requirements: *2155916580
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
82
112
  - !ruby/object:Gem::Dependency
83
113
  name: simplecov
84
- requirement: &2155910660 !ruby/object:Gem::Requirement
114
+ requirement: !ruby/object:Gem::Requirement
85
115
  none: false
86
116
  requirements:
87
117
  - - ! '>='
@@ -89,9 +119,14 @@ dependencies:
89
119
  version: 0.3.0
90
120
  type: :development
91
121
  prerelease: false
92
- version_requirements: *2155910660
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ! '>='
126
+ - !ruby/object:Gem::Version
127
+ version: 0.3.0
93
128
  description: ! " A Resque plugin. Adds locking, with optional timeout/deadlock handling
94
- to\n resque jobs.\n\n Using a `lock_timeout` allows you to re-aquire the lock
129
+ to\n resque jobs.\n\n Using a `lock_timeout` allows you to re-acquire the lock
95
130
  should your worker\n fail, crash, or is otherwise unable to relase the lock.\n
96
131
  \ \n i.e. Your server unexpectedly looses power. Very handy for jobs that are\n
97
132
  \ recurring or may be retried.\n"
@@ -130,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
165
  version: '0'
131
166
  requirements: []
132
167
  rubyforge_project:
133
- rubygems_version: 1.8.10
168
+ rubygems_version: 1.8.24
134
169
  signing_key:
135
170
  specification_version: 3
136
171
  summary: A Resque plugin adding locking, with optional timeout/deadlock handling to