sidekiq-scheduler 2.1.9 → 2.1.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 310c3a0ed3c3976eb6585432e0b2bd00ddb4ddb8
4
- data.tar.gz: ee996ad32bfaacb0f3aa77a7f3073a3a8ad4d9f4
3
+ metadata.gz: a3f7cb5ee802d2e3b5c89b308fbcaff612911217
4
+ data.tar.gz: e6b488b10334b3af01c9d2521e31ec3c53f0ef20
5
5
  SHA512:
6
- metadata.gz: 629bdc5d21f6907a7427356d3b78d1a253587aea9b7f3528ba92f2cc1f3f2b01116eeddb36830a1bed3341e7a9f6d192d94ba1ac3e9c66bf80663a4f5d31fc1c
7
- data.tar.gz: b2a7113238972d104d65f19d465ead5846e157696dee0c77f51000ba4dc751ca6d597566e44824f6012e722d770a634fb9d655d47dd11fafdbb3eb5858010e7a
6
+ metadata.gz: b56413d0c141f2ae15bfacb255a8eed2550b6b0f899703f5fd08d67539794878e411d9a5a48086f4fd9d7f542f6b78fe9a016b7edf32a78819b93c7860ecf2a2
7
+ data.tar.gz: 6b8524661830f24e9233a95ec94a18ad31eac1f0880a63a3bc48f728e802f8f5ffe79437bfd5d17bdc3ec07a79d142b0d991b33a5a6363f79a4e6f1c387ee2c2
data/README.md CHANGED
@@ -94,6 +94,7 @@ Available options are:
94
94
 
95
95
  ``` yaml
96
96
  :dynamic: <if true the schedule can be modified in runtime [false by default]>
97
+ :dynamic_every: <if dynamic is true, the schedule is reloaded every interval [5s by default]>
97
98
  :enabled: <enables scheduler if true [true by default]>
98
99
  :scheduler:
99
100
  :listened_queues_only: <push jobs whose queue is being listened by sidekiq [false by default]>
@@ -244,7 +245,7 @@ Sidekiq.set_schedule('heartbeat', { 'every' => ['1m'], 'class' => 'HeartbeatWork
244
245
 
245
246
  If the schedule did not exist it will be created, if it existed it will be updated.
246
247
 
247
- When `:dynamic` flag is set to `true`, schedule changes are loaded every 5 seconds.
248
+ When `:dynamic` flag is set to `true`, schedule changes are loaded every 5 seconds. Use the `:dynamic_every` flag for a different interval.
248
249
 
249
250
  ``` yaml
250
251
  # config/sidekiq.yml
@@ -3,6 +3,7 @@ require 'tilt/erb'
3
3
 
4
4
  require_relative 'sidekiq-scheduler/version'
5
5
  require_relative 'sidekiq-scheduler/manager'
6
+ require_relative 'sidekiq-scheduler/redis_manager'
6
7
 
7
8
  Sidekiq.configure_server do |config|
8
9
 
@@ -10,6 +11,9 @@ Sidekiq.configure_server do |config|
10
11
  dynamic = Sidekiq::Scheduler.dynamic
11
12
  dynamic = dynamic.nil? ? config.options.fetch(:dynamic, false) : dynamic
12
13
 
14
+ dynamic_every = Sidekiq::Scheduler.dynamic_every
15
+ dynamic_every = dynamic_every.nil? ? config.options.fetch(:dynamic_every, '5s') : dynamic_every
16
+
13
17
  enabled = Sidekiq::Scheduler.enabled
14
18
  enabled = enabled.nil? ? config.options.fetch(:enabled, true) : enabled
15
19
 
@@ -22,14 +26,15 @@ Sidekiq.configure_server do |config|
22
26
  schedule ||= config.options[:schedule] || {}
23
27
 
24
28
  scheduler_options = {
25
- dynamic: dynamic,
26
- enabled: enabled,
27
- schedule: schedule,
29
+ dynamic: dynamic,
30
+ dynamic_every: dynamic_every,
31
+ enabled: enabled,
32
+ schedule: schedule,
28
33
  listened_queues_only: listened_queues_only
29
34
  }
30
35
 
31
36
  # schedules_changed's type was changed from SET to ZSET, so we remove old versions at startup
32
- Sidekiq.redis { |r| r.del(:schedules_changed) unless r.type(:schedules_changed) == 'zset' }
37
+ SidekiqScheduler::RedisManager.clean_schedules_changed
33
38
 
34
39
  schedule_manager = SidekiqScheduler::Manager.new(scheduler_options)
35
40
  config.options[:schedule_manager] = schedule_manager
@@ -3,6 +3,7 @@ begin
3
3
  rescue LoadError
4
4
  require 'sidekiq/web_helpers'
5
5
  end
6
+ require 'sidekiq-scheduler/redis_manager'
6
7
 
7
8
  module SidekiqScheduler
8
9
  class JobPresenter
@@ -19,7 +20,7 @@ module SidekiqScheduler
19
20
  #
20
21
  # @return [String] with the job's next time
21
22
  def next_time
22
- execution_time = Sidekiq.redis { |r| r.hget(Sidekiq::Scheduler.next_times_key, name) }
23
+ execution_time = SidekiqScheduler::RedisManager.get_job_next_time(name)
23
24
 
24
25
  relative_time(Time.parse(execution_time)) if execution_time
25
26
  end
@@ -28,7 +29,7 @@ module SidekiqScheduler
28
29
  #
29
30
  # @return [String] with the job's last time
30
31
  def last_time
31
- execution_time = Sidekiq.redis { |r| r.hget(Sidekiq::Scheduler.last_times_key, name) }
32
+ execution_time = SidekiqScheduler::RedisManager.get_job_last_time(name)
32
33
 
33
34
  relative_time(Time.parse(execution_time)) if execution_time
34
35
  end
@@ -17,6 +17,7 @@ module SidekiqScheduler
17
17
  def initialize(options)
18
18
  Sidekiq::Scheduler.enabled = options[:enabled]
19
19
  Sidekiq::Scheduler.dynamic = options[:dynamic]
20
+ Sidekiq::Scheduler.dynamic_every = options[:dynamic_every]
20
21
  Sidekiq::Scheduler.listened_queues_only = options[:listened_queues_only]
21
22
  Sidekiq.schedule = options[:schedule] if Sidekiq::Scheduler.enabled
22
23
  end
@@ -0,0 +1,212 @@
1
+ module SidekiqScheduler
2
+ module RedisManager
3
+
4
+ REGISTERED_JOBS_THRESHOLD_IN_SECONDS = 24 * 60 * 60
5
+
6
+ # Returns the schedule of a given job
7
+ #
8
+ # @param [String] name The name of the job
9
+ #
10
+ # @return [String] schedule in JSON format
11
+ def self.get_job_schedule(name)
12
+ hget(:schedules, name)
13
+ end
14
+
15
+ # Returns the state of a given job
16
+ #
17
+ # @param [String] name The name of the job
18
+ #
19
+ # @return [String] state in JSON format
20
+ def self.get_job_state(name)
21
+ hget(schedules_state_key, name)
22
+ end
23
+
24
+ # Returns the next execution time for a given job
25
+ #
26
+ # @param [String] name The name of the job
27
+ #
28
+ # @return [String] next time the job has to be executed
29
+ def self.get_job_next_time(name)
30
+ hget(next_times_key, name)
31
+ end
32
+
33
+ # Returns the last execution time of a given job
34
+ #
35
+ # @param [String] name The name of the job
36
+ #
37
+ # @return [String] last time the job was executed
38
+ def self.get_job_last_time(name)
39
+ hget(last_times_key, name)
40
+ end
41
+
42
+ # Sets the schedule for a given job
43
+ #
44
+ # @param [String] name The name of the job
45
+ # @param [Hash] config The new schedule for the job
46
+ def self.set_job_schedule(name, config)
47
+ hset(:schedules, name, JSON.generate(config))
48
+ end
49
+
50
+ # Sets the state for a given job
51
+ #
52
+ # @param [String] name The name of the job
53
+ # @param [Hash] state The new state for the job
54
+ def self.set_job_state(name, state)
55
+ hset(schedules_state_key, name, JSON.generate(state))
56
+ end
57
+
58
+ # Sets the next execution time for a given job
59
+ #
60
+ # @param [String] name The name of the job
61
+ # @param [String] next_time The next time the job has to be executed
62
+ def self.set_job_next_time(name, next_time)
63
+ hset(next_times_key, name, next_time)
64
+ end
65
+
66
+ # Sets the last execution time for a given job
67
+ #
68
+ # @param [String] name The name of the job
69
+ # @param [String] last_time The last time the job was executed
70
+ def self.set_job_last_time(name, last_time)
71
+ hset(last_times_key, name, last_time)
72
+ end
73
+
74
+ # Removes the schedule for a given job
75
+ #
76
+ # @param [String] name The name of the job
77
+ def self.remove_job_schedule(name)
78
+ hdel(:schedules, name)
79
+ end
80
+
81
+ # Removes the next execution time for a given job
82
+ #
83
+ # @param [String] name The name of the job
84
+ def self.remove_job_next_time(name)
85
+ hdel(next_times_key, name)
86
+ end
87
+
88
+ # Returns the schedules of all the jobs
89
+ #
90
+ # @return [Hash] hash with all the job schedules
91
+ def self.get_all_schedules
92
+ Sidekiq.redis { |r| r.hgetall(:schedules) }
93
+ end
94
+
95
+ # Returns boolean value that indicates if the schedules value exists
96
+ #
97
+ # @return [Boolean] true if the schedules key is set, false otherwise
98
+ def self.schedule_exist?
99
+ Sidekiq.redis { |r| r.exists(:schedules) }
100
+ end
101
+
102
+ # Returns all the schedule changes for a given time range.
103
+ #
104
+ # @param [Float] from The minimum value in the range
105
+ # @param [Float] to The maximum value in the range
106
+ #
107
+ # @return [Array] array with all the changed job names
108
+ def self.get_schedule_changes(from, to)
109
+ Sidekiq.redis { |r| r.zrangebyscore(:schedules_changed, from, "(#{to}") }
110
+ end
111
+
112
+ # Register a schedule change for a given job
113
+ #
114
+ # @param [String] name The name of the job
115
+ def self.add_schedule_change(name)
116
+ Sidekiq.redis { |r| r.zadd(:schedules_changed, Time.now.to_f, name) }
117
+ end
118
+
119
+ # Remove all the schedule changes records
120
+ def self.clean_schedules_changed
121
+ Sidekiq.redis { |r| r.del(:schedules_changed) unless r.type(:schedules_changed) == 'zset' }
122
+ end
123
+
124
+ # Removes a queued job instance
125
+ #
126
+ # @param [String] job_name The name of the job
127
+ # @param [Time] time The time at which the job was cleared by the scheduler
128
+ #
129
+ # @return [Boolean] true if the job was registered, false otherwise
130
+ def self.register_job_instance(job_name, time)
131
+ job_key = pushed_job_key(job_name)
132
+ registered, _ = Sidekiq.redis do |r|
133
+ r.pipelined do
134
+ r.zadd(job_key, time.to_i, time.to_i)
135
+ r.expire(job_key, REGISTERED_JOBS_THRESHOLD_IN_SECONDS)
136
+ end
137
+ end
138
+
139
+ registered
140
+ end
141
+
142
+ # Removes instances of the job older than 24 hours
143
+ #
144
+ # @param [String] job_name The name of the job
145
+ def self.remove_elder_job_instances(job_name)
146
+ seconds_ago = Time.now.to_i - REGISTERED_JOBS_THRESHOLD_IN_SECONDS
147
+
148
+ Sidekiq.redis do |r|
149
+ r.zremrangebyscore(pushed_job_key(job_name), 0, seconds_ago)
150
+ end
151
+ end
152
+
153
+ # Returns the key of the Redis sorted set used to store job enqueues
154
+ #
155
+ # @param [String] job_name The name of the job
156
+ #
157
+ # @return [String] the pushed job key
158
+ def self.pushed_job_key(job_name)
159
+ "sidekiq-scheduler:pushed:#{job_name}"
160
+ end
161
+
162
+ # Returns the key of the Redis hash for job's execution times hash
163
+ #
164
+ # @return [String] with the key
165
+ def self.next_times_key
166
+ 'sidekiq-scheduler:next_times'
167
+ end
168
+
169
+ # Returns the key of the Redis hash for job's last execution times hash
170
+ #
171
+ # @return [String] with the key
172
+ def self.last_times_key
173
+ 'sidekiq-scheduler:last_times'
174
+ end
175
+
176
+ # Returns the Redis's key for saving schedule states.
177
+ #
178
+ # @return [String] with the key
179
+ def self.schedules_state_key
180
+ 'sidekiq-scheduler:states'
181
+ end
182
+
183
+ private
184
+
185
+ # Returns the value of a Redis stored hash field
186
+ #
187
+ # @param [String] hash_key The key name of the hash
188
+ # @param [String] field_key The key name of the field
189
+ #
190
+ # @return [String]
191
+ def self.hget(hash_key, field_key)
192
+ Sidekiq.redis { |r| r.hget(hash_key, field_key) }
193
+ end
194
+
195
+ # Sets the value of a Redis stored hash field
196
+ #
197
+ # @param [String] hash_key The key name of the hash
198
+ # @param [String] field_key The key name of the field
199
+ # @param [String] value The new value name for the field
200
+ def self.hset(hash_key, field_key, value)
201
+ Sidekiq.redis { |r| r.hset(hash_key, field_key, value) }
202
+ end
203
+
204
+ # Removes the value of a Redis stored hash field
205
+ #
206
+ # @param [String] hash_key The key name of the hash
207
+ # @param [String] field_key The key name of the field
208
+ def self.hdel(hash_key, field_key)
209
+ Sidekiq.redis { |r| r.hdel(hash_key, field_key) }
210
+ end
211
+ end
212
+ end
@@ -1,6 +1,7 @@
1
1
  require 'json'
2
2
 
3
3
  require 'sidekiq-scheduler/utils'
4
+ require_relative 'redis_manager'
4
5
 
5
6
  module SidekiqScheduler
6
7
  module Schedule
@@ -71,7 +72,7 @@ module SidekiqScheduler
71
72
  if name.nil?
72
73
  get_all_schedules
73
74
  else
74
- encoded_schedule = Sidekiq.redis { |r| r.hget(:schedules, name) }
75
+ encoded_schedule = SidekiqScheduler::RedisManager.get_job_schedule(name)
75
76
  encoded_schedule.nil? ? nil : JSON.parse(encoded_schedule)
76
77
  end
77
78
  end
@@ -80,8 +81,8 @@ module SidekiqScheduler
80
81
  def get_all_schedules
81
82
  schedules = {}
82
83
 
83
- if Sidekiq.redis { |r| r.exists(:schedules) }
84
- Sidekiq.redis { |r| r.hgetall(:schedules) }.tap do |h|
84
+ if SidekiqScheduler::RedisManager.schedule_exist?
85
+ SidekiqScheduler::RedisManager.get_all_schedules.tap do |h|
85
86
  h.each do |name, config|
86
87
  schedules[name] = JSON.parse(config)
87
88
  end
@@ -103,16 +104,16 @@ module SidekiqScheduler
103
104
  def set_schedule(name, config)
104
105
  existing_config = get_schedule(name)
105
106
  unless existing_config && existing_config == config
106
- Sidekiq.redis { |r| r.hset(:schedules, name, JSON.generate(config)) }
107
- Sidekiq.redis { |r| r.zadd(:schedules_changed, Time.now.to_f, name) }
107
+ SidekiqScheduler::RedisManager.set_job_schedule(name, config)
108
+ SidekiqScheduler::RedisManager.add_schedule_change(name)
108
109
  end
109
110
  config
110
111
  end
111
112
 
112
113
  # remove a given schedule by name
113
114
  def remove_schedule(name)
114
- Sidekiq.redis { |r| r.hdel(:schedules, name) }
115
- Sidekiq.redis { |r| r.zadd(:schedules_changed, Time.now.to_f, name) }
115
+ SidekiqScheduler::RedisManager.remove_job_schedule(name)
116
+ SidekiqScheduler::RedisManager.add_schedule_change(name)
116
117
  end
117
118
 
118
119
  private
@@ -1,5 +1,5 @@
1
1
  module SidekiqScheduler
2
2
 
3
- VERSION = '2.1.9'
3
+ VERSION = '2.1.10'
4
4
 
5
5
  end
@@ -3,13 +3,13 @@ require 'thwait'
3
3
  require 'sidekiq/util'
4
4
  require 'sidekiq-scheduler/manager'
5
5
  require 'sidekiq-scheduler/rufus_utils'
6
+ require_relative '../sidekiq-scheduler/redis_manager'
6
7
  require 'json'
7
8
 
8
9
  module Sidekiq
9
10
  class Scheduler
10
11
  extend Sidekiq::Util
11
12
 
12
- REGISTERED_JOBS_THRESHOLD_IN_SECONDS = 24 * 60 * 60
13
13
  RUFUS_METADATA_KEYS = %w(description at cron every in interval enabled)
14
14
 
15
15
  # We expect rufus jobs to have #params
@@ -27,6 +27,9 @@ module Sidekiq
27
27
  # Set to update the schedule in runtime in a given time period.
28
28
  attr_accessor :dynamic
29
29
 
30
+ # Set to update the schedule in runtime dynamically per this period.
31
+ attr_accessor :dynamic_every
32
+
30
33
  # Set to schedule jobs only when will be pushed to queues listened by sidekiq
31
34
  attr_accessor :listened_queues_only
32
35
 
@@ -55,7 +58,7 @@ module Sidekiq
55
58
  if dynamic
56
59
  Sidekiq.reload_schedule!
57
60
  @current_changed_score = Time.now.to_f
58
- rufus_scheduler.every('5s') do
61
+ rufus_scheduler.every(dynamic_every) do
59
62
  update_schedule
60
63
  end
61
64
  end
@@ -137,8 +140,10 @@ module Sidekiq
137
140
  # @param [String] name The job's name
138
141
  # @param [Time] next_time The job's next time execution
139
142
  def update_job_next_time(name, next_time)
140
- Sidekiq.redis do |r|
141
- next_time ? r.hset(next_times_key, name, next_time) : r.hdel(next_times_key, name)
143
+ if next_time
144
+ SidekiqScheduler::RedisManager.set_job_next_time(name, next_time)
145
+ else
146
+ SidekiqScheduler::RedisManager.remove_job_next_time(name)
142
147
  end
143
148
  end
144
149
 
@@ -147,7 +152,7 @@ module Sidekiq
147
152
  # @param [String] name The job's name
148
153
  # @param [Time] last_time The job's last execution time
149
154
  def update_job_last_time(name, last_time)
150
- Sidekiq.redis { |r| r.hset(last_times_key, name, last_time) } if last_time
155
+ SidekiqScheduler::RedisManager.set_job_last_time(name, last_time) if last_time
151
156
  end
152
157
 
153
158
  # Returns true if the given schedule config hash matches the current
@@ -215,9 +220,7 @@ module Sidekiq
215
220
 
216
221
  def update_schedule
217
222
  last_changed_score, @current_changed_score = @current_changed_score, Time.now.to_f
218
- schedule_changes = Sidekiq.redis do |r|
219
- r.zrangebyscore :schedules_changed, last_changed_score, "(#{@current_changed_score}"
220
- end
223
+ schedule_changes = SidekiqScheduler::RedisManager.get_schedule_changes(last_changed_score, @current_changed_score)
221
224
 
222
225
  if schedule_changes.size > 0
223
226
  logger.info 'Updating schedule'
@@ -318,52 +321,11 @@ module Sidekiq
318
321
  #
319
322
  # @return [Boolean] true if the job was registered, false when otherwise
320
323
  def register_job_instance(job_name, time)
321
- pushed_job_key = pushed_job_key(job_name)
322
-
323
- registered, _ = Sidekiq.redis do |r|
324
- r.pipelined do
325
- r.zadd(pushed_job_key, time.to_i, time.to_i)
326
- r.expire(pushed_job_key, REGISTERED_JOBS_THRESHOLD_IN_SECONDS)
327
- end
328
- end
329
-
330
- registered
324
+ SidekiqScheduler::RedisManager.register_job_instance(job_name, time)
331
325
  end
332
326
 
333
327
  def remove_elder_job_instances(job_name)
334
- Sidekiq.redis do |r|
335
- r.zremrangebyscore(pushed_job_key(job_name), 0, Time.now.to_i - REGISTERED_JOBS_THRESHOLD_IN_SECONDS)
336
- end
337
- end
338
-
339
- # Returns the key of the Redis sorted set used to store job enqueues
340
- #
341
- # @param [String] job_name The name of the job
342
- #
343
- # @return [String]
344
- def pushed_job_key(job_name)
345
- "sidekiq-scheduler:pushed:#{job_name}"
346
- end
347
-
348
- # Returns the key of the Redis hash for job's execution times hash
349
- #
350
- # @return [String] with the key
351
- def next_times_key
352
- 'sidekiq-scheduler:next_times'
353
- end
354
-
355
- # Returns the key of the Redis hash for job's last execution times hash
356
- #
357
- # @return [String] with the key
358
- def last_times_key
359
- 'sidekiq-scheduler:last_times'
360
- end
361
-
362
- # Returns the Redis's key for saving schedule states.
363
- #
364
- # @return [String] with the key
365
- def schedules_state_key
366
- 'sidekiq-scheduler:states'
328
+ SidekiqScheduler::RedisManager.remove_elder_job_instances(job_name)
367
329
  end
368
330
 
369
331
  def job_enabled?(name)
@@ -405,7 +367,7 @@ module Sidekiq
405
367
  # @param name [String] with the schedule's name
406
368
  # @return [Hash] with the schedule's state
407
369
  def schedule_state(name)
408
- state = Sidekiq.redis { |r| r.hget(schedules_state_key, name) }
370
+ state = SidekiqScheduler::RedisManager.get_job_state(name)
409
371
 
410
372
  state ? JSON.parse(state) : {}
411
373
  end
@@ -415,7 +377,7 @@ module Sidekiq
415
377
  # @param name [String] with the schedule's name
416
378
  # @param name [Hash] with the schedule's state
417
379
  def set_schedule_state(name, state)
418
- Sidekiq.redis { |r| r.hset(schedules_state_key, name, JSON.generate(state)) }
380
+ SidekiqScheduler::RedisManager.set_job_state(name, state)
419
381
  end
420
382
 
421
383
  # Adds a Hash with schedule metadata as the last argument to call the worker.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-scheduler
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.9
4
+ version: 2.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Morton Jonuschat
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-08-31 00:00:00.000000000 Z
12
+ date: 2017-10-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sidekiq
@@ -29,16 +29,22 @@ dependencies:
29
29
  name: redis
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - "~>"
32
+ - - ">="
33
33
  - !ruby/object:Gem::Version
34
34
  version: '3'
35
+ - - "<"
36
+ - !ruby/object:Gem::Version
37
+ version: '5'
35
38
  type: :runtime
36
39
  prerelease: false
37
40
  version_requirements: !ruby/object:Gem::Requirement
38
41
  requirements:
39
- - - "~>"
42
+ - - ">="
40
43
  - !ruby/object:Gem::Version
41
44
  version: '3'
45
+ - - "<"
46
+ - !ruby/object:Gem::Version
47
+ version: '5'
42
48
  - !ruby/object:Gem::Dependency
43
49
  name: rufus-scheduler
44
50
  requirement: !ruby/object:Gem::Requirement
@@ -235,6 +241,7 @@ files:
235
241
  - lib/sidekiq-scheduler.rb
236
242
  - lib/sidekiq-scheduler/job_presenter.rb
237
243
  - lib/sidekiq-scheduler/manager.rb
244
+ - lib/sidekiq-scheduler/redis_manager.rb
238
245
  - lib/sidekiq-scheduler/rufus_utils.rb
239
246
  - lib/sidekiq-scheduler/schedule.rb
240
247
  - lib/sidekiq-scheduler/utils.rb