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.
@@ -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 usefull helpers back to use in Resque
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
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Resque
4
4
  module Scheduler
5
- VERSION = '4.3.1'.freeze
5
+ VERSION = '4.10.2'.freeze
6
6
  end
7
7
  end
@@ -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
- # Load the schedule into rufus
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
- if master?
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
- rescue Errno::EAGAIN, Errno::ECONNRESET, Redis::CannotConnectError => e
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
- item = nil
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
- item = enqueue_next_item(timestamp) if master?
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 enqueing normally via
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 master?
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
@@ -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 = 'http://github.com/resque/resque-scheduler'
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|appveyor\.yml)/) ||
33
- f.match(/^\.(rubocop|simplecov|travis|vagrant|gitignore)/)
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 'tzinfo-data'
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', '< 5'
58
- spec.add_runtime_dependency 'resque', '~> 1.26'
59
- spec.add_runtime_dependency 'rufus-scheduler', '~> 3.2'
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.3.1
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: 2017-11-20 00:00:00.000000000 Z
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: tzinfo-data
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.26'
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.26'
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: http://github.com/resque/resque-scheduler
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: '0'
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
- rubyforge_project:
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