dynamodb-sidekiq-scheduler 0.0.1
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 +7 -0
- data/CHANGELOG.md +69 -0
- data/MIT-LICENSE +20 -0
- data/README.md +521 -0
- data/Rakefile +29 -0
- data/lib/sidekiq/scheduler.rb +3 -0
- data/lib/sidekiq-scheduler/config.rb +80 -0
- data/lib/sidekiq-scheduler/extensions/schedule.rb +4 -0
- data/lib/sidekiq-scheduler/extensions/web.rb +14 -0
- data/lib/sidekiq-scheduler/job_presenter.rb +74 -0
- data/lib/sidekiq-scheduler/manager.rb +44 -0
- data/lib/sidekiq-scheduler/redis_manager.rb +241 -0
- data/lib/sidekiq-scheduler/rufus_utils.rb +29 -0
- data/lib/sidekiq-scheduler/schedule.rb +154 -0
- data/lib/sidekiq-scheduler/scheduler.rb +356 -0
- data/lib/sidekiq-scheduler/sidekiq_adapter.rb +80 -0
- data/lib/sidekiq-scheduler/utils.rb +143 -0
- data/lib/sidekiq-scheduler/version.rb +3 -0
- data/lib/sidekiq-scheduler/web.rb +98 -0
- data/lib/sidekiq-scheduler.rb +28 -0
- data/web/assets/stylesheets-scheduler/recurring_jobs.css +42 -0
- data/web/locales/cs.yml +14 -0
- data/web/locales/de.yml +16 -0
- data/web/locales/en.yml +23 -0
- data/web/locales/es.yml +14 -0
- data/web/locales/fr.yml +13 -0
- data/web/locales/it.yml +14 -0
- data/web/locales/ja.yml +14 -0
- data/web/locales/nl.yml +14 -0
- data/web/locales/pl.yml +14 -0
- data/web/locales/pt-BR.yml +19 -0
- data/web/locales/ru.yml +16 -0
- data/web/locales/sv.yml +14 -0
- data/web/locales/zh-cn.yml +14 -0
- data/web/views/recurring_job.erb +17 -0
- data/web/views/recurring_jobs.erb +113 -0
- metadata +257 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
module SidekiqScheduler
|
2
|
+
class Config
|
3
|
+
# We have to set the default as nil because the scheduler could be instantiated without
|
4
|
+
# passing the sidekiq config, and in those scenarios we don't want to fail
|
5
|
+
def initialize(sidekiq_config: nil, without_defaults: false)
|
6
|
+
@sidekiq_config = sidekiq_config
|
7
|
+
@scheduler_config = fetch_scheduler_config(sidekiq_config, without_defaults)
|
8
|
+
end
|
9
|
+
|
10
|
+
def enabled?
|
11
|
+
scheduler_config[:enabled]
|
12
|
+
end
|
13
|
+
|
14
|
+
def enabled=(value)
|
15
|
+
scheduler_config[:enabled] = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def dynamic?
|
19
|
+
scheduler_config[:dynamic]
|
20
|
+
end
|
21
|
+
|
22
|
+
def dynamic=(value)
|
23
|
+
scheduler_config[:dynamic] = value
|
24
|
+
end
|
25
|
+
|
26
|
+
def dynamic_every?
|
27
|
+
scheduler_config[:dynamic_every]
|
28
|
+
end
|
29
|
+
|
30
|
+
def dynamic_every=(value)
|
31
|
+
scheduler_config[:dynamic_every] = value
|
32
|
+
end
|
33
|
+
|
34
|
+
def schedule
|
35
|
+
scheduler_config[:schedule]
|
36
|
+
end
|
37
|
+
|
38
|
+
def schedule=(value)
|
39
|
+
scheduler_config[:schedule] = value
|
40
|
+
end
|
41
|
+
|
42
|
+
def listened_queues_only?
|
43
|
+
scheduler_config[:listened_queues_only]
|
44
|
+
end
|
45
|
+
|
46
|
+
def listened_queues_only=(value)
|
47
|
+
scheduler_config[:listened_queues_only] = value
|
48
|
+
end
|
49
|
+
|
50
|
+
def rufus_scheduler_options
|
51
|
+
scheduler_config[:rufus_scheduler_options]
|
52
|
+
end
|
53
|
+
|
54
|
+
def rufus_scheduler_options=(value)
|
55
|
+
scheduler_config[:rufus_scheduler_options] = value
|
56
|
+
end
|
57
|
+
|
58
|
+
def sidekiq_queues
|
59
|
+
SidekiqScheduler::SidekiqAdapter.sidekiq_queues(sidekiq_config)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
attr_reader :scheduler_config
|
65
|
+
attr_reader :sidekiq_config
|
66
|
+
|
67
|
+
DEFAULT_OPTIONS = {
|
68
|
+
enabled: true,
|
69
|
+
dynamic: false,
|
70
|
+
dynamic_every: '5s',
|
71
|
+
schedule: {},
|
72
|
+
rufus_scheduler_options: {}
|
73
|
+
}.freeze
|
74
|
+
|
75
|
+
def fetch_scheduler_config(sidekiq_config, without_defaults)
|
76
|
+
conf = SidekiqScheduler::SidekiqAdapter.fetch_scheduler_config_from_sidekiq(sidekiq_config)
|
77
|
+
without_defaults ? conf : DEFAULT_OPTIONS.merge(conf)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'sidekiq/web' unless defined?(Sidekiq::Web)
|
2
|
+
|
3
|
+
ASSETS_PATH = File.expand_path('../../../web/assets', __dir__)
|
4
|
+
|
5
|
+
Sidekiq::Web.register(SidekiqScheduler::Web)
|
6
|
+
Sidekiq::Web.tabs['recurring_jobs'] = 'recurring-jobs'
|
7
|
+
Sidekiq::Web.locales << File.expand_path("#{File.dirname(__FILE__)}/../../../web/locales")
|
8
|
+
|
9
|
+
if Sidekiq::VERSION >= '6.0.0'
|
10
|
+
Sidekiq::Web.use Rack::Static, urls: ['/stylesheets-scheduler'],
|
11
|
+
root: ASSETS_PATH,
|
12
|
+
cascade: true,
|
13
|
+
header_rules: [[:all, { 'Cache-Control' => 'public, max-age=86400' }]]
|
14
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
begin
|
2
|
+
require 'sidekiq/web/helpers'
|
3
|
+
rescue LoadError
|
4
|
+
require 'sidekiq/web_helpers'
|
5
|
+
end
|
6
|
+
require 'sidekiq-scheduler/redis_manager'
|
7
|
+
|
8
|
+
module SidekiqScheduler
|
9
|
+
class JobPresenter
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
include Sidekiq::WebHelpers
|
13
|
+
|
14
|
+
def initialize(name, attributes)
|
15
|
+
@name = name
|
16
|
+
@attributes = attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the next time execution for the job
|
20
|
+
#
|
21
|
+
# @return [String] with the job's next time
|
22
|
+
def next_time
|
23
|
+
execution_time = SidekiqScheduler::RedisManager.get_job_next_time(name)
|
24
|
+
|
25
|
+
relative_time(Time.parse(execution_time)) if execution_time
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the last execution time for the job
|
29
|
+
#
|
30
|
+
# @return [String] with the job's last time
|
31
|
+
def last_time
|
32
|
+
execution_time = SidekiqScheduler::RedisManager.get_job_last_time(name)
|
33
|
+
|
34
|
+
relative_time(Time.parse(execution_time)) if execution_time
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the interval for the job
|
38
|
+
#
|
39
|
+
# @return [String] with the job's interval
|
40
|
+
def interval
|
41
|
+
@attributes['cron'] || @attributes['interval'] || @attributes['every']
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the queue of the job
|
45
|
+
#
|
46
|
+
# @return [String] with the job's queue
|
47
|
+
def queue
|
48
|
+
@attributes.fetch('queue', 'default')
|
49
|
+
end
|
50
|
+
|
51
|
+
# Delegates the :[] method to the attributes' hash
|
52
|
+
#
|
53
|
+
# @return [String] with the value for that key
|
54
|
+
def [](key)
|
55
|
+
@attributes[key]
|
56
|
+
end
|
57
|
+
|
58
|
+
def enabled?
|
59
|
+
SidekiqScheduler::Scheduler.job_enabled?(@name)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Builds the presenter instances for the schedule hash
|
63
|
+
#
|
64
|
+
# @param schedule_hash [Hash] with the redis schedule
|
65
|
+
# @return [Array<JobPresenter>] an array with the instances of presenters
|
66
|
+
def self.build_collection(schedule_hash)
|
67
|
+
schedule_hash ||= {}
|
68
|
+
|
69
|
+
schedule_hash.sort.map do |name, job_spec|
|
70
|
+
new(name, job_spec)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'sidekiq-scheduler/schedule'
|
2
|
+
require 'sidekiq-scheduler/scheduler'
|
3
|
+
|
4
|
+
module SidekiqScheduler
|
5
|
+
|
6
|
+
# The delayed job router in the system. This
|
7
|
+
# manages the scheduled jobs pushed messages
|
8
|
+
# from Redis onto the work queues
|
9
|
+
#
|
10
|
+
class Manager
|
11
|
+
def initialize(config)
|
12
|
+
set_current_scheduler_options(config)
|
13
|
+
|
14
|
+
@scheduler_instance = SidekiqScheduler::Scheduler.new(config)
|
15
|
+
SidekiqScheduler::Scheduler.instance = @scheduler_instance
|
16
|
+
Sidekiq.schedule = config.schedule if @scheduler_instance.enabled
|
17
|
+
end
|
18
|
+
|
19
|
+
def stop
|
20
|
+
@scheduler_instance.clear_schedule!
|
21
|
+
end
|
22
|
+
|
23
|
+
def start
|
24
|
+
@scheduler_instance.load_schedule!
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def set_current_scheduler_options(config)
|
30
|
+
enabled = SidekiqScheduler::Scheduler.enabled
|
31
|
+
dynamic = SidekiqScheduler::Scheduler.dynamic
|
32
|
+
dynamic_every = SidekiqScheduler::Scheduler.dynamic_every
|
33
|
+
listened_queues_only = SidekiqScheduler::Scheduler.listened_queues_only
|
34
|
+
|
35
|
+
config.enabled = enabled unless enabled.nil?
|
36
|
+
config.dynamic = dynamic unless dynamic.nil?
|
37
|
+
config.dynamic_every = dynamic_every unless dynamic_every.nil?
|
38
|
+
unless Sidekiq.schedule.nil? || (Sidekiq.schedule.respond_to?(:empty?) && Sidekiq.schedule.empty?)
|
39
|
+
config.schedule = Sidekiq.schedule
|
40
|
+
end
|
41
|
+
config.listened_queues_only = listened_queues_only unless listened_queues_only.nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,241 @@
|
|
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_key, 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_key, 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, String(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, String(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_key, 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_key) }
|
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
|
+
SidekiqScheduler::SidekiqAdapter.redis_key_exists?(schedules_key)
|
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_key, 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_key, 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_key) unless r.type(schedules_changed_key) == '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 |pipeline|
|
134
|
+
pipeline.zadd(job_key, time.to_i, time.to_i)
|
135
|
+
pipeline.expire(job_key, REGISTERED_JOBS_THRESHOLD_IN_SECONDS)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
registered.instance_of?(Integer) ? (registered > 0) : 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
|
+
"#{key_prefix}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
|
+
"#{key_prefix}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
|
+
"#{key_prefix}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
|
+
"#{key_prefix}sidekiq-scheduler:states"
|
181
|
+
end
|
182
|
+
|
183
|
+
# Returns the Redis's key for saving schedules.
|
184
|
+
#
|
185
|
+
# @return [String] with the key
|
186
|
+
def self.schedules_key
|
187
|
+
"#{key_prefix}schedules"
|
188
|
+
end
|
189
|
+
|
190
|
+
# Returns the Redis's key for saving schedule changes.
|
191
|
+
#
|
192
|
+
# @return [String] with the key
|
193
|
+
def self.schedules_changed_key
|
194
|
+
"#{key_prefix}schedules_changed"
|
195
|
+
end
|
196
|
+
|
197
|
+
# Returns the key prefix used to generate all scheduler keys
|
198
|
+
#
|
199
|
+
# @return [String] with the key prefix
|
200
|
+
def self.key_prefix
|
201
|
+
@key_prefix
|
202
|
+
end
|
203
|
+
|
204
|
+
# Sets the key prefix used to scope all scheduler keys
|
205
|
+
#
|
206
|
+
# @param [String] value The string to use as the prefix. A ":" will be appended as a delimiter if needed.
|
207
|
+
def self.key_prefix=(value)
|
208
|
+
value = "#{value}:" if value && !%w[. :].include?(value[-1])
|
209
|
+
@key_prefix = value
|
210
|
+
end
|
211
|
+
|
212
|
+
private
|
213
|
+
|
214
|
+
# Returns the value of a Redis stored hash field
|
215
|
+
#
|
216
|
+
# @param [String] hash_key The key name of the hash
|
217
|
+
# @param [String] field_key The key name of the field
|
218
|
+
#
|
219
|
+
# @return [String]
|
220
|
+
def self.hget(hash_key, field_key)
|
221
|
+
Sidekiq.redis { |r| r.hget(hash_key, field_key) }
|
222
|
+
end
|
223
|
+
|
224
|
+
# Sets the value of a Redis stored hash field
|
225
|
+
#
|
226
|
+
# @param [String] hash_key The key name of the hash
|
227
|
+
# @param [String] field_key The key name of the field
|
228
|
+
# @param [String] value The new value name for the field
|
229
|
+
def self.hset(hash_key, field_key, value)
|
230
|
+
Sidekiq.redis { |r| r.hset(hash_key, field_key, value) }
|
231
|
+
end
|
232
|
+
|
233
|
+
# Removes the value of a Redis stored hash field
|
234
|
+
#
|
235
|
+
# @param [String] hash_key The key name of the hash
|
236
|
+
# @param [String] field_key The key name of the field
|
237
|
+
def self.hdel(hash_key, field_key)
|
238
|
+
Sidekiq.redis { |r| r.hdel(hash_key, field_key) }
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'sidekiq-scheduler/utils'
|
2
|
+
|
3
|
+
module SidekiqScheduler
|
4
|
+
class RufusUtils
|
5
|
+
|
6
|
+
# Normalizes schedule options to rufus scheduler options
|
7
|
+
#
|
8
|
+
# @param options [String, Array]
|
9
|
+
#
|
10
|
+
# @return [Array]
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# normalize_schedule_options('15m') => ['15m', {}]
|
14
|
+
# normalize_schedule_options(['15m']) => ['15m', {}]
|
15
|
+
# normalize_schedule_options(['15m', first_in: '5m']) => ['15m', { first_in: '5m' }]
|
16
|
+
def self.normalize_schedule_options(options)
|
17
|
+
schedule, opts = options
|
18
|
+
|
19
|
+
if !opts.is_a?(Hash)
|
20
|
+
opts = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
opts = SidekiqScheduler::Utils.symbolize_keys(opts)
|
24
|
+
|
25
|
+
return schedule, opts
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require 'sidekiq-scheduler/utils'
|
4
|
+
require_relative 'redis_manager'
|
5
|
+
|
6
|
+
module SidekiqScheduler
|
7
|
+
module Schedule
|
8
|
+
|
9
|
+
# Accepts a new schedule configuration of the form:
|
10
|
+
#
|
11
|
+
# {
|
12
|
+
# "MakeTea" => {
|
13
|
+
# "every" => "1m" },
|
14
|
+
# "some_name" => {
|
15
|
+
# "cron" => "5/* * * *",
|
16
|
+
# "class" => "DoSomeWork",
|
17
|
+
# "args" => "work on this string",
|
18
|
+
# "description" => "this thing works it"s butter off" },
|
19
|
+
# ...
|
20
|
+
# }
|
21
|
+
#
|
22
|
+
# Hash keys can be anything and are used to describe and reference
|
23
|
+
# the scheduled job. If the "class" argument is missing, the key
|
24
|
+
# is used implicitly as "class" argument - in the "MakeTea" example,
|
25
|
+
# "MakeTea" is used both as job name and sidekiq worker class.
|
26
|
+
#
|
27
|
+
# :cron can be any cron scheduling string
|
28
|
+
#
|
29
|
+
# :every can be used in lieu of :cron. see rufus-scheduler's 'every' usage
|
30
|
+
# for valid syntax. If :cron is present it will take precedence over :every.
|
31
|
+
#
|
32
|
+
# :class must be a sidekiq worker class. If it is missing, the job name (hash key)
|
33
|
+
# will be used as :class.
|
34
|
+
#
|
35
|
+
# :args can be any yaml which will be converted to a ruby literal and
|
36
|
+
# passed in a params. (optional)
|
37
|
+
#
|
38
|
+
# :description is just that, a description of the job (optional). If
|
39
|
+
# params is an array, each element in the array is passed as a separate
|
40
|
+
# param, otherwise params is passed in as the only parameter to perform.
|
41
|
+
def schedule=(schedule_hash)
|
42
|
+
schedule_hash = prepare_schedule(schedule_hash)
|
43
|
+
to_remove = get_all_schedules.keys - schedule_hash.keys.map(&:to_s)
|
44
|
+
|
45
|
+
schedule_hash.each do |name, job_spec|
|
46
|
+
set_schedule(name, job_spec)
|
47
|
+
end
|
48
|
+
|
49
|
+
to_remove.each do |name|
|
50
|
+
remove_schedule(name)
|
51
|
+
end
|
52
|
+
|
53
|
+
@schedule = schedule_hash
|
54
|
+
end
|
55
|
+
|
56
|
+
def schedule
|
57
|
+
@schedule
|
58
|
+
end
|
59
|
+
|
60
|
+
# Reloads the schedule from Redis and return it.
|
61
|
+
#
|
62
|
+
# @return Hash
|
63
|
+
def reload_schedule!
|
64
|
+
@schedule = get_schedule
|
65
|
+
end
|
66
|
+
alias_method :schedule!, :reload_schedule!
|
67
|
+
|
68
|
+
# Retrive the schedule configuration for the given name
|
69
|
+
# if the name is nil it returns a hash with all the
|
70
|
+
# names end their schedules.
|
71
|
+
def get_schedule(name = nil)
|
72
|
+
if name.nil?
|
73
|
+
get_all_schedules
|
74
|
+
else
|
75
|
+
encoded_schedule = SidekiqScheduler::RedisManager.get_job_schedule(name)
|
76
|
+
encoded_schedule.nil? ? nil : JSON.parse(encoded_schedule)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# gets the schedule as it exists in redis
|
81
|
+
def get_all_schedules
|
82
|
+
schedules = {}
|
83
|
+
|
84
|
+
if SidekiqScheduler::RedisManager.schedule_exist?
|
85
|
+
SidekiqScheduler::RedisManager.get_all_schedules.tap do |h|
|
86
|
+
h.each do |name, config|
|
87
|
+
schedules[name] = JSON.parse(config)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
schedules
|
93
|
+
end
|
94
|
+
|
95
|
+
# Create or update a schedule with the provided name and configuration.
|
96
|
+
#
|
97
|
+
# Note: values for class and custom_job_class need to be strings,
|
98
|
+
# not constants.
|
99
|
+
#
|
100
|
+
# Sidekiq.set_schedule('some_job', { :class => 'SomeJob',
|
101
|
+
# :every => '15mins',
|
102
|
+
# :queue => 'high',
|
103
|
+
# :args => '/tmp/poop' })
|
104
|
+
def set_schedule(name, config)
|
105
|
+
existing_config = get_schedule(name)
|
106
|
+
unless existing_config && existing_config == config
|
107
|
+
SidekiqScheduler::RedisManager.set_job_schedule(name, config)
|
108
|
+
SidekiqScheduler::RedisManager.add_schedule_change(name)
|
109
|
+
end
|
110
|
+
config
|
111
|
+
end
|
112
|
+
|
113
|
+
# remove a given schedule by name
|
114
|
+
def remove_schedule(name)
|
115
|
+
SidekiqScheduler::RedisManager.remove_job_schedule(name)
|
116
|
+
SidekiqScheduler::RedisManager.add_schedule_change(name)
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def prepare_schedule(schedule_hash)
|
122
|
+
schedule_hash = SidekiqScheduler::Utils.stringify_keys(schedule_hash)
|
123
|
+
|
124
|
+
prepared_hash = {}
|
125
|
+
|
126
|
+
schedule_hash.each do |name, job_spec|
|
127
|
+
job_spec = job_spec.dup
|
128
|
+
|
129
|
+
job_class = job_spec.fetch('class', name)
|
130
|
+
inferred_queue = infer_queue(job_class)
|
131
|
+
|
132
|
+
job_spec['class'] ||= job_class
|
133
|
+
job_spec['queue'] ||= inferred_queue unless inferred_queue.nil?
|
134
|
+
|
135
|
+
prepared_hash[name] = job_spec
|
136
|
+
end
|
137
|
+
prepared_hash
|
138
|
+
end
|
139
|
+
|
140
|
+
def infer_queue(klass)
|
141
|
+
klass = try_to_constantize(klass)
|
142
|
+
|
143
|
+
# ActiveJob uses queue_as when the job is created
|
144
|
+
# to determine the queue
|
145
|
+
if klass.respond_to?(:sidekiq_options) && !SidekiqScheduler::Utils.active_job_enqueue?(klass)
|
146
|
+
klass.sidekiq_options['queue']
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def try_to_constantize(klass)
|
151
|
+
SidekiqScheduler::Utils.try_to_constantize(klass)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|