resque-scheduler 4.1.0 → 4.2.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.
Potentially problematic release.
This version of resque-scheduler might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +5 -6
- data/AUTHORS.md +5 -0
- data/HISTORY.md +20 -0
- data/README.md +28 -5
- data/lib/resque/scheduler.rb +58 -30
- data/lib/resque/scheduler/delaying_extensions.rb +4 -0
- data/lib/resque/scheduler/failure_handler.rb +11 -0
- data/lib/resque/scheduler/scheduling_extensions.rb +37 -61
- data/lib/resque/scheduler/server.rb +20 -5
- data/lib/resque/scheduler/server/views/delayed.erb +5 -1
- data/lib/resque/scheduler/server/views/delayed_schedules.erb +1 -1
- data/lib/resque/scheduler/server/views/delayed_timestamp.erb +1 -1
- data/lib/resque/scheduler/server/views/scheduler.erb +12 -6
- data/lib/resque/scheduler/version.rb +1 -1
- data/resque-scheduler.gemspec +1 -1
- data/test/delayed_queue_test.rb +37 -0
- data/test/multi_process_test.rb +100 -12
- data/test/resque-web_test.rb +5 -6
- data/test/scheduler_hooks_test.rb +31 -0
- data/test/scheduler_setup_test.rb +11 -2
- data/test/scheduler_task_test.rb +32 -0
- data/test/scheduler_test.rb +17 -21
- data/test/test_helper.rb +19 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac7465394b46317c0dcce4eff62880f4bd74a823
|
4
|
+
data.tar.gz: 1e6070308b4cce022aa2045b7980c25ac4fc2418
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ccd3427d6b1fc19366ae68ccdce9b69fba9502bf6aee13111aa3a5f486fd6334d0158291714de187b54b48000fafa878529a32413214306b86d6d914ccb37ab
|
7
|
+
data.tar.gz: 418d846ccd4be98b42e8ead43e44b361e2e1ae27b2f6e1ff761d3724dd54d318d8b339b3b399ab680f096c2d2492f85eedfe10fd4cc0465a6d2a1c93b1bc18c7
|
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2016-
|
3
|
+
# on 2016-04-22 13:22:42 -0400 using RuboCop version 0.35.1.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
@@ -13,7 +13,7 @@ Lint/AssignmentInCondition:
|
|
13
13
|
- 'lib/resque/scheduler/delaying_extensions.rb'
|
14
14
|
- 'lib/resque/scheduler/env.rb'
|
15
15
|
|
16
|
-
# Offense count:
|
16
|
+
# Offense count: 16
|
17
17
|
Metrics/AbcSize:
|
18
18
|
Max: 36
|
19
19
|
|
@@ -21,7 +21,7 @@ Metrics/AbcSize:
|
|
21
21
|
Metrics/CyclomaticComplexity:
|
22
22
|
Max: 12
|
23
23
|
|
24
|
-
# Offense count:
|
24
|
+
# Offense count: 2
|
25
25
|
# Configuration parameters: AllowURI, URISchemes.
|
26
26
|
Metrics/LineLength:
|
27
27
|
Max: 87
|
@@ -34,7 +34,7 @@ Metrics/MethodLength:
|
|
34
34
|
# Offense count: 2
|
35
35
|
# Configuration parameters: CountComments.
|
36
36
|
Metrics/ModuleLength:
|
37
|
-
Max:
|
37
|
+
Max: 314
|
38
38
|
|
39
39
|
# Offense count: 1
|
40
40
|
Style/CaseEquality:
|
@@ -54,10 +54,9 @@ Style/FileName:
|
|
54
54
|
- 'lib/resque-scheduler.rb'
|
55
55
|
- 'test/resque-web_test.rb'
|
56
56
|
|
57
|
-
# Offense count:
|
57
|
+
# Offense count: 5
|
58
58
|
# Configuration parameters: MinBodyLength.
|
59
59
|
Style/GuardClause:
|
60
60
|
Exclude:
|
61
61
|
- 'lib/resque/scheduler.rb'
|
62
62
|
- 'lib/resque/scheduler/lock/basic.rb'
|
63
|
-
- 'test/support/redis_instance.rb'
|
data/AUTHORS.md
CHANGED
@@ -26,6 +26,7 @@ Resque Scheduler authors
|
|
26
26
|
- Giovanni Cappellotto
|
27
27
|
- Harry Lascelles
|
28
28
|
- Henrik Nyh
|
29
|
+
- Hormoz Kheradmand
|
29
30
|
- James Le Cuirot
|
30
31
|
- Jarkko Mönkkönen
|
31
32
|
- John Crepezzi
|
@@ -56,6 +57,7 @@ Resque Scheduler authors
|
|
56
57
|
- Ryan Carver
|
57
58
|
- Sameer Siruguri
|
58
59
|
- Scott Francis
|
60
|
+
- Sean Stephens
|
59
61
|
- Sebastian Kippe
|
60
62
|
- Spring MC
|
61
63
|
- tbprojects
|
@@ -66,11 +68,14 @@ Resque Scheduler authors
|
|
66
68
|
- Vladislav Shub
|
67
69
|
- V Sreekanth
|
68
70
|
- Warren Sangster
|
71
|
+
- Yuri Kasperovich
|
69
72
|
- andreas
|
70
73
|
- bbauer
|
71
74
|
- camol
|
75
|
+
- d4rk5eed
|
72
76
|
- fallwith
|
73
77
|
- gravis
|
74
78
|
- hpoydar
|
75
79
|
- malomalo
|
76
80
|
- sawanoboly
|
81
|
+
- serek
|
data/HISTORY.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
# Resque Scheduler History / ChangeLog / Release Notes
|
2
2
|
|
3
|
+
## 4.2.0 (2016-04-29)
|
4
|
+
* Bugfix for a race condition in concurrent restarts
|
5
|
+
* Clean up and simplify the scheduling extension
|
6
|
+
* Make `Resque::Scheduler.logger` accessible to user
|
7
|
+
* Failure hook support for better extensibility
|
8
|
+
* Default failure handler now outputs stacktrace
|
9
|
+
* Add index column to scheduler tab
|
10
|
+
* Update rufus-scheduler
|
11
|
+
* Bugfix for displaying schedules appropriate to the `env`
|
12
|
+
in scheduler UI
|
13
|
+
|
14
|
+
## 4.1.0 (2016-02-10)
|
15
|
+
* View helper to cut down on repetition
|
16
|
+
* Bugfix to check thread life only if present
|
17
|
+
* New `Resque.(find|enqueue)_delayed_selection` methods to complement
|
18
|
+
`Resque.remove_delayed_selection`
|
19
|
+
* Leave undefined env vars unset in internal options hash
|
20
|
+
* Insulate checking `Rails.env`
|
21
|
+
* Documentation updates and typo fixes
|
22
|
+
|
3
23
|
## 4.0.0 (2014-12-21)
|
4
24
|
* Bump rufus-scheduler dependency to `~> 3.0`
|
5
25
|
* Address warning from redis-namespace related to `#unwatch`
|
data/README.md
CHANGED
@@ -170,7 +170,11 @@ Delayed jobs are one-off jobs that you want to be put into a queue at some point
|
|
170
170
|
in the future. The classic example is sending email:
|
171
171
|
|
172
172
|
```ruby
|
173
|
-
Resque.enqueue_in(
|
173
|
+
Resque.enqueue_in(
|
174
|
+
5.days,
|
175
|
+
SendFollowUpEmail,
|
176
|
+
user_id: current_user.id
|
177
|
+
)
|
174
178
|
```
|
175
179
|
|
176
180
|
This will store the job for 5 days in the resque delayed queue at which time
|
@@ -178,13 +182,22 @@ the scheduler process will pull it from the delayed queue and put it in the
|
|
178
182
|
appropriate work queue for the given job and it will be processed as soon as
|
179
183
|
a worker is available (just like any other resque job).
|
180
184
|
|
181
|
-
NOTE
|
185
|
+
**NOTE**: The job does not fire **exactly** at the time supplied. Rather, once that
|
182
186
|
time is in the past, the job moves from the delayed queue to the actual resque
|
183
187
|
work queue and will be completed as workers are free to process it.
|
184
188
|
|
185
189
|
Also supported is `Resque.enqueue_at` which takes a timestamp to queue the
|
186
190
|
job, and `Resque.enqueue_at_with_queue` which takes both a timestamp and a
|
187
|
-
queue name
|
191
|
+
queue name:
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
Resque.enqueue_at_with_queue(
|
195
|
+
'queue_name',
|
196
|
+
5.days.from_now,
|
197
|
+
SendFollowUpEmail,
|
198
|
+
user_id: current_user.id
|
199
|
+
)
|
200
|
+
```
|
188
201
|
|
189
202
|
The delayed queue is stored in redis and is persisted in the same way the
|
190
203
|
standard resque jobs are persisted (redis writing to disk). Delayed jobs differ
|
@@ -299,7 +312,7 @@ resulting in resetting schedule time on every deploy, so it's probably a good id
|
|
299
312
|
frequent jobs (like every 10-30 minutes), otherwise - when you use something like `every 20h` and deploy once-twice per day -
|
300
313
|
it will schedule the job for 20 hours from deploy, resulting in a job to never be run.
|
301
314
|
|
302
|
-
NOTE
|
315
|
+
**NOTE**: Six parameter cron's are also supported (as they supported by
|
303
316
|
rufus-scheduler which powers the resque-scheduler process). This allows you
|
304
317
|
to schedule jobs per second (ie: `"30 * * * * *"` would fire a job every 30
|
305
318
|
seconds past the minute).
|
@@ -320,6 +333,10 @@ must pass the following to `resque-scheduler` initialization (see *Installation*
|
|
320
333
|
Resque::Scheduler.dynamic = true
|
321
334
|
```
|
322
335
|
|
336
|
+
**NOTE**: In order to delete dynamic schedules via `resque-web` in the
|
337
|
+
"Schedule" tab, you must include the `Rack::MethodOverride` middleware (in
|
338
|
+
`config.ru` or equivalent).
|
339
|
+
|
323
340
|
Dynamic schedules allow for greater flexibility than static schedules as they can be set,
|
324
341
|
unset or changed without having to restart `resque-scheduler`. You can specify, if the schedule
|
325
342
|
must survive a resque-scheduler restart or not. This is done by setting the `persist` configuration
|
@@ -398,6 +415,13 @@ Similar to the `before_enqueue`- and `after_enqueue`-hooks provided in Resque
|
|
398
415
|
removed from the delayed queue, but not yet put on a normal queue. It is
|
399
416
|
called before `before_enqueue`-hooks, and on the same job instance as the
|
400
417
|
`before_enqueue`-hooks will be invoked on. Return values are ignored.
|
418
|
+
* `on_enqueue_failure`: Called with the job args and the exception that was raised
|
419
|
+
while enqueueing a job to resque or external application fails. Return
|
420
|
+
values are ignored. For example:
|
421
|
+
|
422
|
+
```ruby
|
423
|
+
Resque::Scheduler.failure_handler = ExceptionHandlerClass
|
424
|
+
```
|
401
425
|
|
402
426
|
#### Support for resque-status (and other custom jobs)
|
403
427
|
|
@@ -511,7 +535,6 @@ require 'resque/scheduler/server'
|
|
511
535
|
|
512
536
|
That should make the scheduler tabs show up in `resque-web`.
|
513
537
|
|
514
|
-
|
515
538
|
#### Changes as of 2.0.0
|
516
539
|
|
517
540
|
As of resque-scheduler 2.0.0, it's no longer necessary to have the resque-web
|
data/lib/resque/scheduler.rb
CHANGED
@@ -5,6 +5,7 @@ require_relative 'scheduler/configuration'
|
|
5
5
|
require_relative 'scheduler/locking'
|
6
6
|
require_relative 'scheduler/logger_builder'
|
7
7
|
require_relative 'scheduler/signal_handling'
|
8
|
+
require_relative 'scheduler/failure_handler'
|
8
9
|
|
9
10
|
module Resque
|
10
11
|
module Scheduler
|
@@ -22,9 +23,14 @@ module Resque
|
|
22
23
|
public
|
23
24
|
|
24
25
|
class << self
|
26
|
+
attr_writer :logger
|
27
|
+
|
25
28
|
# the Rufus::Scheduler jobs that are scheduled
|
26
29
|
attr_reader :scheduled_jobs
|
27
30
|
|
31
|
+
# allow user to set an additional failure handler
|
32
|
+
attr_writer :failure_handler
|
33
|
+
|
28
34
|
# Schedule all jobs and continually look for delayed jobs (never returns)
|
29
35
|
def run
|
30
36
|
procline 'Starting'
|
@@ -137,7 +143,7 @@ module Resque
|
|
137
143
|
if master?
|
138
144
|
log! "queueing #{config['class']} (#{name})"
|
139
145
|
Resque.last_enqueued_at(name, Time.now.to_s)
|
140
|
-
|
146
|
+
enqueue(config)
|
141
147
|
end
|
142
148
|
end
|
143
149
|
@scheduled_jobs[name] = job
|
@@ -182,19 +188,24 @@ module Resque
|
|
182
188
|
end
|
183
189
|
end
|
184
190
|
|
191
|
+
def enqueue_next_item(timestamp)
|
192
|
+
item = Resque.next_item_for_timestamp(timestamp)
|
193
|
+
|
194
|
+
if item
|
195
|
+
log "queuing #{item['class']} [delayed]"
|
196
|
+
enqueue(item)
|
197
|
+
end
|
198
|
+
|
199
|
+
item
|
200
|
+
end
|
201
|
+
|
185
202
|
# Enqueues all delayed jobs for a timestamp
|
186
203
|
def enqueue_delayed_items_for_timestamp(timestamp)
|
187
204
|
item = nil
|
188
205
|
loop do
|
189
206
|
handle_shutdown do
|
190
207
|
# Continually check that it is still the master
|
191
|
-
if master?
|
192
|
-
item = Resque.next_item_for_timestamp(timestamp)
|
193
|
-
if item
|
194
|
-
log "queuing #{item['class']} [delayed]"
|
195
|
-
handle_errors { enqueue_from_config(item) }
|
196
|
-
end
|
197
|
-
end
|
208
|
+
item = enqueue_next_item(timestamp) if master?
|
198
209
|
end
|
199
210
|
# continue processing until there are no more ready items in this
|
200
211
|
# timestamp
|
@@ -202,18 +213,18 @@ module Resque
|
|
202
213
|
end
|
203
214
|
end
|
204
215
|
|
216
|
+
def enqueue(config)
|
217
|
+
enqueue_from_config(config)
|
218
|
+
rescue => e
|
219
|
+
Resque::Scheduler.failure_handler.on_enqueue_failure(config, e)
|
220
|
+
end
|
221
|
+
|
205
222
|
def handle_shutdown
|
206
223
|
exit if @shutdown
|
207
224
|
yield
|
208
225
|
exit if @shutdown
|
209
226
|
end
|
210
227
|
|
211
|
-
def handle_errors
|
212
|
-
yield
|
213
|
-
rescue => e
|
214
|
-
log_error "#{e.class.name}: #{e.message}"
|
215
|
-
end
|
216
|
-
|
217
228
|
# Enqueues a job based on a config hash
|
218
229
|
def enqueue_from_config(job_config)
|
219
230
|
args = job_config['args'] || job_config[:args]
|
@@ -330,24 +341,39 @@ module Resque
|
|
330
341
|
|
331
342
|
def poll_sleep_loop
|
332
343
|
@sleeping = true
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
344
|
+
if poll_sleep_amount > 0
|
345
|
+
start = Time.now
|
346
|
+
loop do
|
347
|
+
elapsed_sleep = (Time.now - start)
|
348
|
+
remaining_sleep = poll_sleep_amount - elapsed_sleep
|
349
|
+
@do_break = false
|
350
|
+
if remaining_sleep <= 0
|
351
|
+
@do_break = true
|
352
|
+
else
|
353
|
+
@do_break = handle_signals_with_operation do
|
354
|
+
sleep(remaining_sleep)
|
355
|
+
end
|
345
356
|
end
|
346
|
-
break
|
357
|
+
break if @do_break
|
347
358
|
end
|
359
|
+
else
|
360
|
+
handle_signals_with_operation
|
348
361
|
end
|
349
362
|
end
|
350
363
|
|
364
|
+
def handle_signals_with_operation
|
365
|
+
yield if block_given?
|
366
|
+
handle_signals
|
367
|
+
false
|
368
|
+
rescue Interrupt
|
369
|
+
before_shutdown if @shutdown
|
370
|
+
true
|
371
|
+
end
|
372
|
+
|
373
|
+
def before_shutdown
|
374
|
+
release_master_lock
|
375
|
+
end
|
376
|
+
|
351
377
|
# Sets the shutdown flag, clean schedules and exits if sleeping
|
352
378
|
def shutdown
|
353
379
|
return if @shutdown
|
@@ -375,9 +401,9 @@ module Resque
|
|
375
401
|
$0 = argv0
|
376
402
|
end
|
377
403
|
|
378
|
-
|
379
|
-
|
380
|
-
|
404
|
+
def failure_handler
|
405
|
+
@failure_handler ||= Resque::Scheduler::FailureHandler
|
406
|
+
end
|
381
407
|
|
382
408
|
def logger
|
383
409
|
@logger ||= Resque::Scheduler::LoggerBuilder.new(
|
@@ -388,6 +414,8 @@ module Resque
|
|
388
414
|
).build
|
389
415
|
end
|
390
416
|
|
417
|
+
private
|
418
|
+
|
391
419
|
def app_str
|
392
420
|
app_name ? "[#{app_name}]" : ''
|
393
421
|
end
|
@@ -210,6 +210,8 @@ module Resque
|
|
210
210
|
# O(N) where N is the number of jobs scheduled to fire at the given
|
211
211
|
# timestamp
|
212
212
|
def remove_delayed_job_from_timestamp(timestamp, klass, *args)
|
213
|
+
return 0 if Resque.inline?
|
214
|
+
|
213
215
|
key = "delayed:#{timestamp.to_i}"
|
214
216
|
encoded_job = encode(job_to_hash(klass, args))
|
215
217
|
|
@@ -264,6 +266,8 @@ module Resque
|
|
264
266
|
end
|
265
267
|
|
266
268
|
def remove_delayed_job(encoded_job)
|
269
|
+
return 0 if Resque.inline?
|
270
|
+
|
267
271
|
timestamps = redis.smembers("timestamps:#{encoded_job}")
|
268
272
|
|
269
273
|
replies = redis.pipelined do
|
@@ -44,33 +44,11 @@ module Resque
|
|
44
44
|
# param, otherwise params is passed in as the only parameter to
|
45
45
|
# perform.
|
46
46
|
def schedule=(schedule_hash)
|
47
|
-
|
48
|
-
|
49
|
-
# Unlikely, but this could still cause a race condition.
|
50
|
-
#
|
51
|
-
# A more robust solution would be to SCRIPT it, but that would change
|
52
|
-
# the required version of Redis.
|
53
|
-
|
54
|
-
# select schedules to remove
|
55
|
-
if redis.exists(:schedules)
|
56
|
-
clean_keys = non_persistent_schedules
|
57
|
-
else
|
58
|
-
clean_keys = []
|
59
|
-
end
|
60
|
-
|
61
|
-
# Start the transaction. If this is not atomic and more than one
|
62
|
-
# process is calling `schedule=` the clean_schedules might overlap a
|
63
|
-
# set_schedule and cause the schedules to become corrupt.
|
64
|
-
redis.multi do
|
65
|
-
clean_schedules(clean_keys)
|
47
|
+
@non_persistent_schedules = nil
|
48
|
+
prepared_schedules = prepare_schedules(schedule_hash)
|
66
49
|
|
67
|
-
|
68
|
-
|
69
|
-
# store all schedules in redis, so we can retrieve them back
|
70
|
-
# everywhere.
|
71
|
-
schedule_hash.each do |name, job_spec|
|
72
|
-
set_schedule(name, job_spec)
|
73
|
-
end
|
50
|
+
prepared_schedules.each do |schedule, config|
|
51
|
+
set_schedule(schedule, config, false)
|
74
52
|
end
|
75
53
|
|
76
54
|
# ensure only return the successfully saved data!
|
@@ -83,33 +61,14 @@ module Resque
|
|
83
61
|
@schedule || {}
|
84
62
|
end
|
85
63
|
|
86
|
-
# reloads the schedule from redis
|
64
|
+
# reloads the schedule from redis and memory
|
87
65
|
def reload_schedule!
|
88
66
|
@schedule = all_schedules
|
89
67
|
end
|
90
68
|
|
91
69
|
# gets the schedules as it exists in redis
|
92
70
|
def all_schedules
|
93
|
-
|
94
|
-
|
95
|
-
redis.hgetall(:schedules).tap do |h|
|
96
|
-
h.each do |name, config|
|
97
|
-
h[name] = decode(config)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# clean the schedules as it exists in redis, useful for first setup?
|
103
|
-
def clean_schedules(keys = non_persistent_schedules)
|
104
|
-
keys.each do |key|
|
105
|
-
remove_schedule(key)
|
106
|
-
end
|
107
|
-
@schedule = nil
|
108
|
-
true
|
109
|
-
end
|
110
|
-
|
111
|
-
def non_persistent_schedules
|
112
|
-
redis.hkeys(:schedules).select { |k| !schedule_persisted?(k) }
|
71
|
+
non_persistent_schedules.merge(persistent_schedules)
|
113
72
|
end
|
114
73
|
|
115
74
|
# Create or update a schedule with the provided name and configuration.
|
@@ -121,35 +80,52 @@ module Resque
|
|
121
80
|
# :every => '15mins',
|
122
81
|
# :queue => 'high',
|
123
82
|
# :args => '/tmp/poop'})
|
124
|
-
|
83
|
+
#
|
84
|
+
# Preventing a reload is optional and available to batch operations
|
85
|
+
def set_schedule(name, config, reload = true)
|
125
86
|
persist = config.delete(:persist) || config.delete('persist')
|
126
|
-
|
127
|
-
|
128
|
-
redis.
|
129
|
-
|
87
|
+
|
88
|
+
if persist
|
89
|
+
redis.hset(:persistent_schedules, name, encode(config))
|
90
|
+
else
|
91
|
+
non_persistent_schedules[name] = decode(encode(config))
|
130
92
|
end
|
131
|
-
|
93
|
+
|
94
|
+
redis.sadd(:schedules_changed, name)
|
95
|
+
reload_schedule! if reload
|
132
96
|
end
|
133
97
|
|
134
98
|
# retrive the schedule configuration for the given name
|
135
99
|
def fetch_schedule(name)
|
136
|
-
|
137
|
-
end
|
138
|
-
|
139
|
-
def schedule_persisted?(name)
|
140
|
-
redis.sismember(:persisted_schedules, name)
|
100
|
+
schedule[name]
|
141
101
|
end
|
142
102
|
|
143
103
|
# remove a given schedule by name
|
144
104
|
def remove_schedule(name)
|
145
|
-
|
146
|
-
redis.
|
105
|
+
non_persistent_schedules.delete(name)
|
106
|
+
redis.hdel(:persistent_schedules, name)
|
147
107
|
redis.sadd(:schedules_changed, name)
|
108
|
+
|
109
|
+
reload_schedule!
|
148
110
|
end
|
149
111
|
|
150
112
|
private
|
151
113
|
|
152
|
-
|
114
|
+
# we store our non-persistent schedules in this hash
|
115
|
+
def non_persistent_schedules
|
116
|
+
@non_persistent_schedules ||= {}
|
117
|
+
end
|
118
|
+
|
119
|
+
# reads the persistent schedules from redis
|
120
|
+
def persistent_schedules
|
121
|
+
redis.hgetall(:persistent_schedules).tap do |h|
|
122
|
+
h.each do |name, config|
|
123
|
+
h[name] = decode(config)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def prepare_schedules(schedule_hash)
|
153
129
|
prepared_hash = {}
|
154
130
|
schedule_hash.each do |name, job_spec|
|
155
131
|
job_spec = job_spec.dup
|
@@ -8,6 +8,8 @@ require 'json'
|
|
8
8
|
module Resque
|
9
9
|
module Scheduler
|
10
10
|
module Server
|
11
|
+
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S %z'
|
12
|
+
|
11
13
|
unless defined?(::Resque::Scheduler::Server::VIEW_PATH)
|
12
14
|
VIEW_PATH = File.join(File.dirname(__FILE__), 'server', 'views')
|
13
15
|
end
|
@@ -105,10 +107,19 @@ module Resque
|
|
105
107
|
|
106
108
|
def delayed_queue_now
|
107
109
|
timestamp = params['timestamp'].to_i
|
110
|
+
formatted_time = Time.at(timestamp).strftime(
|
111
|
+
::Resque::Scheduler::Server::TIMESTAMP_FORMAT
|
112
|
+
)
|
113
|
+
|
108
114
|
if timestamp > 0
|
109
|
-
Resque::Scheduler.
|
115
|
+
unless Resque::Scheduler.enqueue_next_item(timestamp)
|
116
|
+
@error_message = "Unable to remove item at #{formatted_time}"
|
117
|
+
end
|
118
|
+
else
|
119
|
+
@error_message = "Incorrect timestamp #{formatted_time}"
|
110
120
|
end
|
111
|
-
|
121
|
+
|
122
|
+
erb scheduler_template('delayed')
|
112
123
|
end
|
113
124
|
|
114
125
|
def delayed_cancel_now
|
@@ -127,7 +138,11 @@ module Resque
|
|
127
138
|
|
128
139
|
module HelperMethods
|
129
140
|
def format_time(t)
|
130
|
-
t.strftime(
|
141
|
+
t.strftime(::Resque::Scheduler::Server::TIMESTAMP_FORMAT)
|
142
|
+
end
|
143
|
+
|
144
|
+
def show_job_arguments(args)
|
145
|
+
Array(args).map(&:inspect).join("\n")
|
131
146
|
end
|
132
147
|
|
133
148
|
def queue_from_class_name(class_name)
|
@@ -196,12 +211,12 @@ module Resque
|
|
196
211
|
end
|
197
212
|
|
198
213
|
def scheduled_in_this_env?(name)
|
199
|
-
return true if
|
214
|
+
return true if rails_env(name).nil?
|
200
215
|
rails_env(name).split(/[\s,]+/).include?(Resque::Scheduler.env)
|
201
216
|
end
|
202
217
|
|
203
218
|
def rails_env(name)
|
204
|
-
Resque.schedule[name]['rails_env']
|
219
|
+
Resque.schedule[name]['rails_env'] || Resque.schedule[name]['env']
|
205
220
|
end
|
206
221
|
|
207
222
|
def scheduler_view(filename, options = {}, locals = {})
|
@@ -3,6 +3,10 @@
|
|
3
3
|
|
4
4
|
<%= scheduler_view :search_form, layout: false %>
|
5
5
|
|
6
|
+
<p style="font-color: red; font-weight: bold;">
|
7
|
+
<%= @error_message %>
|
8
|
+
</p>
|
9
|
+
|
6
10
|
<p class='intro'>
|
7
11
|
This list below contains the timestamps for scheduled delayed jobs.
|
8
12
|
Server local time: <%= Time.now %>
|
@@ -39,7 +43,7 @@
|
|
39
43
|
<a href="<%= u "delayed/#{timestamp}" %>">see details</a>
|
40
44
|
<% end %>
|
41
45
|
</td>
|
42
|
-
<td><%= h(job['args']
|
46
|
+
<td><%= h(show_job_arguments(job['args'])) if job && delayed_timestamp_size == 1 %></td>
|
43
47
|
<td>
|
44
48
|
<% if job %>
|
45
49
|
<a href="<%=u URI("/delayed/jobs/#{job['class']}?args=" + URI.encode(job['args'].to_json)) %>">All schedules</a>
|
@@ -3,15 +3,20 @@
|
|
3
3
|
<p class='intro'>
|
4
4
|
The list below contains all scheduled jobs. Click "Queue now" to queue
|
5
5
|
a job immediately.
|
6
|
-
Server local time: <%= Time.now %>
|
7
|
-
|
6
|
+
<br/> Server local time: <%= Time.now %>
|
7
|
+
<br/> Server Environment: <%= Resque::Scheduler.env %>
|
8
|
+
<br/> Current master: <%= Resque.redis.get(Resque::Scheduler.master_lock.key) %>
|
9
|
+
</p>
|
10
|
+
<p class='intro'>
|
11
|
+
The highlighted jobs are skipped for current environment.
|
8
12
|
</p>
|
9
13
|
<div style="overflow-y: auto; width:100%; padding: 0px 5px;">
|
10
14
|
<table>
|
11
15
|
<tr>
|
16
|
+
<th>Index</th>
|
12
17
|
<% if Resque::Scheduler.dynamic %>
|
13
18
|
<th></th>
|
14
|
-
|
19
|
+
<% end %>
|
15
20
|
<th></th>
|
16
21
|
<th>Name</th>
|
17
22
|
<th>Description</th>
|
@@ -21,9 +26,10 @@
|
|
21
26
|
<th>Arguments</th>
|
22
27
|
<th>Last Enqueued</th>
|
23
28
|
</tr>
|
24
|
-
<% Resque.schedule.keys.sort.
|
29
|
+
<% Resque.schedule.keys.sort.each_with_index do |name, index| %>
|
25
30
|
<% config = Resque.schedule[name] %>
|
26
|
-
<tr>
|
31
|
+
<tr style="<%= scheduled_in_this_env?(name) ? '' : 'color: #9F6000;background: #FEEFB3;' %>">
|
32
|
+
<td style="padding-left: 15px;"><%= index+ 1 %>.</td>
|
27
33
|
<% if Resque::Scheduler.dynamic %>
|
28
34
|
<td style="padding-top: 12px; padding-bottom: 2px; width: 10px">
|
29
35
|
<form action="<%= u "/schedule" %>" method="post" style="margin-left: 0">
|
@@ -44,7 +50,7 @@
|
|
44
50
|
<td style="white-space:nowrap"><%= h schedule_interval(config) %></td>
|
45
51
|
<td><%= h schedule_class(config) %></td>
|
46
52
|
<td><%= h config['queue'] || queue_from_class_name(config['class']) %></td>
|
47
|
-
<td><%= h config['args']
|
53
|
+
<td><%= h show_job_arguments(config['args']) %></td>
|
48
54
|
<td><%= h Resque.get_last_enqueued_at(name) || 'Never' %></td>
|
49
55
|
</tr>
|
50
56
|
<% end %>
|
data/resque-scheduler.gemspec
CHANGED
@@ -41,5 +41,5 @@ Gem::Specification.new do |spec|
|
|
41
41
|
spec.add_runtime_dependency 'mono_logger', '~> 1.0'
|
42
42
|
spec.add_runtime_dependency 'redis', '~> 3.0'
|
43
43
|
spec.add_runtime_dependency 'resque', '~> 1.25'
|
44
|
-
spec.add_runtime_dependency 'rufus-scheduler', '~> 3.
|
44
|
+
spec.add_runtime_dependency 'rufus-scheduler', '~> 3.2'
|
45
45
|
end
|
data/test/delayed_queue_test.rb
CHANGED
@@ -265,6 +265,15 @@ context 'DelayedQueue' do
|
|
265
265
|
end
|
266
266
|
end
|
267
267
|
|
268
|
+
test 'enqueue_next_item picks one job' do
|
269
|
+
t = Time.now + 60
|
270
|
+
|
271
|
+
Resque.enqueue_at(t, SomeIvarJob)
|
272
|
+
Resque.enqueue_at(t, SomeIvarJob)
|
273
|
+
Resque::Scheduler.enqueue_next_item(t)
|
274
|
+
assert_equal(1, Resque.delayed_timestamp_peek(t, 0, 3).length)
|
275
|
+
end
|
276
|
+
|
268
277
|
test 'enqueue_delayed_items_for_timestamp creates jobs ' \
|
269
278
|
'and empties the delayed queue' do
|
270
279
|
t = Time.now + 60
|
@@ -335,6 +344,20 @@ context 'DelayedQueue' do
|
|
335
344
|
assert_equal(0, Resque.redis.scard("timestamps:#{encoded_job}"))
|
336
345
|
end
|
337
346
|
|
347
|
+
test "when Resque.inline = true, remove_delayed doesn't remove the job" \
|
348
|
+
'and returns 0' do
|
349
|
+
begin
|
350
|
+
Resque.inline = true
|
351
|
+
|
352
|
+
timestamp = Time.now + 120
|
353
|
+
Resque.enqueue_at(timestamp, SomeIvarJob, 'foo', 'bar')
|
354
|
+
|
355
|
+
assert_equal(0, Resque.remove_delayed(SomeIvarJob))
|
356
|
+
ensure
|
357
|
+
Resque.inline = false
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
338
361
|
test 'scheduled_at returns an array containing job schedule time' do
|
339
362
|
t = Time.now + 120
|
340
363
|
Resque.enqueue_at(t, SomeIvarJob)
|
@@ -816,6 +839,20 @@ context 'DelayedQueue' do
|
|
816
839
|
assert_equal 0, Resque.delayed_timestamp_size(t2)
|
817
840
|
end
|
818
841
|
|
842
|
+
test 'when Resque.inline = true, remove_delayed_job_from_timestamp' \
|
843
|
+
"doesn't remove any jobs and returns 0" do
|
844
|
+
begin
|
845
|
+
Resque.inline = true
|
846
|
+
|
847
|
+
timestamp = Time.now + 120
|
848
|
+
Resque.enqueue_at(timestamp, SomeIvarJob, 'foo', 'bar')
|
849
|
+
|
850
|
+
assert_equal(0, Resque.delayed_timestamp_size(timestamp))
|
851
|
+
ensure
|
852
|
+
Resque.inline = false
|
853
|
+
end
|
854
|
+
end
|
855
|
+
|
819
856
|
test 'remove_delayed_job_from_timestamp removes nothing if there ' \
|
820
857
|
'are no matches' do
|
821
858
|
t = Time.now + 120
|
data/test/multi_process_test.rb
CHANGED
@@ -3,35 +3,123 @@ require_relative 'test_helper'
|
|
3
3
|
|
4
4
|
context 'Multi Process' do
|
5
5
|
test 'setting schedule= from many process does not corrupt the schedules' do
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
# more info on why we're not using threads:
|
7
|
+
# https://github.com/resque/resque-scheduler/pull/439#discussion_r16788812
|
8
|
+
omit('forking is not supported by jruby but this behaviour' \
|
9
|
+
' is best tested using forks') if RUBY_ENGINE == 'jruby'
|
10
|
+
schedules_1 = {}
|
11
|
+
schedules_2 = {}
|
12
|
+
schedules = []
|
13
|
+
pids = []
|
9
14
|
|
10
15
|
# This number may need to be increased if this test is not failing
|
11
|
-
processes =
|
16
|
+
processes = 100
|
12
17
|
|
13
|
-
schedule_count =
|
18
|
+
schedule_count = 300
|
14
19
|
|
15
20
|
schedule_count.times do |n|
|
16
|
-
|
21
|
+
schedules_1["1_job_#{n}"] = { cron: '0 1 0 0 0' }
|
22
|
+
schedules_2["2_job_#{n}"] = { cron: '0 1 0 0 0' }
|
17
23
|
end
|
18
24
|
|
19
25
|
processes.times do |n|
|
20
|
-
|
26
|
+
pids << fork_with_marshalled_pipe_and_result do
|
21
27
|
sleep n * 0.1
|
22
|
-
Resque.schedule =
|
23
|
-
|
28
|
+
Resque.schedule = n.even? ? schedules_2 : schedules_1
|
29
|
+
Resque.schedule
|
24
30
|
end
|
25
31
|
end
|
26
32
|
|
27
|
-
|
33
|
+
schedules += get_results_from_children(pids)
|
34
|
+
|
35
|
+
assert_equal processes, schedules.size,
|
36
|
+
'missing some schedules, did a process die?'
|
37
|
+
schedules.each_with_index do |schedule, i|
|
38
|
+
assert_equal schedule_count, schedule.size,
|
39
|
+
"schedule count is incorrect (schedule[#{i}]: #{schedule})"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
test 'concurrent shutdowns and startups do not corrupt the schedule' do
|
44
|
+
omit('forking is not supported by jruby but this behaviour' \
|
45
|
+
' is best tested using forks') if RUBY_ENGINE == 'jruby'
|
46
|
+
counts = []
|
47
|
+
children = []
|
48
|
+
|
49
|
+
processes = 40
|
50
|
+
|
51
|
+
schedules = {}
|
52
|
+
schedule_count = 300
|
53
|
+
schedule_count.times do |n|
|
54
|
+
schedules["job_#{n}"] = { 'cron' => '0 1 0 0 0' }
|
55
|
+
end
|
56
|
+
|
28
57
|
Resque.schedule = schedules
|
29
|
-
counts << Resque.schedule.size
|
30
58
|
|
31
|
-
|
59
|
+
processes.times do |n|
|
60
|
+
children << fork_with_marshalled_pipe_and_result do
|
61
|
+
sleep Random.rand(3) * 0.1
|
62
|
+
if n.even?
|
63
|
+
Resque.schedule = schedules
|
64
|
+
Resque.schedule.size
|
65
|
+
else
|
66
|
+
Resque::Scheduler.before_shutdown
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
counts += get_results_from_children(children).compact
|
32
73
|
|
74
|
+
assert_equal processes / 2, counts.size,
|
75
|
+
'missing some counts, did a process die?'
|
33
76
|
counts.each_with_index do |c, i|
|
34
77
|
assert_equal schedule_count, c, "schedule count is incorrect (c: #{i})"
|
35
78
|
end
|
36
79
|
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def fork_with_marshalled_pipe_and_result
|
84
|
+
pipe_read, pipe_write = IO.pipe
|
85
|
+
pid = fork do
|
86
|
+
pipe_read.close
|
87
|
+
result = begin
|
88
|
+
[yield, nil]
|
89
|
+
rescue StandardError => exc
|
90
|
+
[nil, exc]
|
91
|
+
end
|
92
|
+
pipe_write.syswrite(Marshal.dump(result))
|
93
|
+
# exit true the process to get around fork issues on minitest 5
|
94
|
+
# see https://github.com/seattlerb/minitest/issues/467
|
95
|
+
Process.exit!(true)
|
96
|
+
end
|
97
|
+
pipe_write.close
|
98
|
+
|
99
|
+
[pid, pipe_read]
|
100
|
+
end
|
101
|
+
|
102
|
+
def get_results_from_children(children)
|
103
|
+
results = []
|
104
|
+
children.each do |pid, pipe|
|
105
|
+
wait_for_child_process_to_terminate(pid)
|
106
|
+
|
107
|
+
fail "forked process failed with #{$CHILD_STATUS}" unless $CHILD_STATUS.success?
|
108
|
+
result, exc = Marshal.load(pipe.read)
|
109
|
+
fail exc if exc
|
110
|
+
results << result
|
111
|
+
end
|
112
|
+
results
|
113
|
+
end
|
114
|
+
|
115
|
+
def wait_for_child_process_to_terminate(pid = -1, timeout = 30)
|
116
|
+
Timeout.timeout(timeout) do
|
117
|
+
Process.wait(pid)
|
118
|
+
end
|
119
|
+
rescue Timeout::Error
|
120
|
+
Process.kill('KILL', pid)
|
121
|
+
# collect status so it doesn't stick around as zombie process
|
122
|
+
Process.wait(pid)
|
123
|
+
flunk 'Child process did not terminate in time.'
|
124
|
+
end
|
37
125
|
end
|
data/test/resque-web_test.rb
CHANGED
@@ -51,8 +51,8 @@ context 'on GET to /schedule with scheduled jobs' do
|
|
51
51
|
assert last_response.body.include?('SomeIvarJob')
|
52
52
|
end
|
53
53
|
|
54
|
-
test '
|
55
|
-
assert
|
54
|
+
test 'include(highlight) jobs for other envs' do
|
55
|
+
assert last_response.body.include?('SomeFancyJob')
|
56
56
|
end
|
57
57
|
|
58
58
|
test 'includes job used in multiple environments' do
|
@@ -256,11 +256,10 @@ context 'on POST to /delayed/clear' do
|
|
256
256
|
end
|
257
257
|
|
258
258
|
context 'on POST to /delayed/queue_now' do
|
259
|
-
setup { post '/delayed/queue_now' }
|
259
|
+
setup { post '/delayed/queue_now', timestamp: 0 }
|
260
260
|
|
261
|
-
test '
|
262
|
-
assert last_response.status ==
|
263
|
-
assert last_response.header['Location'].include? '/overview'
|
261
|
+
test 'returns ok status' do
|
262
|
+
assert last_response.status == 200
|
264
263
|
end
|
265
264
|
end
|
266
265
|
|
@@ -21,4 +21,35 @@ context 'scheduling jobs with hooks' do
|
|
21
21
|
assert_equal(0, Resque.delayed_timestamp_size(enqueue_time.to_i),
|
22
22
|
'job should not be enqueued')
|
23
23
|
end
|
24
|
+
|
25
|
+
test 'default failure hooks are called when enqueueing a job fails' do
|
26
|
+
config = {
|
27
|
+
'cron' => '* * * * *',
|
28
|
+
'class' => 'SomeRealClass',
|
29
|
+
'args' => '/tmp'
|
30
|
+
}
|
31
|
+
|
32
|
+
e = RuntimeError.new('custom error')
|
33
|
+
Resque::Scheduler.expects(:enqueue_from_config).raises(e)
|
34
|
+
|
35
|
+
Resque::Scheduler::FailureHandler.expects(:on_enqueue_failure).with(config, e)
|
36
|
+
Resque::Scheduler.enqueue(config)
|
37
|
+
end
|
38
|
+
|
39
|
+
test 'failure hooks are called when enqueueing a job fails' do
|
40
|
+
with_failure_handler(ExceptionHandlerClass) do
|
41
|
+
config = {
|
42
|
+
'cron' => '* * * * *',
|
43
|
+
'class' => 'SomeRealClass',
|
44
|
+
'args' => '/tmp'
|
45
|
+
}
|
46
|
+
|
47
|
+
e = RuntimeError.new('custom error')
|
48
|
+
Resque::Scheduler.expects(:enqueue_from_config).raises(e)
|
49
|
+
|
50
|
+
ExceptionHandlerClass.expects(:on_enqueue_failure).with(config, e)
|
51
|
+
|
52
|
+
Resque::Scheduler.enqueue(config)
|
53
|
+
end
|
54
|
+
end
|
24
55
|
end
|
@@ -14,8 +14,17 @@ context 'Resque::Scheduler' do
|
|
14
14
|
|
15
15
|
test 'set custom logger' do
|
16
16
|
custom_logger = MonoLogger.new('/dev/null')
|
17
|
-
Resque::Scheduler.
|
18
|
-
|
17
|
+
Resque::Scheduler.logger = custom_logger
|
18
|
+
|
19
|
+
custom_logger.expects(:error).once
|
20
|
+
Resque::Scheduler.log_error('test')
|
21
|
+
end
|
22
|
+
|
23
|
+
test 'custom logger is accessible' do
|
24
|
+
custom_logger = MonoLogger.new('/dev/null')
|
25
|
+
Resque::Scheduler.logger = custom_logger
|
26
|
+
|
27
|
+
assert_equal custom_logger, Resque::Scheduler.logger
|
19
28
|
end
|
20
29
|
|
21
30
|
test 'configure block' do
|
data/test/scheduler_task_test.rb
CHANGED
@@ -37,4 +37,36 @@ context 'Resque::Scheduler' do
|
|
37
37
|
Resque::Scheduler.unstub(:release_master_lock)
|
38
38
|
Resque::Scheduler.release_master_lock
|
39
39
|
end
|
40
|
+
|
41
|
+
test 'can start successfully' do
|
42
|
+
Resque::Scheduler.poll_sleep_amount = nil
|
43
|
+
|
44
|
+
@pid = Process.pid
|
45
|
+
Thread.new do
|
46
|
+
sleep(0.15)
|
47
|
+
Process.kill(:TERM, @pid)
|
48
|
+
end
|
49
|
+
|
50
|
+
assert_raises SystemExit do
|
51
|
+
Resque::Scheduler.run
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
test 'sending TERM to scheduler breaks out when poll_sleep_amount = 0' do
|
56
|
+
Resque::Scheduler.poll_sleep_amount = 0
|
57
|
+
Resque::Scheduler.expects(:release_master_lock)
|
58
|
+
|
59
|
+
@pid = Process.pid
|
60
|
+
Thread.new do
|
61
|
+
sleep(0.05)
|
62
|
+
Process.kill(:TERM, @pid)
|
63
|
+
end
|
64
|
+
|
65
|
+
assert_raises SystemExit do
|
66
|
+
Resque::Scheduler.run
|
67
|
+
end
|
68
|
+
|
69
|
+
Resque::Scheduler.unstub(:release_master_lock)
|
70
|
+
Resque::Scheduler.release_master_lock
|
71
|
+
end
|
40
72
|
end
|
data/test/scheduler_test.rb
CHANGED
@@ -31,16 +31,16 @@ context 'Resque::Scheduler' do
|
|
31
31
|
Resque::Scheduler.env = 'production'
|
32
32
|
config = {
|
33
33
|
'cron' => '* * * * *',
|
34
|
-
'class' => '
|
34
|
+
'class' => 'SomeJobWithResqueHooks',
|
35
35
|
'args' => '/tmp'
|
36
36
|
}
|
37
37
|
|
38
38
|
Resque::Job.expects(:create).with(
|
39
|
-
|
39
|
+
SomeJobWithResqueHooks.queue, SomeJobWithResqueHooks, '/tmp'
|
40
40
|
)
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
SomeJobWithResqueHooks.expects(:before_delayed_enqueue_example).with('/tmp')
|
42
|
+
SomeJobWithResqueHooks.expects(:before_enqueue_example).with('/tmp')
|
43
|
+
SomeJobWithResqueHooks.expects(:after_enqueue_example).with('/tmp')
|
44
44
|
|
45
45
|
Resque::Scheduler.enqueue_from_config(config)
|
46
46
|
end
|
@@ -59,7 +59,7 @@ context 'Resque::Scheduler' do
|
|
59
59
|
assert_equal(0, Resque::Scheduler.rufus_scheduler.jobs.size)
|
60
60
|
|
61
61
|
Resque.schedule = {
|
62
|
-
some_ivar_job
|
62
|
+
'some_ivar_job' => {
|
63
63
|
'cron' => '* * * * *',
|
64
64
|
'class' => 'SomeIvarJob',
|
65
65
|
'args' => '/tmp'
|
@@ -87,15 +87,13 @@ context 'Resque::Scheduler' do
|
|
87
87
|
assert Resque::Scheduler.scheduled_jobs.include?('some_ivar_job')
|
88
88
|
|
89
89
|
Resque.redis.del(:schedules)
|
90
|
-
Resque.
|
91
|
-
|
92
|
-
'some_ivar_job2',
|
93
|
-
Resque.encode(
|
90
|
+
Resque.schedule = {
|
91
|
+
'some_ivar_job2' => {
|
94
92
|
'cron' => '* * * * *',
|
95
93
|
'class' => 'SomeIvarJob',
|
96
94
|
'args' => '/tmp/2'
|
97
|
-
|
98
|
-
|
95
|
+
}
|
96
|
+
}
|
99
97
|
|
100
98
|
Resque::Scheduler.reload_schedule!
|
101
99
|
|
@@ -313,7 +311,7 @@ context 'Resque::Scheduler' do
|
|
313
311
|
}
|
314
312
|
assert_equal(
|
315
313
|
{ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/75' },
|
316
|
-
Resque.
|
314
|
+
Resque.schedule['my_ivar_job']
|
317
315
|
)
|
318
316
|
end
|
319
317
|
|
@@ -346,7 +344,7 @@ context 'Resque::Scheduler' do
|
|
346
344
|
} }
|
347
345
|
assert_equal(
|
348
346
|
{ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/75' },
|
349
|
-
Resque.
|
347
|
+
Resque.schedule['SomeIvarJob']
|
350
348
|
)
|
351
349
|
assert_equal('SomeIvarJob', Resque.schedule['SomeIvarJob']['class'])
|
352
350
|
end
|
@@ -366,21 +364,19 @@ context 'Resque::Scheduler' do
|
|
366
364
|
)
|
367
365
|
assert_equal(
|
368
366
|
{ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/22' },
|
369
|
-
Resque.
|
367
|
+
Resque.schedule['some_ivar_job']
|
370
368
|
)
|
371
369
|
assert Resque.redis.sismember(:schedules_changed, 'some_ivar_job')
|
372
370
|
end
|
373
371
|
|
374
372
|
test 'fetch_schedule returns a schedule' do
|
375
|
-
Resque.
|
376
|
-
|
377
|
-
'some_ivar_job2',
|
378
|
-
Resque.encode(
|
373
|
+
Resque.schedule = {
|
374
|
+
'some_ivar_job2' => {
|
379
375
|
'cron' => '* * * * *',
|
380
376
|
'class' => 'SomeIvarJob',
|
381
377
|
'args' => '/tmp/33'
|
382
|
-
|
383
|
-
|
378
|
+
}
|
379
|
+
}
|
384
380
|
assert_equal(
|
385
381
|
{ 'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/33' },
|
386
382
|
Resque.fetch_schedule('some_ivar_job2')
|
data/test/test_helper.rb
CHANGED
@@ -49,6 +49,10 @@ unless defined?(Rails)
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
+
class ExceptionHandlerClass
|
53
|
+
def self.on_enqueue_failure(_, _); end
|
54
|
+
end
|
55
|
+
|
52
56
|
class FakeCustomJobClass
|
53
57
|
def self.scheduled(_, _, *_); end
|
54
58
|
end
|
@@ -89,6 +93,12 @@ class SomeRealClass
|
|
89
93
|
end
|
90
94
|
end
|
91
95
|
|
96
|
+
class SomeJobWithResqueHooks < SomeRealClass
|
97
|
+
def before_enqueue_example; end
|
98
|
+
|
99
|
+
def after_enqueue_example; end
|
100
|
+
end
|
101
|
+
|
92
102
|
class JobWithParams
|
93
103
|
def self.perform(*args)
|
94
104
|
@args = args
|
@@ -115,7 +125,7 @@ def nullify_logger
|
|
115
125
|
c.quiet = nil
|
116
126
|
c.verbose = nil
|
117
127
|
c.logfile = nil
|
118
|
-
c.
|
128
|
+
c.logger = nil
|
119
129
|
end
|
120
130
|
|
121
131
|
ENV['LOGFILE'] = nil
|
@@ -126,4 +136,12 @@ def restore_devnull_logfile
|
|
126
136
|
ENV['LOGFILE'] = '/dev/null'
|
127
137
|
end
|
128
138
|
|
139
|
+
def with_failure_handler(handler)
|
140
|
+
original_handler = Resque::Scheduler.failure_handler
|
141
|
+
Resque::Scheduler.failure_handler = handler
|
142
|
+
yield
|
143
|
+
ensure
|
144
|
+
Resque::Scheduler.failure_handler = original_handler
|
145
|
+
end
|
146
|
+
|
129
147
|
restore_devnull_logfile
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque-scheduler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben VandenBos
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -226,14 +226,14 @@ dependencies:
|
|
226
226
|
requirements:
|
227
227
|
- - "~>"
|
228
228
|
- !ruby/object:Gem::Version
|
229
|
-
version: '3.
|
229
|
+
version: '3.2'
|
230
230
|
type: :runtime
|
231
231
|
prerelease: false
|
232
232
|
version_requirements: !ruby/object:Gem::Requirement
|
233
233
|
requirements:
|
234
234
|
- - "~>"
|
235
235
|
- !ruby/object:Gem::Version
|
236
|
-
version: '3.
|
236
|
+
version: '3.2'
|
237
237
|
description: |2
|
238
238
|
Light weight job scheduling on top of Resque.
|
239
239
|
Adds methods enqueue_at/enqueue_in to schedule jobs in the future.
|
@@ -280,6 +280,7 @@ files:
|
|
280
280
|
- lib/resque/scheduler/delaying_extensions.rb
|
281
281
|
- lib/resque/scheduler/env.rb
|
282
282
|
- lib/resque/scheduler/extension.rb
|
283
|
+
- lib/resque/scheduler/failure_handler.rb
|
283
284
|
- lib/resque/scheduler/lock.rb
|
284
285
|
- lib/resque/scheduler/lock/base.rb
|
285
286
|
- lib/resque/scheduler/lock/basic.rb
|
@@ -336,7 +337,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
336
337
|
version: '0'
|
337
338
|
requirements: []
|
338
339
|
rubyforge_project:
|
339
|
-
rubygems_version: 2.
|
340
|
+
rubygems_version: 2.2.3
|
340
341
|
signing_key:
|
341
342
|
specification_version: 4
|
342
343
|
summary: Light weight job scheduling on top of Resque
|
@@ -354,4 +355,3 @@ test_files:
|
|
354
355
|
- test/scheduler_test.rb
|
355
356
|
- test/test_helper.rb
|
356
357
|
- test/util_test.rb
|
357
|
-
has_rdoc:
|