sidekiq-scheduler 2.2.2 → 3.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2747b49a61187194593a74c6cdba9979d26fa99a
4
- data.tar.gz: 3cda4ae1544bff66d97b7a94503a75aca57a237f
3
+ metadata.gz: 9baa032bdb214ffc3f0d229c0a5181f36a8be200
4
+ data.tar.gz: 011c8eaf157ceea568cc64ba125af2c7cc0706db
5
5
  SHA512:
6
- metadata.gz: fc793b476d9a19bbc541dedef091e52ffabf08d88800c4a423121e9181d6042b39bd06498bec1c98dc3763b7ee4bf6b0f13e43896bf7d58d672d3570f5039776
7
- data.tar.gz: 97da3f5c4e75468877f59e713407554364befe57b8891371dab4b17387b2f18cbba633695b28cbb99e53f6f63ac090ee3ae3bd64abf2875062f49db71cf45d84
6
+ metadata.gz: 7c2f4d826b83179acee13b422dfb4ab00542b747b17d4f30685d675e8938d1b4b8b06d362a348590a9363b65d4acbb3711c0577db4b1ab6f5d5a0cac32d44b97
7
+ data.tar.gz: db0db5b8556f897a50800856031102e3f2001d8bf4658e9daabd8b41a269da92d475b37f5f01026539c435433636cb441cc3123c7fe292091b43c7c5ff04a232
data/README.md CHANGED
@@ -30,6 +30,8 @@
30
30
  `sidekiq-scheduler` is an extension to [Sidekiq](http://github.com/mperham/sidekiq) that
31
31
  pushes jobs in a scheduled way, mimicking cron utility.
32
32
 
33
+ __Note:__ If you are looking for version 2.2.*, go to [2.2-stable branch](https://github.com/moove-it/sidekiq-scheduler/tree/2.2-stable).
34
+
33
35
  ## Installation
34
36
 
35
37
  ``` shell
@@ -228,7 +230,7 @@ require 'sidekiq-scheduler'
228
230
  Sidekiq.configure_server do |config|
229
231
  config.on(:startup) do
230
232
  Sidekiq.schedule = YAML.load_file(File.expand_path('../../sidekiq_scheduler.yml', __FILE__))
231
- Sidekiq::Scheduler.reload_schedule!
233
+ SidekiqScheduler::Scheduler.instance.reload_schedule!
232
234
  end
233
235
  end
234
236
  ```
@@ -256,7 +258,7 @@ If `:dynamic` flag is set to `false`, you'll have to reload the schedule manuall
256
258
  side:
257
259
 
258
260
  ``` ruby
259
- Sidekiq::Scheduler.reload_schedule!
261
+ SidekiqScheduler::Scheduler.instance.reload_schedule!
260
262
  ```
261
263
 
262
264
  Invoke `Sidekiq.get_schedule` to obtain the current schedule:
@@ -367,12 +369,12 @@ if Rails.env == 'production' && (defined?(Rails::Server) || defined?(Unicorn))
367
369
 
368
370
  config.on(:startup) do
369
371
  Sidekiq.schedule = YAML.load_file(File.expand_path('../../scheduler.yml', __FILE__))
370
- Sidekiq::Scheduler.reload_schedule!
372
+ SidekiqScheduler::Scheduler.instance.reload_schedule!
371
373
  end
372
374
  end
373
375
  else
374
- Sidekiq::Scheduler.enabled = false
375
- puts "Sidekiq::Scheduler.enabled is #{Sidekiq::Scheduler.enabled.inspect}"
376
+ SidekiqScheduler::Scheduler.instance.enabled = false
377
+ puts "SidekiqScheduler::Scheduler.instance.enabled is #{SidekiqScheduler::Scheduler.instance.enabled.inspect}"
376
378
  end
377
379
  ```
378
380
 
@@ -382,6 +384,6 @@ MIT License
382
384
 
383
385
  ## Copyright
384
386
 
385
- Copyright 2013 - 2017 Moove-IT.
387
+ Copyright 2013 - 2018 Moove-IT.
386
388
  Copyright 2012 Morton Jonuschat.
387
389
  Some parts copyright 2010 Ben VandenBos.
@@ -10,35 +10,10 @@ require_relative 'sidekiq-scheduler/extensions/schedule'
10
10
  Sidekiq.configure_server do |config|
11
11
 
12
12
  config.on(:startup) do
13
- dynamic = Sidekiq::Scheduler.dynamic
14
- dynamic = dynamic.nil? ? config.options.fetch(:dynamic, false) : dynamic
15
-
16
- dynamic_every = Sidekiq::Scheduler.dynamic_every
17
- dynamic_every = dynamic_every.nil? ? config.options.fetch(:dynamic_every, '5s') : dynamic_every
18
-
19
- enabled = Sidekiq::Scheduler.enabled
20
- enabled = enabled.nil? ? config.options.fetch(:enabled, true) : enabled
21
-
22
- scheduler = config.options.fetch(:scheduler, {})
23
-
24
- listened_queues_only = Sidekiq::Scheduler.listened_queues_only
25
- listened_queues_only = listened_queues_only.nil? ? scheduler[:listened_queues_only] : listened_queues_only
26
-
27
- schedule = Sidekiq.schedule
28
- schedule ||= config.options[:schedule] || {}
29
-
30
- scheduler_options = {
31
- dynamic: dynamic,
32
- dynamic_every: dynamic_every,
33
- enabled: enabled,
34
- schedule: schedule,
35
- listened_queues_only: listened_queues_only
36
- }
37
-
38
13
  # schedules_changed's type was changed from SET to ZSET, so we remove old versions at startup
39
14
  SidekiqScheduler::RedisManager.clean_schedules_changed
40
15
 
41
- schedule_manager = SidekiqScheduler::Manager.new(scheduler_options)
16
+ schedule_manager = SidekiqScheduler::Manager.new(config.options)
42
17
  config.options[:schedule_manager] = schedule_manager
43
18
  config.options[:schedule_manager].start
44
19
  end
@@ -14,26 +14,48 @@ module SidekiqScheduler
14
14
  class Manager
15
15
  include Sidekiq::Util
16
16
 
17
+ DEFAULT_SCHEDULER_OPTIONS = {
18
+ enabled: true,
19
+ dynamic: false,
20
+ dynamic_every: '5s',
21
+ schedule: {}
22
+ }
23
+
17
24
  def initialize(options)
18
- SidekiqScheduler::Scheduler.enabled = options[:enabled]
19
- SidekiqScheduler::Scheduler.dynamic = options[:dynamic]
20
- SidekiqScheduler::Scheduler.dynamic_every = options[:dynamic_every]
21
- SidekiqScheduler::Scheduler.listened_queues_only = options[:listened_queues_only]
22
- Sidekiq.schedule = options[:schedule] if SidekiqScheduler::Scheduler.enabled
25
+ scheduler_options = load_scheduler_options(options)
26
+
27
+ @scheduler_instance = SidekiqScheduler::Scheduler.new(scheduler_options)
28
+ SidekiqScheduler::Scheduler.instance = @scheduler_instance
29
+ Sidekiq.schedule = scheduler_options[:schedule] if @scheduler_instance.enabled
23
30
  end
24
31
 
25
32
  def stop
26
- SidekiqScheduler::Scheduler.clear_schedule!
33
+ @scheduler_instance.clear_schedule!
27
34
  end
28
35
 
29
36
  def start
30
- SidekiqScheduler::Scheduler.load_schedule!
37
+ @scheduler_instance.load_schedule!
31
38
  end
32
39
 
33
40
  def reset
34
41
  clear_scheduled_work
35
42
  end
36
43
 
37
- end
44
+ private
45
+
46
+ def load_scheduler_options(options)
47
+ options[:listened_queues_only] = options.fetch(:scheduler, {})[:listened_queues_only]
48
+ scheduler_options = DEFAULT_SCHEDULER_OPTIONS.merge(options)
38
49
 
50
+ current_options = {
51
+ enabled: SidekiqScheduler::Scheduler.enabled,
52
+ dynamic: SidekiqScheduler::Scheduler.dynamic,
53
+ dynamic_every: SidekiqScheduler::Scheduler.dynamic_every,
54
+ schedule: Sidekiq.schedule,
55
+ listened_queues_only: SidekiqScheduler::Scheduler.listened_queues_only
56
+ }.delete_if { |_, value| value.nil? }
57
+
58
+ scheduler_options.merge(current_options)
59
+ end
60
+ end
39
61
  end
@@ -10,400 +10,345 @@ module SidekiqScheduler
10
10
  class Scheduler
11
11
  extend Sidekiq::Util
12
12
 
13
- RUFUS_METADATA_KEYS = %w(description at cron every in interval enabled)
14
-
15
13
  # We expect rufus jobs to have #params
16
14
  Rufus::Scheduler::Job.module_eval do
17
-
18
15
  alias_method :params, :opts
19
-
20
16
  end
21
17
 
22
- class << self
18
+ # Set to enable or disable the scheduler.
19
+ attr_accessor :enabled
23
20
 
24
- # Set to enable or disable the scheduler.
25
- attr_accessor :enabled
21
+ # Set to update the schedule in runtime in a given time period.
22
+ attr_accessor :dynamic
26
23
 
27
- # Set to update the schedule in runtime in a given time period.
28
- attr_accessor :dynamic
24
+ # Set to update the schedule in runtime dynamically per this period.
25
+ attr_accessor :dynamic_every
29
26
 
30
- # Set to update the schedule in runtime dynamically per this period.
31
- attr_accessor :dynamic_every
27
+ # Set to schedule jobs only when will be pushed to queues listened by sidekiq
28
+ attr_accessor :listened_queues_only
32
29
 
33
- # Set to schedule jobs only when will be pushed to queues listened by sidekiq
34
- attr_accessor :listened_queues_only
30
+ class << self
35
31
 
36
- # the Rufus::Scheduler jobs that are scheduled
37
- def scheduled_jobs
38
- @@scheduled_jobs
32
+ def instance
33
+ @instance = new unless @instance
34
+ @instance
39
35
  end
40
36
 
41
- def print_schedule
42
- if rufus_scheduler
43
- logger.info "Scheduling Info\tLast Run"
44
- scheduler_jobs = rufus_scheduler.all_jobs
45
- scheduler_jobs.each_value do |v|
46
- logger.info "#{v.t}\t#{v.last}\t"
47
- end
48
- end
37
+ def instance=(value)
38
+ @instance = value
49
39
  end
50
40
 
51
- # Pulls the schedule from Sidekiq.schedule and loads it into the
52
- # rufus scheduler instance
53
- def load_schedule!
54
- if enabled
55
- logger.info 'Loading Schedule'
56
-
57
- # Load schedule from redis for the first time if dynamic
58
- if dynamic
59
- Sidekiq.reload_schedule!
60
- @current_changed_score = Time.now.to_f
61
- rufus_scheduler.every(dynamic_every) do
62
- update_schedule
63
- end
64
- end
65
-
66
- logger.info 'Schedule empty! Set Sidekiq.schedule' if Sidekiq.schedule.empty?
67
-
41
+ def method_missing(method, *arguments, &block)
42
+ instance_methods.include?(method) ? instance.public_send(method, *arguments) : super
43
+ end
44
+ end
68
45
 
69
- @@scheduled_jobs = {}
70
- queues = sidekiq_queues
46
+ def initialize(options = {})
47
+ self.enabled = options[:enabled]
48
+ self.dynamic = options[:dynamic]
49
+ self.dynamic_every = options[:dynamic_every]
50
+ self.listened_queues_only = options[:listened_queues_only]
51
+ end
71
52
 
72
- Sidekiq.schedule.each do |name, config|
73
- if !listened_queues_only || enabled_queue?(config['queue'].to_s, queues)
74
- load_schedule_job(name, config)
75
- else
76
- logger.info { "Ignoring #{name}, job's queue is not enabled." }
77
- end
78
- end
53
+ # the Rufus::Scheduler jobs that are scheduled
54
+ def scheduled_jobs
55
+ @scheduled_jobs
56
+ end
79
57
 
80
- logger.info 'Schedules Loaded'
81
- else
82
- logger.info 'SidekiqScheduler is disabled'
58
+ def print_schedule
59
+ if rufus_scheduler
60
+ Sidekiq.logger.info "Scheduling Info\tLast Run"
61
+ scheduler_jobs = rufus_scheduler.all_jobs
62
+ scheduler_jobs.each_value do |v|
63
+ Sidekiq.logger.info "#{v.t}\t#{v.last}\t"
83
64
  end
84
65
  end
66
+ end
85
67
 
86
- # Loads a job schedule into the Rufus::Scheduler and stores it in @@scheduled_jobs
87
- def load_schedule_job(name, config)
88
- # If rails_env is set in the config, enforce ENV['RAILS_ENV'] as
89
- # required for the jobs to be scheduled. If rails_env is missing, the
90
- # job should be scheduled regardless of what ENV['RAILS_ENV'] is set
91
- # to.
92
- if config['rails_env'].nil? || rails_env_matches?(config)
93
- logger.info "Scheduling #{name} #{config}"
94
- interval_defined = false
95
- interval_types = %w{cron every at in interval}
96
- interval_types.each do |interval_type|
97
- config_interval_type = config[interval_type]
98
-
99
- if !config_interval_type.nil? && config_interval_type.length > 0
68
+ # Pulls the schedule from Sidekiq.schedule and loads it into the
69
+ # rufus scheduler instance
70
+ def load_schedule!
71
+ if enabled
72
+ Sidekiq.logger.info 'Loading Schedule'
100
73
 
101
- schedule, options = SidekiqScheduler::RufusUtils.normalize_schedule_options(config_interval_type)
74
+ # Load schedule from redis for the first time if dynamic
75
+ if dynamic
76
+ Sidekiq.reload_schedule!
77
+ @current_changed_score = Time.now.to_f
78
+ rufus_scheduler.every(dynamic_every) do
79
+ update_schedule
80
+ end
81
+ end
102
82
 
103
- rufus_job = new_job(name, interval_type, config, schedule, options)
104
- @@scheduled_jobs[name] = rufus_job
105
- update_job_next_time(name, rufus_job.next_time)
83
+ Sidekiq.logger.info 'Schedule empty! Set Sidekiq.schedule' if Sidekiq.schedule.empty?
106
84
 
107
- interval_defined = true
108
85
 
109
- break
110
- end
111
- end
86
+ @scheduled_jobs = {}
87
+ queues = sidekiq_queues
112
88
 
113
- unless interval_defined
114
- logger.info "no #{interval_types.join(' / ')} found for #{config['class']} (#{name}) - skipping"
89
+ Sidekiq.schedule.each do |name, config|
90
+ if !listened_queues_only || enabled_queue?(config['queue'].to_s, queues)
91
+ load_schedule_job(name, config)
92
+ else
93
+ Sidekiq.logger.info { "Ignoring #{name}, job's queue is not enabled." }
115
94
  end
116
95
  end
117
- end
118
96
 
119
- # Pushes the job into Sidekiq if not already pushed for the given time
120
- #
121
- # @param [String] job_name The job's name
122
- # @param [Time] time The time when the job got cleared for triggering
123
- # @param [Hash] config Job's config hash
124
- def idempotent_job_enqueue(job_name, time, config)
125
- registered = register_job_instance(job_name, time)
97
+ Sidekiq.logger.info 'Schedules Loaded'
98
+ else
99
+ Sidekiq.logger.info 'SidekiqScheduler is disabled'
100
+ end
101
+ end
126
102
 
127
- if registered
128
- logger.info "queueing #{config['class']} (#{job_name})"
103
+ # Loads a job schedule into the Rufus::Scheduler and stores it in @scheduled_jobs
104
+ def load_schedule_job(name, config)
105
+ # If rails_env is set in the config, enforce ENV['RAILS_ENV'] as
106
+ # required for the jobs to be scheduled. If rails_env is missing, the
107
+ # job should be scheduled regardless of what ENV['RAILS_ENV'] is set
108
+ # to.
109
+ if config['rails_env'].nil? || rails_env_matches?(config)
110
+ Sidekiq.logger.info "Scheduling #{name} #{config}"
111
+ interval_defined = false
112
+ interval_types = %w(cron every at in interval)
113
+ interval_types.each do |interval_type|
114
+ config_interval_type = config[interval_type]
129
115
 
130
- handle_errors { enqueue_job(config, time) }
116
+ if !config_interval_type.nil? && config_interval_type.length > 0
131
117
 
132
- remove_elder_job_instances(job_name)
133
- else
134
- logger.debug { "Ignoring #{job_name} job as it has been already enqueued" }
135
- end
136
- end
118
+ schedule, options = SidekiqScheduler::RufusUtils.normalize_schedule_options(config_interval_type)
137
119
 
138
- # Pushes job's next time execution
139
- #
140
- # @param [String] name The job's name
141
- # @param [Time] next_time The job's next time execution
142
- def update_job_next_time(name, next_time)
143
- if next_time
144
- SidekiqScheduler::RedisManager.set_job_next_time(name, next_time)
145
- else
146
- SidekiqScheduler::RedisManager.remove_job_next_time(name)
147
- end
148
- end
120
+ rufus_job = new_job(name, interval_type, config, schedule, options)
121
+ @scheduled_jobs[name] = rufus_job
122
+ SidekiqScheduler::Utils.update_job_next_time(name, rufus_job.next_time)
149
123
 
150
- # Pushes job's last execution time
151
- #
152
- # @param [String] name The job's name
153
- # @param [Time] last_time The job's last execution time
154
- def update_job_last_time(name, last_time)
155
- SidekiqScheduler::RedisManager.set_job_last_time(name, last_time) if last_time
156
- end
124
+ interval_defined = true
157
125
 
158
- # Returns true if the given schedule config hash matches the current
159
- # ENV['RAILS_ENV']
160
- def rails_env_matches?(config)
161
- config['rails_env'] && ENV['RAILS_ENV'] && config['rails_env'].gsub(/\s/, '').split(',').include?(ENV['RAILS_ENV'])
162
- end
126
+ break
127
+ end
128
+ end
163
129
 
164
- def handle_errors
165
- begin
166
- yield
167
- rescue StandardError => e
168
- logger.info "#{e.class.name}: #{e.message}"
130
+ unless interval_defined
131
+ Sidekiq.logger.info "no #{interval_types.join(' / ')} found for #{config['class']} (#{name}) - skipping"
169
132
  end
170
133
  end
134
+ end
171
135
 
172
- # Enqueue a job based on a config hash
173
- #
174
- # @param job_config [Hash] the job configuration
175
- # @param time [Time] time the job is enqueued
176
- def enqueue_job(job_config, time=Time.now)
177
- config = prepare_arguments(job_config.dup)
136
+ # Pushes the job into Sidekiq if not already pushed for the given time
137
+ #
138
+ # @param [String] job_name The job's name
139
+ # @param [Time] time The time when the job got cleared for triggering
140
+ # @param [Hash] config Job's config hash
141
+ def idempotent_job_enqueue(job_name, time, config)
142
+ registered = SidekiqScheduler::RedisManager.register_job_instance(job_name, time)
178
143
 
179
- if config.delete('include_metadata')
180
- config['args'] = arguments_with_metadata(config['args'], scheduled_at: time.to_f)
181
- end
144
+ if registered
145
+ Sidekiq.logger.info "queueing #{config['class']} (#{job_name})"
182
146
 
183
- if active_job_enqueue?(config['class'])
184
- enqueue_with_active_job(config)
185
- else
186
- enqueue_with_sidekiq(config)
187
- end
188
- end
147
+ handle_errors { enqueue_job(config, time) }
189
148
 
190
- def rufus_scheduler_options
191
- @rufus_scheduler_options ||= {}
149
+ SidekiqScheduler::RedisManager.remove_elder_job_instances(job_name)
150
+ else
151
+ Sidekiq.logger.debug { "Ignoring #{job_name} job as it has been already enqueued" }
192
152
  end
153
+ end
193
154
 
194
- def rufus_scheduler_options=(options)
195
- @rufus_scheduler_options = options
196
- end
155
+ # Enqueue a job based on a config hash
156
+ #
157
+ # @param job_config [Hash] the job configuration
158
+ # @param time [Time] time the job is enqueued
159
+ def enqueue_job(job_config, time = Time.now)
160
+ config = prepare_arguments(job_config.dup)
197
161
 
198
- def rufus_scheduler
199
- @rufus_scheduler ||= new_rufus_scheduler
162
+ if config.delete('include_metadata')
163
+ config['args'] = arguments_with_metadata(config['args'], scheduled_at: time.to_f)
200
164
  end
201
165
 
202
- # Stops old rufus scheduler and creates a new one. Returns the new
203
- # rufus scheduler
204
- #
205
- # @param [Symbol] stop_option The option to be passed to Rufus::Scheduler#stop
206
- def clear_schedule!(stop_option = :wait)
207
- rufus_scheduler.stop(stop_option)
208
- @rufus_scheduler = nil
209
- @@scheduled_jobs = {}
210
- rufus_scheduler
166
+ if active_job_enqueue?(config['class'])
167
+ SidekiqScheduler::Utils.enqueue_with_active_job(config)
168
+ else
169
+ SidekiqScheduler::Utils.enqueue_with_sidekiq(config)
211
170
  end
171
+ end
212
172
 
213
- def reload_schedule!
214
- if enabled
215
- logger.info 'Reloading Schedule'
216
- clear_schedule!
217
- load_schedule!
218
- else
219
- logger.info 'SidekiqScheduler is disabled'
220
- end
221
- end
173
+ def rufus_scheduler_options
174
+ @rufus_scheduler_options ||= {}
175
+ end
222
176
 
223
- def update_schedule
224
- last_changed_score, @current_changed_score = @current_changed_score, Time.now.to_f
225
- schedule_changes = SidekiqScheduler::RedisManager.get_schedule_changes(last_changed_score, @current_changed_score)
177
+ def rufus_scheduler_options=(options)
178
+ @rufus_scheduler_options = options
179
+ end
226
180
 
227
- if schedule_changes.size > 0
228
- logger.info 'Updating schedule'
181
+ def rufus_scheduler
182
+ @rufus_scheduler ||= SidekiqScheduler::Utils.new_rufus_scheduler(rufus_scheduler_options)
183
+ end
229
184
 
230
- Sidekiq.reload_schedule!
231
- schedule_changes.each do |schedule_name|
232
- if Sidekiq.schedule.keys.include?(schedule_name)
233
- unschedule_job(schedule_name)
234
- load_schedule_job(schedule_name, Sidekiq.schedule[schedule_name])
235
- else
236
- unschedule_job(schedule_name)
237
- end
238
- end
239
- logger.info 'Schedule updated'
240
- end
241
- end
185
+ # Stops old rufus scheduler and creates a new one. Returns the new
186
+ # rufus scheduler
187
+ #
188
+ # @param [Symbol] stop_option The option to be passed to Rufus::Scheduler#stop
189
+ def clear_schedule!(stop_option = :wait)
190
+ rufus_scheduler.stop(stop_option)
191
+ @rufus_scheduler = nil
192
+ @@scheduled_jobs = {}
193
+ rufus_scheduler
194
+ end
242
195
 
243
- def unschedule_job(name)
244
- if scheduled_jobs[name]
245
- logger.debug "Removing schedule #{name}"
246
- scheduled_jobs[name].unschedule
247
- scheduled_jobs.delete(name)
248
- end
196
+ def reload_schedule!
197
+ if enabled
198
+ Sidekiq.logger.info 'Reloading Schedule'
199
+ clear_schedule!
200
+ load_schedule!
201
+ else
202
+ Sidekiq.logger.info 'SidekiqScheduler is disabled'
249
203
  end
204
+ end
250
205
 
251
- def enqueue_with_active_job(config)
252
- options = {
253
- queue: config['queue']
254
- }.keep_if { |_, v| !v.nil? }
255
-
256
- initialize_active_job(config['class'], config['args']).enqueue(options)
257
- end
206
+ def update_schedule
207
+ last_changed_score, @current_changed_score = @current_changed_score, Time.now.to_f
208
+ schedule_changes = SidekiqScheduler::RedisManager.get_schedule_changes(last_changed_score, @current_changed_score)
258
209
 
259
- def enqueue_with_sidekiq(config)
260
- Sidekiq::Client.push(sanitize_job_config(config))
261
- end
210
+ if schedule_changes.size > 0
211
+ Sidekiq.logger.info 'Updating schedule'
262
212
 
263
- def initialize_active_job(klass, args)
264
- if args.is_a?(Array)
265
- klass.new(*args)
266
- else
267
- klass.new(args)
213
+ Sidekiq.reload_schedule!
214
+ schedule_changes.each do |schedule_name|
215
+ if Sidekiq.schedule.keys.include?(schedule_name)
216
+ unschedule_job(schedule_name)
217
+ load_schedule_job(schedule_name, Sidekiq.schedule[schedule_name])
218
+ else
219
+ unschedule_job(schedule_name)
220
+ end
268
221
  end
222
+ Sidekiq.logger.info 'Schedule updated'
269
223
  end
224
+ end
270
225
 
271
- # Returns true if the enqueuing needs to be done for an ActiveJob
272
- # class false otherwise.
273
- #
274
- # @param [Class] klass the class to check is decendant from ActiveJob
275
- #
276
- # @return [Boolean]
277
- def active_job_enqueue?(klass)
278
- klass.is_a?(Class) && defined?(ActiveJob::Enqueuing) &&
279
- klass.included_modules.include?(ActiveJob::Enqueuing)
280
- end
281
-
282
- # Convert the given arguments in the format expected to be enqueued.
283
- #
284
- # @param [Hash] config the options to be converted
285
- # @option config [String] class the job class
286
- # @option config [Hash/Array] args the arguments to be passed to the job
287
- # class
288
- #
289
- # @return [Hash]
290
- def prepare_arguments(config)
291
- config['class'] = try_to_constantize(config['class'])
292
-
293
- if config['args'].is_a?(Hash)
294
- config['args'].symbolize_keys! if config['args'].respond_to?(:symbolize_keys!)
295
- else
296
- config['args'] = Array(config['args'])
297
- end
226
+ def job_enabled?(name)
227
+ job = Sidekiq.schedule[name]
228
+ schedule_state(name).fetch('enabled', job.fetch('enabled', true)) if job
229
+ end
298
230
 
299
- config
300
- end
231
+ def toggle_job_enabled(name)
232
+ state = schedule_state(name)
233
+ state['enabled'] = !job_enabled?(name)
234
+ set_schedule_state(name, state)
235
+ end
301
236
 
302
- def try_to_constantize(klass)
303
- klass.is_a?(String) ? klass.constantize : klass
304
- rescue NameError
305
- klass
306
- end
237
+ private
307
238
 
308
- # Returns true if a job's queue is included in the array of queues
309
- #
310
- # If queues are empty, returns true.
311
- #
312
- # @param [String] job_queue Job's queue name
313
- # @param [Array<String>] queues
314
- #
315
- # @return [Boolean]
316
- def enabled_queue?(job_queue, queues)
317
- queues.empty? || queues.include?(job_queue)
318
- end
239
+ def new_job(name, interval_type, config, schedule, options)
240
+ options = options.merge({ :job => true, :tags => [name] })
319
241
 
320
- # Registers a queued job instance
321
- #
322
- # @param [String] job_name The job's name
323
- # @param [Time] time Time at which the job was cleared by the scheduler
324
- #
325
- # @return [Boolean] true if the job was registered, false when otherwise
326
- def register_job_instance(job_name, time)
327
- SidekiqScheduler::RedisManager.register_job_instance(job_name, time)
242
+ rufus_scheduler.send(interval_type, schedule, options) do |job, time|
243
+ idempotent_job_enqueue(name, time, SidekiqScheduler::Utils.sanitize_job_config(config)) if job_enabled?(name)
328
244
  end
245
+ end
329
246
 
330
- def remove_elder_job_instances(job_name)
331
- SidekiqScheduler::RedisManager.remove_elder_job_instances(job_name)
247
+ def unschedule_job(name)
248
+ if scheduled_jobs[name]
249
+ Sidekiq.logger.debug "Removing schedule #{name}"
250
+ scheduled_jobs[name].unschedule
251
+ scheduled_jobs.delete(name)
332
252
  end
253
+ end
333
254
 
334
- def job_enabled?(name)
335
- job = Sidekiq.schedule[name]
336
- schedule_state(name).fetch('enabled', job.fetch('enabled', true)) if job
337
- end
255
+ # Retrieves a schedule state
256
+ #
257
+ # @param name [String] with the schedule's name
258
+ # @return [Hash] with the schedule's state
259
+ def schedule_state(name)
260
+ state = SidekiqScheduler::RedisManager.get_job_state(name)
338
261
 
339
- def toggle_job_enabled(name)
340
- state = schedule_state(name)
341
- state['enabled'] = !job_enabled?(name)
342
- set_schedule_state(name, state)
343
- end
262
+ state ? JSON.parse(state) : {}
263
+ end
344
264
 
345
- private
265
+ # Saves a schedule state
266
+ #
267
+ # @param name [String] with the schedule's name
268
+ # @param name [Hash] with the schedule's state
269
+ def set_schedule_state(name, state)
270
+ SidekiqScheduler::RedisManager.set_job_state(name, state)
271
+ end
346
272
 
347
- def new_rufus_scheduler
348
- Rufus::Scheduler.new(rufus_scheduler_options).tap do |scheduler|
349
- scheduler.define_singleton_method(:on_post_trigger) do |job, triggered_time|
350
- SidekiqScheduler::Scheduler.update_job_last_time(job.tags[0], triggered_time)
351
- SidekiqScheduler::Scheduler.update_job_next_time(job.tags[0], job.next_time)
352
- end
353
- end
273
+ # Adds a Hash with schedule metadata as the last argument to call the worker.
274
+ # It currently returns the schedule time as a Float number representing the milisencods
275
+ # since epoch.
276
+ #
277
+ # @example with hash argument
278
+ # arguments_with_metadata({value: 1}, scheduled_at: Time.now)
279
+ # #=> [{value: 1}, {scheduled_at: <miliseconds since epoch>}]
280
+ #
281
+ # @param args [Array|Hash]
282
+ # @param metadata [Hash]
283
+ # @return [Array] arguments with added metadata
284
+ def arguments_with_metadata(args, metadata)
285
+ if args.is_a? Array
286
+ [*args, metadata]
287
+ else
288
+ [args, metadata]
354
289
  end
290
+ end
355
291
 
356
- def new_job(name, interval_type, config, schedule, options)
357
- options = options.merge({ :job => true, :tags => [name] })
292
+ def sidekiq_queues
293
+ Sidekiq.options[:queues].map(&:to_s)
294
+ end
358
295
 
359
- rufus_scheduler.send(interval_type, schedule, options) do |job, time|
360
- idempotent_job_enqueue(name, time, sanitize_job_config(config)) if job_enabled?(name)
361
- end
362
- end
296
+ # Returns true if a job's queue is included in the array of queues
297
+ #
298
+ # If queues are empty, returns true.
299
+ #
300
+ # @param [String] job_queue Job's queue name
301
+ # @param [Array<String>] queues
302
+ #
303
+ # @return [Boolean]
304
+ def enabled_queue?(job_queue, queues)
305
+ queues.empty? || queues.include?(job_queue)
306
+ end
363
307
 
364
- def sanitize_job_config(config)
365
- config.reject { |k, _| RUFUS_METADATA_KEYS.include?(k) }
366
- end
308
+ # Returns true if the enqueuing needs to be done for an ActiveJob
309
+ # class false otherwise.
310
+ #
311
+ # @param [Class] klass the class to check is decendant from ActiveJob
312
+ #
313
+ # @return [Boolean]
314
+ def active_job_enqueue?(klass)
315
+ klass.is_a?(Class) && defined?(ActiveJob::Enqueuing) &&
316
+ klass.included_modules.include?(ActiveJob::Enqueuing)
317
+ end
367
318
 
368
- # Retrieves a schedule state
369
- #
370
- # @param name [String] with the schedule's name
371
- # @return [Hash] with the schedule's state
372
- def schedule_state(name)
373
- state = SidekiqScheduler::RedisManager.get_job_state(name)
319
+ # Convert the given arguments in the format expected to be enqueued.
320
+ #
321
+ # @param [Hash] config the options to be converted
322
+ # @option config [String] class the job class
323
+ # @option config [Hash/Array] args the arguments to be passed to the job
324
+ # class
325
+ #
326
+ # @return [Hash]
327
+ def prepare_arguments(config)
328
+ config['class'] = SidekiqScheduler::Utils.try_to_constantize(config['class'])
374
329
 
375
- state ? JSON.parse(state) : {}
330
+ if config['args'].is_a?(Hash)
331
+ config['args'].symbolize_keys! if config['args'].respond_to?(:symbolize_keys!)
332
+ else
333
+ config['args'] = Array(config['args'])
376
334
  end
377
335
 
378
- # Saves a schedule state
379
- #
380
- # @param name [String] with the schedule's name
381
- # @param name [Hash] with the schedule's state
382
- def set_schedule_state(name, state)
383
- SidekiqScheduler::RedisManager.set_job_state(name, state)
384
- end
336
+ config
337
+ end
385
338
 
386
- # Adds a Hash with schedule metadata as the last argument to call the worker.
387
- # It currently returns the schedule time as a Float number representing the milisencods
388
- # since epoch.
389
- #
390
- # @example with hash argument
391
- # arguments_with_metadata({value: 1}, scheduled_at: Time.now)
392
- # #=> [{value: 1}, {scheduled_at: <miliseconds since epoch>}]
393
- #
394
- # @param args [Array|Hash]
395
- # @param metadata [Hash]
396
- # @return [Array] arguments with added metadata
397
- def arguments_with_metadata(args, metadata)
398
- if args.is_a? Array
399
- [*args, metadata]
400
- else
401
- [args, metadata]
402
- end
403
- end
339
+ # Returns true if the given schedule config hash matches the current ENV['RAILS_ENV']
340
+ # @param [Hash] config The schedule job configuration
341
+ #
342
+ # @return [Boolean] true if the schedule config matches the current ENV['RAILS_ENV']
343
+ def rails_env_matches?(config)
344
+ config['rails_env'] && ENV['RAILS_ENV'] && config['rails_env'].gsub(/\s/, '').split(',').include?(ENV['RAILS_ENV'])
345
+ end
404
346
 
405
- def sidekiq_queues
406
- Sidekiq.options[:queues].map(&:to_s)
347
+ def handle_errors
348
+ begin
349
+ yield
350
+ rescue StandardError => e
351
+ Sidekiq.logger.info "#{e.class.name}: #{e.message}"
407
352
  end
408
353
  end
409
354
  end
@@ -3,6 +3,8 @@ require 'set'
3
3
  module SidekiqScheduler
4
4
  module Utils
5
5
 
6
+ RUFUS_METADATA_KEYS = %w(description at cron every in interval enabled)
7
+
6
8
  # Stringify keys belonging to a hash.
7
9
  #
8
10
  # Also stringifies nested keys and keys of hashes inside arrays, and sets
@@ -40,5 +42,89 @@ module SidekiqScheduler
40
42
  object
41
43
  end
42
44
  end
45
+
46
+ # Constantize a given string.
47
+ #
48
+ # @param [String] klass The string to constantize
49
+ #
50
+ # @return [Class] the class corresponding to the klass param
51
+ def self.try_to_constantize(klass)
52
+ klass.is_a?(String) ? klass.constantize : klass
53
+ rescue NameError
54
+ klass
55
+ end
56
+
57
+ # Initializes active_job using the passed parameters.
58
+ #
59
+ # @param [Class] klass The class to initialize
60
+ # @param [Array/Hash] the parameters passed to the klass initializer
61
+ #
62
+ # @return [Object] instance of the class klass
63
+ def self.initialize_active_job(klass, args)
64
+ if args.is_a?(Array)
65
+ klass.new(*args)
66
+ else
67
+ klass.new(args)
68
+ end
69
+ end
70
+
71
+ # Enqueues the job using the Sidekiq client.
72
+ #
73
+ # @param [Hash] config The job configuration
74
+ def self.enqueue_with_sidekiq(config)
75
+ Sidekiq::Client.push(sanitize_job_config(config))
76
+ end
77
+
78
+ # Enqueues the job using the ActiveJob.
79
+ #
80
+ # @param [Hash] config The job configuration
81
+ def self.enqueue_with_active_job(config)
82
+ options = {
83
+ queue: config['queue']
84
+ }.keep_if { |_, v| !v.nil? }
85
+
86
+ initialize_active_job(config['class'], config['args']).enqueue(options)
87
+ end
88
+
89
+ # Removes the hash values associated to the rufus metadata keys.
90
+ #
91
+ # @param [Hash] config The job configuration
92
+ #
93
+ # @return [Hash] the sanitized job config
94
+ def self.sanitize_job_config(config)
95
+ config.reject { |k, _| RUFUS_METADATA_KEYS.include?(k) }
96
+ end
97
+
98
+ # Creates a new instance of rufus scheduler.
99
+ #
100
+ # @return [Rufus::Scheduler] the scheduler instance
101
+ def self.new_rufus_scheduler(options = {})
102
+ Rufus::Scheduler.new(options).tap do |scheduler|
103
+ scheduler.define_singleton_method(:on_post_trigger) do |job, triggered_time|
104
+ SidekiqScheduler::Utils.update_job_last_time(job.tags[0], triggered_time)
105
+ SidekiqScheduler::Utils.update_job_next_time(job.tags[0], job.next_time)
106
+ end
107
+ end
108
+ end
109
+
110
+ # Pushes job's next time execution
111
+ #
112
+ # @param [String] name The job's name
113
+ # @param [Time] next_time The job's next time execution
114
+ def self.update_job_next_time(name, next_time)
115
+ if next_time
116
+ SidekiqScheduler::RedisManager.set_job_next_time(name, next_time)
117
+ else
118
+ SidekiqScheduler::RedisManager.remove_job_next_time(name)
119
+ end
120
+ end
121
+
122
+ # Pushes job's last execution time
123
+ #
124
+ # @param [String] name The job's name
125
+ # @param [Time] last_time The job's last execution time
126
+ def self.update_job_last_time(name, last_time)
127
+ SidekiqScheduler::RedisManager.set_job_last_time(name, last_time) if last_time
128
+ end
43
129
  end
44
130
  end
@@ -1,5 +1,5 @@
1
1
  module SidekiqScheduler
2
2
 
3
- VERSION = '2.2.2'
3
+ VERSION = '3.0.0'
4
4
 
5
5
  end
@@ -17,14 +17,14 @@ module SidekiqScheduler
17
17
 
18
18
  app.get '/recurring-jobs/:name/enqueue' do
19
19
  schedule = Sidekiq.get_schedule(params[:name])
20
- SidekiqScheduler::Scheduler.enqueue_job(schedule)
20
+ SidekiqScheduler::Scheduler.instance.enqueue_job(schedule)
21
21
  redirect "#{root_path}recurring-jobs"
22
22
  end
23
23
 
24
24
  app.get '/recurring-jobs/:name/toggle' do
25
25
  Sidekiq.reload_schedule!
26
26
 
27
- SidekiqScheduler::Scheduler.toggle_job_enabled(params[:name])
27
+ SidekiqScheduler::Scheduler.instance.toggle_job_enabled(params[:name])
28
28
  redirect "#{root_path}recurring-jobs"
29
29
  end
30
30
  end
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.2.2
4
+ version: 3.0.0
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: 2018-05-15 00:00:00.000000000 Z
12
+ date: 2018-06-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sidekiq