sidekiq-scheduler 2.2.2 → 3.0.0

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: 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