resque-scheduler 4.3.1 → 4.10.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/dependabot.yml +12 -0
- data/.github/funding.yml +4 -0
- data/.github/workflows/codeql-analysis.yml +59 -0
- data/.github/workflows/rubocop.yml +27 -0
- data/.github/workflows/ruby.yml +81 -0
- data/AUTHORS.md +11 -0
- data/CHANGELOG.md +84 -2
- data/Gemfile +25 -0
- data/README.md +104 -26
- data/Rakefile +2 -5
- data/lib/resque/scheduler/configuration.rb +38 -9
- data/lib/resque/scheduler/delaying_extensions.rb +81 -34
- data/lib/resque/scheduler/env.rb +3 -7
- data/lib/resque/scheduler/lock/base.rb +1 -1
- data/lib/resque/scheduler/lock/resilient.rb +2 -2
- data/lib/resque/scheduler/locking.rb +3 -3
- data/lib/resque/scheduler/logger_builder.rb +17 -6
- data/lib/resque/scheduler/scheduling_extensions.rb +6 -5
- data/lib/resque/scheduler/server/views/delayed.erb +12 -11
- 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 +2 -2
- data/lib/resque/scheduler/server/views/search.erb +2 -5
- data/lib/resque/scheduler/server/views/search_form.erb +1 -5
- data/lib/resque/scheduler/server.rb +1 -1
- data/lib/resque/scheduler/signal_handling.rb +2 -2
- data/lib/resque/scheduler/util.rb +1 -1
- data/lib/resque/scheduler/version.rb +1 -1
- data/lib/resque/scheduler.rb +95 -19
- data/resque-scheduler.gemspec +16 -8
- metadata +27 -32
@@ -4,7 +4,7 @@ module Resque
|
|
4
4
|
module Scheduler
|
5
5
|
class Util
|
6
6
|
# In order to upgrade to resque(1.25) which has deprecated following
|
7
|
-
# methods, we just added these
|
7
|
+
# methods, we just added these useful helpers back to use in Resque
|
8
8
|
# Scheduler. refer to:
|
9
9
|
# https://github.com/resque/resque-scheduler/pull/273
|
10
10
|
|
data/lib/resque/scheduler.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# vim:fileencoding=utf-8
|
2
2
|
|
3
|
+
require 'redis/errors'
|
3
4
|
require 'rufus/scheduler'
|
4
5
|
require_relative 'scheduler/configuration'
|
5
6
|
require_relative 'scheduler/locking'
|
@@ -13,6 +14,9 @@ module Resque
|
|
13
14
|
autoload :Extension, 'resque/scheduler/extension'
|
14
15
|
autoload :Util, 'resque/scheduler/util'
|
15
16
|
autoload :VERSION, 'resque/scheduler/version'
|
17
|
+
INTERMITTENT_ERRORS = [
|
18
|
+
Errno::EAGAIN, Errno::ECONNRESET, Redis::CannotConnectError, Redis::TimeoutError
|
19
|
+
].freeze
|
16
20
|
|
17
21
|
private
|
18
22
|
|
@@ -44,13 +48,7 @@ module Resque
|
|
44
48
|
$stdout.sync = true
|
45
49
|
$stderr.sync = true
|
46
50
|
|
47
|
-
|
48
|
-
# If dynamic is set, load that schedule otherwise use normal load
|
49
|
-
if dynamic
|
50
|
-
reload_schedule!
|
51
|
-
else
|
52
|
-
load_schedule!
|
53
|
-
end
|
51
|
+
was_master = nil
|
54
52
|
|
55
53
|
begin
|
56
54
|
@th = Thread.current
|
@@ -58,11 +56,21 @@ module Resque
|
|
58
56
|
# Now start the scheduling part of the loop.
|
59
57
|
loop do
|
60
58
|
begin
|
61
|
-
|
59
|
+
# Check on changes to master/child
|
60
|
+
@am_master = master?
|
61
|
+
if am_master != was_master
|
62
|
+
procline am_master ? 'Master scheduler' : 'Child scheduler'
|
63
|
+
|
64
|
+
# Load schedule because changed
|
65
|
+
reload_schedule!
|
66
|
+
end
|
67
|
+
|
68
|
+
if am_master
|
62
69
|
handle_delayed_items
|
63
70
|
update_schedule if dynamic
|
64
71
|
end
|
65
|
-
|
72
|
+
was_master = am_master
|
73
|
+
rescue *INTERMITTENT_ERRORS => e
|
66
74
|
log! e.message
|
67
75
|
release_master_lock
|
68
76
|
end
|
@@ -99,7 +107,7 @@ module Resque
|
|
99
107
|
Resque.schedule.each do |name, config|
|
100
108
|
load_schedule_job(name, config)
|
101
109
|
end
|
102
|
-
Resque.redis.del(:schedules_changed)
|
110
|
+
Resque.redis.del(:schedules_changed) if am_master && dynamic
|
103
111
|
procline 'Schedules Loaded'
|
104
112
|
end
|
105
113
|
|
@@ -198,16 +206,80 @@ module Resque
|
|
198
206
|
|
199
207
|
# Enqueues all delayed jobs for a timestamp
|
200
208
|
def enqueue_delayed_items_for_timestamp(timestamp)
|
201
|
-
|
209
|
+
count = 0
|
210
|
+
batch_size = delayed_requeue_batch_size
|
211
|
+
actual_batch_size = nil
|
212
|
+
|
213
|
+
log "Processing delayed items for timestamp #{timestamp}, in batches of #{batch_size}"
|
214
|
+
|
202
215
|
loop do
|
203
216
|
handle_shutdown do
|
204
217
|
# Continually check that it is still the master
|
205
|
-
|
218
|
+
if am_master
|
219
|
+
actual_batch_size = enqueue_items_in_batch_for_timestamp(timestamp,
|
220
|
+
batch_size)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
count += actual_batch_size
|
225
|
+
log "queued #{count} jobs" if actual_batch_size != -1
|
226
|
+
|
227
|
+
# continue processing until there are no more items in this
|
228
|
+
# timestamp. If we don't have a full batch, this is the last one.
|
229
|
+
# This also breaks us in the event of a redis transaction failure
|
230
|
+
# i.e. enqueue_items_in_batch_for_timestamp returned -1
|
231
|
+
break if actual_batch_size < batch_size
|
232
|
+
end
|
233
|
+
|
234
|
+
log "finished queueing #{count} total jobs for timestamp #{timestamp}" if count != -1
|
235
|
+
end
|
236
|
+
|
237
|
+
def timestamp_key(timestamp)
|
238
|
+
"delayed:#{timestamp.to_i}"
|
239
|
+
end
|
240
|
+
|
241
|
+
def enqueue_items_in_batch_for_timestamp(timestamp, batch_size)
|
242
|
+
timestamp_bucket_key = timestamp_key(timestamp)
|
243
|
+
|
244
|
+
encoded_jobs_to_requeue = Resque.redis.lrange(timestamp_bucket_key, 0, batch_size - 1)
|
245
|
+
|
246
|
+
# Watch is used to ensure that the timestamp bucket we are operating on
|
247
|
+
# is not altered by any other clients between the watch call and when we call exec
|
248
|
+
# (to execute the multi block). We should error catch on the redis.exec return value
|
249
|
+
# as that will indicate if the entire transaction was aborted or not. Though we should
|
250
|
+
# be safe as our ltrim is inside the multi block and therefore also would have been
|
251
|
+
# aborted. So nothing would have been queued, but also nothing lost from the bucket.
|
252
|
+
watch_result = Resque.redis.watch(timestamp_bucket_key) do
|
253
|
+
Resque.redis.multi do |pipeline|
|
254
|
+
encoded_jobs_to_requeue.each do |encoded_job|
|
255
|
+
pipeline.srem("timestamps:#{encoded_job}", timestamp_bucket_key)
|
256
|
+
|
257
|
+
decoded_job = Resque.decode(encoded_job)
|
258
|
+
enqueue(decoded_job)
|
259
|
+
end
|
260
|
+
|
261
|
+
pipeline.ltrim(timestamp_bucket_key, batch_size, -1)
|
206
262
|
end
|
207
|
-
# continue processing until there are no more ready items in this
|
208
|
-
# timestamp
|
209
|
-
break if item.nil?
|
210
263
|
end
|
264
|
+
|
265
|
+
# Did the multi block successfully remove from this timestamp and enqueue the jobs?
|
266
|
+
success = !watch_result.nil?
|
267
|
+
|
268
|
+
# If this was the last batch in this timestamp bucket, clean up
|
269
|
+
if success && encoded_jobs_to_requeue.count < batch_size
|
270
|
+
Resque.clean_up_timestamp(timestamp_bucket_key, timestamp)
|
271
|
+
end
|
272
|
+
|
273
|
+
unless success
|
274
|
+
# Our batched transaction failed in Redis due to the timestamp_bucket_key value
|
275
|
+
# being modified while we built our multi block. We return -1 to ensure we break
|
276
|
+
# out of the loop iterating on this timestamp so it can be re-processed via the
|
277
|
+
# loop in handle_delayed_items.
|
278
|
+
return -1
|
279
|
+
end
|
280
|
+
|
281
|
+
# will return 0 if none were left to batch
|
282
|
+
encoded_jobs_to_requeue.count
|
211
283
|
end
|
212
284
|
|
213
285
|
def enqueue(config)
|
@@ -243,7 +315,7 @@ module Resque
|
|
243
315
|
if job_klass && job_klass != 'Resque::Job'
|
244
316
|
# The custom job class API must offer a static "scheduled" method. If
|
245
317
|
# the custom job class can not be constantized (via a requeue call
|
246
|
-
# from the web perhaps), fall back to
|
318
|
+
# from the web perhaps), fall back to enqueuing normally via
|
247
319
|
# Resque::Job.create.
|
248
320
|
begin
|
249
321
|
Resque::Scheduler::Util.constantize(job_klass).scheduled(
|
@@ -369,7 +441,6 @@ module Resque
|
|
369
441
|
|
370
442
|
def stop_rufus_scheduler
|
371
443
|
rufus_scheduler.shutdown(:wait)
|
372
|
-
rufus_scheduler.join
|
373
444
|
end
|
374
445
|
|
375
446
|
def before_shutdown
|
@@ -420,10 +491,10 @@ module Resque
|
|
420
491
|
private
|
421
492
|
|
422
493
|
def enqueue_recurring(name, config)
|
423
|
-
if
|
494
|
+
if am_master
|
424
495
|
log! "queueing #{config['class']} (#{name})"
|
425
|
-
Resque.last_enqueued_at(name, Time.now.to_s)
|
426
496
|
enqueue(config)
|
497
|
+
Resque.last_enqueued_at(name, Time.now.to_s)
|
427
498
|
end
|
428
499
|
end
|
429
500
|
|
@@ -442,6 +513,11 @@ module Resque
|
|
442
513
|
def internal_name
|
443
514
|
"resque-scheduler-#{Resque::Scheduler::VERSION}"
|
444
515
|
end
|
516
|
+
|
517
|
+
def am_master
|
518
|
+
@am_master = master? unless defined?(@am_master)
|
519
|
+
@am_master
|
520
|
+
end
|
445
521
|
end
|
446
522
|
end
|
447
523
|
end
|
data/resque-scheduler.gemspec
CHANGED
@@ -11,12 +11,16 @@ Gem::Specification.new do |spec|
|
|
11
11
|
Simon Eskildsen
|
12
12
|
Ryan Biesemeyer
|
13
13
|
Dan Buch
|
14
|
+
Michael Bianco
|
15
|
+
Patrick Tulskie
|
14
16
|
EOF
|
15
17
|
spec.email = %w(
|
16
18
|
bvandenbos@gmail.com
|
17
19
|
sirup@sirupsen.com
|
18
20
|
ryan@yaauie.com
|
19
21
|
dan@meatballhat.com
|
22
|
+
mike@mikebian.co
|
23
|
+
patricktulskie@gmail.com
|
20
24
|
)
|
21
25
|
spec.summary = 'Light weight job scheduling on top of Resque'
|
22
26
|
spec.description = <<-DESCRIPTION
|
@@ -24,13 +28,16 @@ Gem::Specification.new do |spec|
|
|
24
28
|
Adds methods enqueue_at/enqueue_in to schedule jobs in the future.
|
25
29
|
Also supports queueing jobs on a fixed, cron-like schedule.
|
26
30
|
DESCRIPTION
|
27
|
-
spec.homepage = '
|
31
|
+
spec.homepage = 'https://github.com/resque/resque-scheduler'
|
28
32
|
spec.license = 'MIT'
|
33
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
34
|
+
|
35
|
+
spec.required_ruby_version = '>= 2.3.0'
|
29
36
|
|
30
37
|
spec.files = `git ls-files -z`.split("\0").reject do |f|
|
31
38
|
f.match(%r{^(test|spec|features|examples|bin|tasks)/}) ||
|
32
|
-
f.match(/^(Vagrantfile|Gemfile\.lock
|
33
|
-
f.match(/^\.(rubocop|simplecov|
|
39
|
+
f.match(/^(Vagrantfile|Gemfile\.lock)/) ||
|
40
|
+
f.match(/^\.(rubocop|simplecov|vagrant|gitignore)/)
|
34
41
|
end
|
35
42
|
spec.bindir = 'exe'
|
36
43
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
@@ -38,7 +45,6 @@ Gem::Specification.new do |spec|
|
|
38
45
|
|
39
46
|
spec.add_development_dependency 'bundler'
|
40
47
|
spec.add_development_dependency 'json'
|
41
|
-
spec.add_development_dependency 'kramdown'
|
42
48
|
spec.add_development_dependency 'minitest'
|
43
49
|
spec.add_development_dependency 'mocha'
|
44
50
|
spec.add_development_dependency 'pry'
|
@@ -47,14 +53,16 @@ Gem::Specification.new do |spec|
|
|
47
53
|
spec.add_development_dependency 'simplecov'
|
48
54
|
spec.add_development_dependency 'test-unit'
|
49
55
|
spec.add_development_dependency 'yard'
|
50
|
-
spec.add_development_dependency '
|
56
|
+
spec.add_development_dependency 'timecop'
|
51
57
|
|
52
58
|
# We pin rubocop because new cops have a tendency to result in false-y
|
53
59
|
# positives for new contributors, which is not a nice experience.
|
54
60
|
spec.add_development_dependency 'rubocop', '~> 0.40.0'
|
55
61
|
|
56
62
|
spec.add_runtime_dependency 'mono_logger', '~> 1.0'
|
57
|
-
spec.add_runtime_dependency 'redis', '>= 3.3'
|
58
|
-
spec.add_runtime_dependency 'resque', '
|
59
|
-
|
63
|
+
spec.add_runtime_dependency 'redis', '>= 3.3'
|
64
|
+
spec.add_runtime_dependency 'resque', '>= 1.27'
|
65
|
+
# rufus-scheduler v3.7 causes a failure in test/multi_process_test.rb
|
66
|
+
# rufus-scheduler v3.3 is missing a to_local method which fails tests
|
67
|
+
spec.add_runtime_dependency 'rufus-scheduler', '~> 3.2', '!= 3.3'
|
60
68
|
end
|
metadata
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque-scheduler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.10.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben VandenBos
|
8
8
|
- Simon Eskildsen
|
9
9
|
- Ryan Biesemeyer
|
10
10
|
- Dan Buch
|
11
|
+
- Michael Bianco
|
12
|
+
- Patrick Tulskie
|
11
13
|
autorequire:
|
12
14
|
bindir: exe
|
13
15
|
cert_chain: []
|
14
|
-
date:
|
16
|
+
date: 2023-12-15 00:00:00.000000000 Z
|
15
17
|
dependencies:
|
16
18
|
- !ruby/object:Gem::Dependency
|
17
19
|
name: bundler
|
@@ -41,20 +43,6 @@ dependencies:
|
|
41
43
|
- - ">="
|
42
44
|
- !ruby/object:Gem::Version
|
43
45
|
version: '0'
|
44
|
-
- !ruby/object:Gem::Dependency
|
45
|
-
name: kramdown
|
46
|
-
requirement: !ruby/object:Gem::Requirement
|
47
|
-
requirements:
|
48
|
-
- - ">="
|
49
|
-
- !ruby/object:Gem::Version
|
50
|
-
version: '0'
|
51
|
-
type: :development
|
52
|
-
prerelease: false
|
53
|
-
version_requirements: !ruby/object:Gem::Requirement
|
54
|
-
requirements:
|
55
|
-
- - ">="
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
version: '0'
|
58
46
|
- !ruby/object:Gem::Dependency
|
59
47
|
name: minitest
|
60
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -168,7 +156,7 @@ dependencies:
|
|
168
156
|
- !ruby/object:Gem::Version
|
169
157
|
version: '0'
|
170
158
|
- !ruby/object:Gem::Dependency
|
171
|
-
name:
|
159
|
+
name: timecop
|
172
160
|
requirement: !ruby/object:Gem::Requirement
|
173
161
|
requirements:
|
174
162
|
- - ">="
|
@@ -216,9 +204,6 @@ dependencies:
|
|
216
204
|
- - ">="
|
217
205
|
- !ruby/object:Gem::Version
|
218
206
|
version: '3.3'
|
219
|
-
- - "<"
|
220
|
-
- !ruby/object:Gem::Version
|
221
|
-
version: '5'
|
222
207
|
type: :runtime
|
223
208
|
prerelease: false
|
224
209
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -226,23 +211,20 @@ dependencies:
|
|
226
211
|
- - ">="
|
227
212
|
- !ruby/object:Gem::Version
|
228
213
|
version: '3.3'
|
229
|
-
- - "<"
|
230
|
-
- !ruby/object:Gem::Version
|
231
|
-
version: '5'
|
232
214
|
- !ruby/object:Gem::Dependency
|
233
215
|
name: resque
|
234
216
|
requirement: !ruby/object:Gem::Requirement
|
235
217
|
requirements:
|
236
|
-
- - "
|
218
|
+
- - ">="
|
237
219
|
- !ruby/object:Gem::Version
|
238
|
-
version: '1.
|
220
|
+
version: '1.27'
|
239
221
|
type: :runtime
|
240
222
|
prerelease: false
|
241
223
|
version_requirements: !ruby/object:Gem::Requirement
|
242
224
|
requirements:
|
243
|
-
- - "
|
225
|
+
- - ">="
|
244
226
|
- !ruby/object:Gem::Version
|
245
|
-
version: '1.
|
227
|
+
version: '1.27'
|
246
228
|
- !ruby/object:Gem::Dependency
|
247
229
|
name: rufus-scheduler
|
248
230
|
requirement: !ruby/object:Gem::Requirement
|
@@ -250,6 +232,9 @@ dependencies:
|
|
250
232
|
- - "~>"
|
251
233
|
- !ruby/object:Gem::Version
|
252
234
|
version: '3.2'
|
235
|
+
- - "!="
|
236
|
+
- !ruby/object:Gem::Version
|
237
|
+
version: '3.3'
|
253
238
|
type: :runtime
|
254
239
|
prerelease: false
|
255
240
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -257,6 +242,9 @@ dependencies:
|
|
257
242
|
- - "~>"
|
258
243
|
- !ruby/object:Gem::Version
|
259
244
|
version: '3.2'
|
245
|
+
- - "!="
|
246
|
+
- !ruby/object:Gem::Version
|
247
|
+
version: '3.3'
|
260
248
|
description: |2
|
261
249
|
Light weight job scheduling on top of Resque.
|
262
250
|
Adds methods enqueue_at/enqueue_in to schedule jobs in the future.
|
@@ -266,11 +254,18 @@ email:
|
|
266
254
|
- sirup@sirupsen.com
|
267
255
|
- ryan@yaauie.com
|
268
256
|
- dan@meatballhat.com
|
257
|
+
- mike@mikebian.co
|
258
|
+
- patricktulskie@gmail.com
|
269
259
|
executables:
|
270
260
|
- resque-scheduler
|
271
261
|
extensions: []
|
272
262
|
extra_rdoc_files: []
|
273
263
|
files:
|
264
|
+
- ".github/dependabot.yml"
|
265
|
+
- ".github/funding.yml"
|
266
|
+
- ".github/workflows/codeql-analysis.yml"
|
267
|
+
- ".github/workflows/rubocop.yml"
|
268
|
+
- ".github/workflows/ruby.yml"
|
274
269
|
- AUTHORS.md
|
275
270
|
- CHANGELOG.md
|
276
271
|
- CODE_OF_CONDUCT.md
|
@@ -309,10 +304,11 @@ files:
|
|
309
304
|
- lib/resque/scheduler/util.rb
|
310
305
|
- lib/resque/scheduler/version.rb
|
311
306
|
- resque-scheduler.gemspec
|
312
|
-
homepage:
|
307
|
+
homepage: https://github.com/resque/resque-scheduler
|
313
308
|
licenses:
|
314
309
|
- MIT
|
315
|
-
metadata:
|
310
|
+
metadata:
|
311
|
+
rubygems_mfa_required: 'true'
|
316
312
|
post_install_message:
|
317
313
|
rdoc_options: []
|
318
314
|
require_paths:
|
@@ -321,15 +317,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
321
317
|
requirements:
|
322
318
|
- - ">="
|
323
319
|
- !ruby/object:Gem::Version
|
324
|
-
version:
|
320
|
+
version: 2.3.0
|
325
321
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
326
322
|
requirements:
|
327
323
|
- - ">="
|
328
324
|
- !ruby/object:Gem::Version
|
329
325
|
version: '0'
|
330
326
|
requirements: []
|
331
|
-
|
332
|
-
rubygems_version: 2.6.8
|
327
|
+
rubygems_version: 3.0.3.1
|
333
328
|
signing_key:
|
334
329
|
specification_version: 4
|
335
330
|
summary: Light weight job scheduling on top of Resque
|