resque-lock-timeout 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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