resque-rate_limited 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.hound.yml +4 -0
- data/.rspec +2 -0
- data/.rubocop.yml +46 -0
- data/Gemfile +4 -0
- data/Guardfile +16 -0
- data/LICENSE.txt +22 -0
- data/README.md +215 -0
- data/Rakefile +1 -0
- data/lib/resque-rate_limited/version.rb +3 -0
- data/lib/resque/plugins/rate_limited/apis/angellist_queue.rb +19 -0
- data/lib/resque/plugins/rate_limited/apis/base_api_queue.rb +17 -0
- data/lib/resque/plugins/rate_limited/apis/evernote_queue.rb +19 -0
- data/lib/resque/plugins/rate_limited/apis/twitter_queue.rb +19 -0
- data/lib/resque/plugins/rate_limited/rate_limited.rb +92 -0
- data/lib/resque/plugins/rate_limited/rate_limited_un_pause.rb +39 -0
- data/lib/resque/rate_limited.rb +9 -0
- data/resque-rate_limited.gemspec +43 -0
- data/spec/apis/angellist_queue_spec.rb +51 -0
- data/spec/apis/evernote_queue_spec.rb +77 -0
- data/spec/apis/twitter_queue_spec.rb +52 -0
- data/spec/rate_limited_spec.rb +243 -0
- data/spec/rate_limited_un_pause_spec.rb +49 -0
- data/spec/spec_helper.rb +18 -0
- metadata +326 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1d7f4298346cb2c327ba603fd644953c89397d0d
|
4
|
+
data.tar.gz: b860c30f7289da40aa619cbb66ec59ea3f6afece
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c5ce4337150d6b28bfe5ff6c202d187eaa0e34323b66a9d6789be3c8834b220839f49aa51486d243d21a73c42f11f085270ebe6a25d0628e5f5a1ece1d160da1
|
7
|
+
data.tar.gz: a2cb553a6a1856f3cece469a99451b4faa923e0ef5278cc03881b07479f513b196e964cfb7df12bf436a9c2c3b5dd19563c49ff78276cf8e655f7376dfc476bf
|
data/.gitignore
ADDED
data/.hound.yml
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
---
|
2
|
+
AllCops:
|
3
|
+
Exclude:
|
4
|
+
- 'tmp/**/*'
|
5
|
+
- 'coverage/**/*'
|
6
|
+
- 'spec/dummy/**/*'
|
7
|
+
|
8
|
+
#-------------------------------------------------------------------------------
|
9
|
+
# Project standards
|
10
|
+
#-------------------------------------------------------------------------------
|
11
|
+
StringLiterals:
|
12
|
+
EnforcedStyle: single_quotes
|
13
|
+
Enabled: true
|
14
|
+
|
15
|
+
DotPosition:
|
16
|
+
Description: 'Checks the position of the dot in multi-line method calls.'
|
17
|
+
EnforcedStyle: leading
|
18
|
+
Enabled: true
|
19
|
+
|
20
|
+
Documentation:
|
21
|
+
Description: 'Document classes and non-namespace modules.'
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
FileName:
|
25
|
+
Description: 'Use snake_case for source file names.'
|
26
|
+
Enabled: true
|
27
|
+
|
28
|
+
Style/SymbolArray:
|
29
|
+
Description: 'Use %i or %I for arrays of symbols.'
|
30
|
+
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-i'
|
31
|
+
Enabled: false # Only available in Ruby 2.0+
|
32
|
+
|
33
|
+
Style/ExtraSpacing:
|
34
|
+
Description: 'Do not use unnecessary spacing.'
|
35
|
+
Enabled: true
|
36
|
+
|
37
|
+
Lint/LiteralInInterpolation:
|
38
|
+
Description: 'Avoid interpolating literals in strings'
|
39
|
+
AutoCorrect: true
|
40
|
+
|
41
|
+
#-------------------------------------------------------------------------------
|
42
|
+
# These rules have been relaxed because of existing code
|
43
|
+
# We should tighten these up over time
|
44
|
+
#-------------------------------------------------------------------------------
|
45
|
+
LineLength:
|
46
|
+
Max: 166 # project standard is 120
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
guard :rubocop do
|
2
|
+
watch(/.+\.rb$/)
|
3
|
+
watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
|
4
|
+
end
|
5
|
+
|
6
|
+
guard(
|
7
|
+
:rspec,
|
8
|
+
all_after_pass: true,
|
9
|
+
all_on_start: true,
|
10
|
+
cmd: 'NO_SIMPLECOV=true bundle exec rspec --fail-fast --format documentation'
|
11
|
+
) do
|
12
|
+
watch(%r{spec/.+_spec\.rb$})
|
13
|
+
watch(%r{lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
14
|
+
watch('spec/spec_helper.rb') { 'spec' }
|
15
|
+
watch(%r{^spec/support/.+\.rb$}) { 'spec' }
|
16
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 pavoni
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
# Resque Rate Limited
|
2
|
+
|
3
|
+
A Resque plugin which makes handling jobs that use rate limited apis easier
|
4
|
+
|
5
|
+
If you have a series of jobs in a queue, this gem will pause the queue when one of the jobs hits a rate limit, and re-start the queue when the rate limit has expired.
|
6
|
+
|
7
|
+
There are two ways to use the gem.
|
8
|
+
|
9
|
+
If the api you are using has a dedicated queue included in the gem (currently Twitter, Angellist and Evernote) then you just need to make some very minor changes to how you queue jobs, and the gem will do the rest.
|
10
|
+
|
11
|
+
If you are using another API, then you need to write a little code that catches the rate limit signal.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'resque-rate_limited'
|
19
|
+
```
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle
|
24
|
+
|
25
|
+
Or install it yourself as:
|
26
|
+
|
27
|
+
$ gem install resque-rate_limited
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
### Configuration
|
32
|
+
#### Redis
|
33
|
+
The gem uses [redis-mutex](https://github.com/kenn/redis-mutex ) which requires you to register the Redis server: (e.g. in `config/initializers/redis_mutex.rb` for Rails)
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
RedisClassy.redis = Redis.new
|
37
|
+
```
|
38
|
+
Note that Redis Mutex uses the `redis-classy` gem internally.
|
39
|
+
|
40
|
+
#### Un Pause
|
41
|
+
Queues can be unpaused in two ways.
|
42
|
+
|
43
|
+
The most elegant is using [resque-scheduler](https://github.com/resque/resque-scheduler), this works well as long as you aren't running on a platform like heroku which requires a dedicated worker to run the resque-scheduler.
|
44
|
+
|
45
|
+
To tell the gem to use `resque-scheduler` you need to include it in your Gemfile - and also let the gem know which queue to use to schedule the unpause job (make sure this isn't a queue that could get paused). Put this in an initializer.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
Resque::Plugins::RateLimited::UnPause.queue = :my_queue
|
49
|
+
```
|
50
|
+
|
51
|
+
Please see the section below on how to unpause on heroku as an alternative. If you don't install `resque-scheduler` AND configure the queue, then the gem will not schedule unpause jobs this way.
|
52
|
+
|
53
|
+
#### Workers
|
54
|
+
Queues are paused by renaming them, so a resque queue called 'twitter\_api' will be renamed 'twitter\_api\_paused' when it hits a rate limit. Of course this will only work if your resque workers are not also taking jobs from the 'twitter\_api\_paused' queue. So your worker commands need to look like:
|
55
|
+
|
56
|
+
Either
|
57
|
+
```ruby
|
58
|
+
bin/resque work --queues=high,low,twitter_api
|
59
|
+
```
|
60
|
+
or
|
61
|
+
```ruby
|
62
|
+
env QUEUES=high,low,twitter_api bundle exec rake jobs:work
|
63
|
+
```
|
64
|
+
|
65
|
+
NOT
|
66
|
+
```ruby
|
67
|
+
bin/resque work --queues=*
|
68
|
+
```
|
69
|
+
or NOT
|
70
|
+
```ruby
|
71
|
+
env QUEUES=* bundle exec rake jobs:work
|
72
|
+
```
|
73
|
+
|
74
|
+
#### Unpausing on heroku
|
75
|
+
The built in schedler on heroku doesn't support dynamic scheduling from an API, so unless you want to provision an extra worker to run resque-scheduler - the best option is just to unpause all your queues on a regular basis. If they aren't paused this is a harmless no-op. If not enough time has elapsed the jobs will just hit the rate_limit and get paused again. We've found that a hourly 'rake unpause' job seems to work well. The rake task would need to call:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
Resque::Plugins::RateLimited.TwitterQueue.un_pause
|
79
|
+
Resque::Plugins::RateLimited.AngellistQueue.un_pause
|
80
|
+
MyQueue.un_pause
|
81
|
+
MyJob.un_pause
|
82
|
+
```
|
83
|
+
### A pausable job using one of the build-in queues (Twitter, Angellist, Evernote)
|
84
|
+
If you're using the [twitter gem](https://github.com/sferik/twitter), this is really simple. Instead of queuing using Resque.enqueue, you just use Resque::Plugins::RateLimited:TwitterQueue.enqueue.
|
85
|
+
|
86
|
+
Make sure your code in perform doesn't catch the rate_limit exception.
|
87
|
+
|
88
|
+
The TwitterQueue will catch the exception and pause the queue (as well as re-scheduling the jobs and scheduling an un pause (if you are using resque-scheduler)). Any jobs you add while the queue is paused will be added to the paused queue
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
class TwitterJob
|
92
|
+
class << self
|
93
|
+
def refresh(handle)
|
94
|
+
Resque::Plugins::RateLimited:TwitterQueue.enqueue(TwitterJob, handle)
|
95
|
+
end
|
96
|
+
|
97
|
+
def perform(handle)
|
98
|
+
do_something_with_the_twitter_gem(handle)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
### A single class of pausable job using a new api
|
105
|
+
If you only have one class of job you want to queue using the api, then you can use the PauseQueue module directly
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
class MyApiJob
|
109
|
+
extend Resque::Plugins::RateLimited
|
110
|
+
@queue = :my_api
|
111
|
+
WAIT_TIME = 60*60
|
112
|
+
|
113
|
+
def self.perform(*params)
|
114
|
+
do_api_stuff
|
115
|
+
rescue MyApiRateLimit
|
116
|
+
pause_until(Time.now + WAIT_TIME, name)
|
117
|
+
rate_limited_requeue(self, *params)
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.enqueue(*params)
|
121
|
+
rate_limited_enqueue(self, *params)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
````
|
125
|
+
|
126
|
+
### Multiple classes of pausable job using a new api
|
127
|
+
If you have more than one class of job you want to queue to the api, then you need to add another Queue class. This isn't hard
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
class MyApiQueue < Resque::Plugins::RateLimited::BaseApiQueue
|
131
|
+
@queue = :my_api
|
132
|
+
WAIT_TIME = 60*60
|
133
|
+
|
134
|
+
def self.perform(klass, *params)
|
135
|
+
super
|
136
|
+
rescue MyApiRateLimit
|
137
|
+
pause_until(Time.now + WAIT_TIME, name)
|
138
|
+
rate_limited_requeue(self, klass, *params)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
````
|
142
|
+
If you do this - please contribute - and I'll add to the gem.
|
143
|
+
|
144
|
+
## Development Documentation
|
145
|
+
All the functions are class methods
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
rate_limited_enqueue(klass, *params)
|
149
|
+
rate_limited_requeue(klass, *params)
|
150
|
+
````
|
151
|
+
Queue the job specified to the resque queue specified by `@queue`. `rate_limited_requeue` is intended for use when you need the job to be pushed back to the queue; there are two reasons to split this from `rate_limited_enqueue`. Firstly it makes testing with stubs easier - secondly there is a boundary condition when you need to requeue the last job in the queue.
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
pause
|
155
|
+
````
|
156
|
+
Pauses the queue specified by `@queue`, if it is not already paused.
|
157
|
+
In most cases you should call `pause_until` to pause a queue when you hit a rate limit.
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
un_pause
|
161
|
+
````
|
162
|
+
Un-pauses the queue specified by `@queue`, if it is paused.
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
pause_until(timestamp)
|
166
|
+
````
|
167
|
+
Pauses the queue (specified by `@queue`) and then queues a job to unpause the queue specified by `@queue`, using resque-scheduler to the queue specified by `Resque::Plugins::RateLimited::UnPause.queue` at the timestamp specified.
|
168
|
+
If `resque-schedule` is not included, or `UnPause.queue` isn't specified this will just pause the queue.
|
169
|
+
|
170
|
+
This is the prefered function to call when you hit a rate limit, since it with work regardless of the unpause method used by the application.
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
paused?
|
174
|
+
````
|
175
|
+
This returns true or false to indicate wheher the queue is paused. Be aware that the queue state could change get after the call returns, but before your code executes. Use `with_lock` if you need to avoid this.
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
paused_queue_name
|
179
|
+
````
|
180
|
+
Returns the name of the queue when it is paused.
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
with_lock(&block)
|
184
|
+
````
|
185
|
+
Takes ownership of the PauseQueue semaphor before executing the block passed. Useful if you need to test the state of the queue and take some action without the state changing.
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
find_class(klass)
|
189
|
+
````
|
190
|
+
Takes the parameter passed, and if it's a string class name, tries to turn it into a class.
|
191
|
+
|
192
|
+
|
193
|
+
## Contributing
|
194
|
+
|
195
|
+
1. Fork it ( https://github.com/[my-github-username]/resque_rate_limited/fork )
|
196
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
197
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
198
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
199
|
+
5. Create a new Pull Request
|
200
|
+
|
201
|
+
|
202
|
+
## Version history
|
203
|
+
|
204
|
+
0.0.x Mostly pre-release versions
|
205
|
+
|
206
|
+
1.0.0 First release version. Breaking change - renamed `pause_for` to be `pause_until` to better reflect function
|
207
|
+
|
208
|
+
1.1.0 Enqueues underlying job instead of directly performing it
|
209
|
+
|
210
|
+
|
211
|
+
|
212
|
+
## Final thoughts
|
213
|
+
Thanks to [Dominic](https://github.com/dominicsayers) for idea of renaming the redis key - and the sample code that does this.
|
214
|
+
|
215
|
+
This is my first gem - so please forgive any errors - and feedback very welcome
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'angellist_api'
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
module Plugins
|
5
|
+
module RateLimited
|
6
|
+
class AngellistQueue < BaseApiQueue
|
7
|
+
WAIT_TIME = 60
|
8
|
+
@queue = :angellist_api
|
9
|
+
|
10
|
+
def self.perform(klass, *params)
|
11
|
+
super
|
12
|
+
rescue AngellistApi::Error::TooManyRequests
|
13
|
+
pause_until(Time.now + (60 * 60))
|
14
|
+
rate_limited_requeue(self, klass, *params)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Resque
|
2
|
+
module Plugins
|
3
|
+
module RateLimited
|
4
|
+
class BaseApiQueue
|
5
|
+
extend Resque::Plugins::RateLimited
|
6
|
+
def self.perform(klass, *params)
|
7
|
+
# find_class(klass).perform(*params)
|
8
|
+
Resque.enqueue_to @queue, klass, *params # Enqueue so that ResqueSolo can take effect
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.enqueue(klass, *params)
|
12
|
+
rate_limited_enqueue(self, klass.to_s, *params)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'evernote-thrift'
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
module Plugins
|
5
|
+
module RateLimited
|
6
|
+
class EvernoteQueue < BaseApiQueue
|
7
|
+
@queue = :evernote_api
|
8
|
+
|
9
|
+
def self.perform(klass, *params)
|
10
|
+
super
|
11
|
+
rescue Evernote::EDAM::Error::EDAMSystemException => e
|
12
|
+
raise unless e.errorCode == Evernote::EDAM::Error::EDAMErrorCode::RATE_LIMIT_REACHED
|
13
|
+
pause_until(Time.now + 60 * e.rateLimitDuration.seconds)
|
14
|
+
rate_limited_requeue(self, klass, *params)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'twitter'
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
module Plugins
|
5
|
+
module RateLimited
|
6
|
+
class TwitterQueue < BaseApiQueue
|
7
|
+
@queue = :twitter_api
|
8
|
+
|
9
|
+
def self.perform(klass, *params)
|
10
|
+
super
|
11
|
+
rescue Twitter::Error::TooManyRequests,
|
12
|
+
Twitter::Error::EnhanceYourCalm => e
|
13
|
+
pause_until(Time.now + e.rate_limit.reset_in)
|
14
|
+
rate_limited_requeue(self, klass, *params)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Resque
|
2
|
+
module Plugins
|
3
|
+
module RateLimited
|
4
|
+
RESQUE_PREFIX = 'queue:'.freeze
|
5
|
+
MUTEX = 'Resque::Plugins::RateLimited'.freeze
|
6
|
+
|
7
|
+
def around_perform_with_check_and_requeue(*params)
|
8
|
+
paused = false
|
9
|
+
with_lock do
|
10
|
+
paused = paused?
|
11
|
+
Resque.enqueue_to(paused_queue_name, self, *params) if paused
|
12
|
+
end
|
13
|
+
return if paused
|
14
|
+
yield
|
15
|
+
end
|
16
|
+
|
17
|
+
def rate_limited_enqueue(klass, *params)
|
18
|
+
with_lock do
|
19
|
+
if paused?
|
20
|
+
Resque.enqueue_to(paused_queue_name, klass, *params)
|
21
|
+
else
|
22
|
+
Resque.enqueue_to(@queue, klass, *params)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def rate_limited_requeue(klass, *params)
|
28
|
+
# if the queue is empty, this was the last job - so queue to the paused queue
|
29
|
+
with_lock do
|
30
|
+
if paused?(true)
|
31
|
+
Resque.enqueue_to(paused_queue_name, klass, *params)
|
32
|
+
else
|
33
|
+
Resque.enqueue_to(@queue, klass, *params)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def pause_until(timestamp)
|
39
|
+
UnPause.enqueue(timestamp, name) if pause
|
40
|
+
end
|
41
|
+
|
42
|
+
def un_pause
|
43
|
+
Resque.redis.renamenx(RESQUE_PREFIX + paused_queue_name, RESQUE_PREFIX + @queue.to_s)
|
44
|
+
true
|
45
|
+
rescue Redis::CommandError => e
|
46
|
+
raise unless e.message == 'ERR no such key'
|
47
|
+
false
|
48
|
+
end
|
49
|
+
|
50
|
+
def pause
|
51
|
+
Resque.redis.renamenx(RESQUE_PREFIX + @queue.to_s, RESQUE_PREFIX + paused_queue_name)
|
52
|
+
true
|
53
|
+
rescue Redis::CommandError => e
|
54
|
+
raise unless e.message == 'ERR no such key'
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
def paused?(unknown = false)
|
59
|
+
# parameter is what to return if the queue is empty, and so the state is unknown
|
60
|
+
if Resque.inline
|
61
|
+
false
|
62
|
+
elsif Resque.redis.exists(RESQUE_PREFIX + @queue.to_s)
|
63
|
+
false
|
64
|
+
elsif Resque.redis.exists(RESQUE_PREFIX + paused_queue_name)
|
65
|
+
true
|
66
|
+
else
|
67
|
+
unknown
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def paused_queue_name
|
72
|
+
@queue.to_s + '_paused'
|
73
|
+
end
|
74
|
+
|
75
|
+
def with_lock
|
76
|
+
if Resque.inline
|
77
|
+
yield
|
78
|
+
else
|
79
|
+
RedisMutex.with_lock(MUTEX, block: 60, expire: 120) { yield }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_class(klass)
|
84
|
+
return klass if klass.is_a? Class
|
85
|
+
return Object.const_get(klass) unless klass.include?('::')
|
86
|
+
klass.split('::').reduce(Object) do |mod, class_name|
|
87
|
+
mod.const_get(class_name)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|