sidekiq-scheduler 2.0.8 → 2.0.9
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 +4 -4
- data/README.md +6 -11
- data/lib/sidekiq-scheduler/job_presenter.rb +58 -0
- data/lib/sidekiq-scheduler/manager.rb +1 -1
- data/lib/sidekiq-scheduler/version.rb +1 -1
- data/lib/sidekiq-scheduler/web.rb +5 -3
- data/lib/sidekiq/scheduler.rb +266 -224
- data/web/locales/cs.yml +3 -1
- data/web/locales/en.yml +3 -1
- data/web/locales/es.yml +3 -1
- data/web/views/recurring_jobs.erb +10 -8
- metadata +42 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f7d8c790cf456b0a62f67d543f1c7532f21e16b
|
4
|
+
data.tar.gz: ca1112b924897da7d6d06d72e071ea63a92ac8e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 666ed752c0a891f3ad99af53402ff3274e881dc0540d79d0025c191cdcb6d80d73066d4e9c4021f85c0e04d4d65ecb6a63d41e2b4d6b0420f58070ab791b28b2
|
7
|
+
data.tar.gz: f8dec2ee3b6add1cd02bd6760dc58fc49fc65cef3b1ae1a01891fb77e5fd535c9052ab78c98f21c5a396bbe30e457466a92bcf17dbf104c6e5be631879aa2f63
|
data/README.md
CHANGED
@@ -81,8 +81,7 @@ if Rails.env == 'production' && (defined?(Rails::Server) || defined?(Unicorn))
|
|
81
81
|
Sidekiq.configure_server do |config|
|
82
82
|
|
83
83
|
config.on(:startup) do
|
84
|
-
Sidekiq.schedule = YAML
|
85
|
-
.load_file(File.expand_path('../../../config/scheduler.yml', __FILE__))
|
84
|
+
Sidekiq.schedule = YAML.load_file(File.expand_path('../../config/scheduler.yml', __FILE__))
|
86
85
|
Sidekiq::Scheduler.reload_schedule!
|
87
86
|
end
|
88
87
|
end
|
@@ -157,7 +156,7 @@ require 'sidekiq/scheduler'
|
|
157
156
|
|
158
157
|
Sidekiq.configure_server do |config|
|
159
158
|
config.on(:startup) do
|
160
|
-
Sidekiq.schedule = YAML.load_file(File.expand_path("
|
159
|
+
Sidekiq.schedule = YAML.load_file(File.expand_path("../../config/scheduler.yml", __FILE__))
|
161
160
|
Sidekiq::Scheduler.reload_schedule!
|
162
161
|
end
|
163
162
|
end
|
@@ -167,11 +166,11 @@ If you are running a non Rails project you should add code to load the workers c
|
|
167
166
|
|
168
167
|
```ruby
|
169
168
|
require 'sidekiq/scheduler'
|
170
|
-
Dir[File.expand_path('../lib/workers/*.rb',__FILE__)].each do |file| load file; end
|
169
|
+
Dir[File.expand_path('../lib/workers/*.rb', __FILE__)].each do |file| load file; end
|
171
170
|
|
172
171
|
Sidekiq.configure_server do |config|
|
173
172
|
config.on(:startup) do
|
174
|
-
Sidekiq.schedule = YAML.load_file(File.expand_path("
|
173
|
+
Sidekiq.schedule = YAML.load_file(File.expand_path("../../config/scheduler.yml", __FILE__))
|
175
174
|
Sidekiq::Scheduler.reload_schedule!
|
176
175
|
end
|
177
176
|
end
|
@@ -218,15 +217,11 @@ You can set that flag in the following ways.
|
|
218
217
|
```ruby
|
219
218
|
Sidekiq.configure_server do |config|
|
220
219
|
# ...
|
221
|
-
|
222
|
-
config.on(:startup) do
|
223
|
-
# ...
|
224
|
-
Sidekiq::Scheduler.dynamic = true
|
225
|
-
end
|
220
|
+
Sidekiq::Scheduler.dynamic = true
|
226
221
|
end
|
227
222
|
```
|
228
223
|
|
229
|
-
If `:dynamic` flag is set to false
|
224
|
+
If `:dynamic` flag is set to `false`, you have to reload the schedule manually in sidekiq
|
230
225
|
side:
|
231
226
|
|
232
227
|
```ruby
|
@@ -0,0 +1,58 @@
|
|
1
|
+
begin
|
2
|
+
require 'sidekiq/web/helpers'
|
3
|
+
rescue LoadError
|
4
|
+
require 'sidekiq/web_helpers'
|
5
|
+
end
|
6
|
+
|
7
|
+
module SidekiqScheduler
|
8
|
+
class JobPresenter
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
include Sidekiq::WebHelpers
|
12
|
+
|
13
|
+
def initialize(name, attributes)
|
14
|
+
@name = name
|
15
|
+
@attributes = attributes
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns the next time execution for the job
|
19
|
+
#
|
20
|
+
# @return [String] with the job's next time
|
21
|
+
def next_time
|
22
|
+
execution_time = Sidekiq.redis { |r| r.hget(Sidekiq::Scheduler.next_times_key, name) }
|
23
|
+
|
24
|
+
relative_time(Time.parse(execution_time)) if execution_time
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the interval for the job
|
28
|
+
#
|
29
|
+
# @return [String] with the job's interval
|
30
|
+
def interval
|
31
|
+
@attributes['cron'] || @attributes['interval'] || @attributes['every']
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the queue of the job
|
35
|
+
#
|
36
|
+
# @return [String] with the job's queue
|
37
|
+
def queue
|
38
|
+
@attributes.fetch('queue', 'default')
|
39
|
+
end
|
40
|
+
|
41
|
+
# Delegates the :[] method to the attributes' hash
|
42
|
+
#
|
43
|
+
# @return [String] with the value for that key
|
44
|
+
def [](key)
|
45
|
+
@attributes[key]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Builds the presenter instances for the schedule hash
|
49
|
+
#
|
50
|
+
# @param schedule_hash [Hash] with the redis schedule
|
51
|
+
# @return [Array<JobPresenter>] an array with the instances of presenters
|
52
|
+
def self.build_collection(schedule_hash)
|
53
|
+
Hash(schedule_hash).map do |name, job_spec|
|
54
|
+
new(name, job_spec)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -18,7 +18,7 @@ module SidekiqScheduler
|
|
18
18
|
Sidekiq::Scheduler.enabled = options[:enabled]
|
19
19
|
Sidekiq::Scheduler.dynamic = options[:dynamic]
|
20
20
|
Sidekiq::Scheduler.listened_queues_only = options[:listened_queues_only]
|
21
|
-
Sidekiq.schedule = options[:schedule]
|
21
|
+
Sidekiq.schedule = options[:schedule] || {}
|
22
22
|
end
|
23
23
|
|
24
24
|
def stop
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative 'job_presenter'
|
2
|
+
|
1
3
|
module SidekiqScheduler
|
2
4
|
# Hook into *Sidekiq::Web* Sinatra app which adds a new '/recurring-jobs' page
|
3
5
|
|
@@ -6,7 +8,7 @@ module SidekiqScheduler
|
|
6
8
|
|
7
9
|
def self.registered(app)
|
8
10
|
app.get '/recurring-jobs' do
|
9
|
-
@
|
11
|
+
@presented_jobs = JobPresenter.build_collection(Sidekiq.schedule!)
|
10
12
|
|
11
13
|
erb File.read(File.join(VIEW_PATH, 'recurring_jobs.erb'))
|
12
14
|
end
|
@@ -14,7 +16,7 @@ module SidekiqScheduler
|
|
14
16
|
app.get '/recurring-jobs/:name/enqueue' do
|
15
17
|
schedule = Sidekiq.get_schedule(params[:name])
|
16
18
|
Sidekiq::Scheduler.enqueue_job(schedule)
|
17
|
-
redirect
|
19
|
+
redirect '/recurring-jobs'
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
@@ -23,4 +25,4 @@ end
|
|
23
25
|
require 'sidekiq/web' unless defined?(Sidekiq::Web)
|
24
26
|
Sidekiq::Web.register(SidekiqScheduler::Web)
|
25
27
|
Sidekiq::Web.tabs['recurring_jobs'] = 'recurring-jobs'
|
26
|
-
Sidekiq::Web.
|
28
|
+
Sidekiq::Web.locales << File.expand_path(File.dirname(__FILE__) + "/../../web/locales")
|
data/lib/sidekiq/scheduler.rb
CHANGED
@@ -8,6 +8,7 @@ module Sidekiq
|
|
8
8
|
extend Sidekiq::Util
|
9
9
|
|
10
10
|
REGISTERED_JOBS_THRESHOLD_IN_SECONDS = 24 * 60 * 60
|
11
|
+
RUFUS_METADATA_KEYS = %w(description at cron every in interval)
|
11
12
|
|
12
13
|
# We expect rufus jobs to have #params
|
13
14
|
Rufus::Scheduler::Job.module_eval do
|
@@ -26,299 +27,340 @@ module Sidekiq
|
|
26
27
|
|
27
28
|
# Set to schedule jobs only when will be pushed to queues listened by sidekiq
|
28
29
|
attr_accessor :listened_queues_only
|
29
|
-
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
# the Rufus::Scheduler jobs that are scheduled
|
32
|
+
def scheduled_jobs
|
33
|
+
@@scheduled_jobs
|
34
|
+
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
36
|
+
def print_schedule
|
37
|
+
if rufus_scheduler
|
38
|
+
logger.info "Scheduling Info\tLast Run"
|
39
|
+
scheduler_jobs = rufus_scheduler.all_jobs
|
40
|
+
scheduler_jobs.each do |_, v|
|
41
|
+
logger.info "#{v.t}\t#{v.last}\t"
|
42
|
+
end
|
42
43
|
end
|
43
44
|
end
|
44
|
-
end
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
46
|
+
# Pulls the schedule from Sidekiq.schedule and loads it into the
|
47
|
+
# rufus scheduler instance
|
48
|
+
def load_schedule!
|
49
|
+
if enabled
|
50
|
+
logger.info 'Loading Schedule'
|
51
|
+
|
52
|
+
# Load schedule from redis for the first time if dynamic
|
53
|
+
if dynamic
|
54
|
+
Sidekiq.reload_schedule!
|
55
|
+
rufus_scheduler.every('5s') do
|
56
|
+
update_schedule
|
57
|
+
end
|
57
58
|
end
|
58
|
-
end
|
59
59
|
|
60
|
-
|
60
|
+
logger.info 'Schedule empty! Set Sidekiq.schedule' if Sidekiq.schedule.empty?
|
61
61
|
|
62
62
|
|
63
|
-
|
63
|
+
@@scheduled_jobs = {}
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
65
|
+
Sidekiq.schedule.each do |name, config|
|
66
|
+
if !listened_queues_only || enabled_queue?(config['queue'])
|
67
|
+
load_schedule_job(name, config)
|
68
|
+
else
|
69
|
+
logger.info { "Ignoring #{name}, job's queue is not enabled." }
|
70
|
+
end
|
70
71
|
end
|
71
|
-
end
|
72
72
|
|
73
|
-
|
73
|
+
Sidekiq.redis { |r| r.del(:schedules_changed) }
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
logger.info 'Schedules Loaded'
|
76
|
+
else
|
77
|
+
logger.info 'SidekiqScheduler is disabled'
|
78
|
+
end
|
78
79
|
end
|
79
|
-
end
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
81
|
+
# modify interval type value to value with options if options available
|
82
|
+
def optionizate_interval_value(value)
|
83
|
+
args = value
|
84
|
+
if args.is_a?(::Array)
|
85
|
+
return args.first if args.size > 2 || !args.last.is_a?(::Hash)
|
86
|
+
# symbolize keys of hash for options
|
87
|
+
args[1] = args[1].inject({}) do |m, i|
|
88
|
+
key, value = i
|
89
|
+
m[(key.to_sym rescue key) || key] = value
|
90
|
+
m
|
91
|
+
end
|
91
92
|
end
|
93
|
+
args
|
92
94
|
end
|
93
|
-
args
|
94
|
-
end
|
95
95
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
96
|
+
# Loads a job schedule into the Rufus::Scheduler and stores it in @@scheduled_jobs
|
97
|
+
def load_schedule_job(name, config)
|
98
|
+
# If rails_env is set in the config, enforce ENV['RAILS_ENV'] as
|
99
|
+
# required for the jobs to be scheduled. If rails_env is missing, the
|
100
|
+
# job should be scheduled regardless of what ENV['RAILS_ENV'] is set
|
101
|
+
# to.
|
102
|
+
if config['rails_env'].nil? || rails_env_matches?(config)
|
103
|
+
logger.info "Scheduling #{name} #{config}"
|
104
|
+
interval_defined = false
|
105
|
+
interval_types = %w{cron every at in interval}
|
106
|
+
interval_types.each do |interval_type|
|
107
|
+
config_interval_type = config[interval_type]
|
108
108
|
|
109
|
-
|
109
|
+
if !config_interval_type.nil? && config_interval_type.length > 0
|
110
110
|
|
111
|
-
|
111
|
+
args = optionizate_interval_value(config_interval_type)
|
112
112
|
|
113
|
-
|
114
|
-
|
113
|
+
rufus_job = new_job(name, interval_type, config, args)
|
114
|
+
@@scheduled_jobs[name] = rufus_job
|
115
|
+
update_job_next_time(name, rufus_job.next_time)
|
115
116
|
|
116
|
-
|
117
|
-
config.delete(interval_type)
|
117
|
+
interval_defined = true
|
118
118
|
|
119
|
-
|
119
|
+
break
|
120
120
|
end
|
121
|
+
end
|
121
122
|
|
122
|
-
|
123
|
-
|
124
|
-
break
|
123
|
+
unless interval_defined
|
124
|
+
logger.info "no #{interval_types.join(' / ')} found for #{config['class']} (#{name}) - skipping"
|
125
125
|
end
|
126
126
|
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Pushes the job into Sidekiq if not already pushed for the given time
|
130
|
+
#
|
131
|
+
# @param [String] job_name The job's name
|
132
|
+
# @param [Time] time The time when the job got cleared for triggering
|
133
|
+
# @param [Hash] config Job's config hash
|
134
|
+
def idempotent_job_enqueue(job_name, time, config)
|
135
|
+
registered = register_job_instance(job_name, time)
|
136
|
+
|
137
|
+
if registered
|
138
|
+
logger.info "queueing #{config['class']} (#{job_name})"
|
139
|
+
|
140
|
+
handle_errors { enqueue_job(config) }
|
141
|
+
|
142
|
+
remove_elder_job_instances(job_name)
|
143
|
+
else
|
144
|
+
logger.debug { "Ignoring #{job_name} job as it has been already enqueued" }
|
145
|
+
end
|
146
|
+
end
|
127
147
|
|
128
|
-
|
129
|
-
|
148
|
+
# Pushes job's next time execution
|
149
|
+
#
|
150
|
+
# @param [String] name The job's name
|
151
|
+
# @param [Time] next_time The job's next time execution
|
152
|
+
def update_job_next_time(name, next_time)
|
153
|
+
Sidekiq.redis do |r|
|
154
|
+
next_time ? r.hset(next_times_key, name, next_time) : r.hdel(next_times_key, name)
|
130
155
|
end
|
131
156
|
end
|
132
|
-
end
|
133
157
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
def self.idempotent_job_enqueue(job_name, time, config)
|
140
|
-
registered = register_job_instance(job_name, time)
|
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
|
141
163
|
|
142
|
-
|
143
|
-
|
164
|
+
def handle_errors
|
165
|
+
begin
|
166
|
+
yield
|
167
|
+
rescue StandardError => e
|
168
|
+
logger.info "#{e.class.name}: #{e.message}"
|
169
|
+
end
|
170
|
+
end
|
144
171
|
|
145
|
-
|
172
|
+
# Enqueue a job based on a config hash
|
173
|
+
def enqueue_job(job_config)
|
174
|
+
config = prepare_arguments(job_config.dup)
|
146
175
|
|
147
|
-
|
148
|
-
|
149
|
-
|
176
|
+
if active_job_enqueue?(config['class'])
|
177
|
+
enque_with_active_job(config)
|
178
|
+
else
|
179
|
+
enque_with_sidekiq(config)
|
180
|
+
end
|
150
181
|
end
|
151
|
-
end
|
152
182
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
config['rails_env'] && ENV['RAILS_ENV'] && config['rails_env'].gsub(/\s/, '').split(',').include?(ENV['RAILS_ENV'])
|
157
|
-
end
|
183
|
+
def rufus_scheduler_options
|
184
|
+
@rufus_scheduler_options ||= {}
|
185
|
+
end
|
158
186
|
|
159
|
-
|
160
|
-
|
161
|
-
yield
|
162
|
-
rescue StandardError => e
|
163
|
-
logger.info "#{e.class.name}: #{e.message}"
|
187
|
+
def rufus_scheduler_options=(options)
|
188
|
+
@rufus_scheduler_options = options
|
164
189
|
end
|
165
|
-
end
|
166
190
|
|
167
|
-
|
168
|
-
|
169
|
-
|
191
|
+
def rufus_scheduler
|
192
|
+
@rufus_scheduler ||= new_rufus_scheduler
|
193
|
+
end
|
170
194
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
195
|
+
# Stops old rufus scheduler and creates a new one. Returns the new
|
196
|
+
# rufus scheduler
|
197
|
+
def clear_schedule!
|
198
|
+
rufus_scheduler.stop
|
199
|
+
@rufus_scheduler = nil
|
200
|
+
@@scheduled_jobs = {}
|
201
|
+
rufus_scheduler
|
175
202
|
end
|
176
|
-
end
|
177
203
|
|
178
|
-
|
179
|
-
|
180
|
-
|
204
|
+
def reload_schedule!
|
205
|
+
if enabled
|
206
|
+
logger.info 'Reloading Schedule'
|
207
|
+
clear_schedule!
|
208
|
+
load_schedule!
|
209
|
+
else
|
210
|
+
logger.info 'SidekiqScheduler is disabled'
|
211
|
+
end
|
212
|
+
end
|
181
213
|
|
182
|
-
|
183
|
-
|
184
|
-
|
214
|
+
def update_schedule
|
215
|
+
if Sidekiq.redis { |r| r.scard(:schedules_changed) } > 0
|
216
|
+
logger.info 'Updating schedule'
|
217
|
+
Sidekiq.reload_schedule!
|
218
|
+
while schedule_name = Sidekiq.redis { |r| r.spop(:schedules_changed) }
|
219
|
+
if Sidekiq.schedule.keys.include?(schedule_name)
|
220
|
+
unschedule_job(schedule_name)
|
221
|
+
load_schedule_job(schedule_name, Sidekiq.schedule[schedule_name])
|
222
|
+
else
|
223
|
+
unschedule_job(schedule_name)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
logger.info 'Schedules Loaded'
|
227
|
+
end
|
228
|
+
end
|
185
229
|
|
186
|
-
|
187
|
-
|
188
|
-
|
230
|
+
def unschedule_job(name)
|
231
|
+
if scheduled_jobs[name]
|
232
|
+
logger.debug "Removing schedule #{name}"
|
233
|
+
scheduled_jobs[name].unschedule
|
234
|
+
scheduled_jobs.delete(name)
|
235
|
+
end
|
236
|
+
end
|
189
237
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
self.rufus_scheduler.stop
|
194
|
-
@rufus_scheduler = nil
|
195
|
-
@@scheduled_jobs = {}
|
196
|
-
self.rufus_scheduler
|
197
|
-
end
|
238
|
+
def enque_with_active_job(config)
|
239
|
+
initialize_active_job(config['class'], config['args']).enqueue(config)
|
240
|
+
end
|
198
241
|
|
199
|
-
|
200
|
-
|
201
|
-
logger.info 'Reloading Schedule'
|
202
|
-
self.clear_schedule!
|
203
|
-
self.load_schedule!
|
204
|
-
else
|
205
|
-
logger.info 'SidekiqScheduler is disabled'
|
242
|
+
def enque_with_sidekiq(config)
|
243
|
+
Sidekiq::Client.push(sanitize_job_config(config))
|
206
244
|
end
|
207
|
-
end
|
208
245
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
if Sidekiq.schedule.keys.include?(schedule_name)
|
215
|
-
self.unschedule_job(schedule_name)
|
216
|
-
self.load_schedule_job(schedule_name, Sidekiq.schedule[schedule_name])
|
217
|
-
else
|
218
|
-
self.unschedule_job(schedule_name)
|
219
|
-
end
|
246
|
+
def initialize_active_job(klass, args)
|
247
|
+
if args.is_a?(Array)
|
248
|
+
klass.new(*args)
|
249
|
+
else
|
250
|
+
klass.new(args)
|
220
251
|
end
|
221
|
-
logger.info 'Schedules Loaded'
|
222
252
|
end
|
223
|
-
end
|
224
253
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
254
|
+
# Returns true if the enqueuing needs to be done for an ActiveJob
|
255
|
+
# class false otherwise.
|
256
|
+
#
|
257
|
+
# @param [Class] klass the class to check is decendant from ActiveJob
|
258
|
+
#
|
259
|
+
# @return [Boolean]
|
260
|
+
def active_job_enqueue?(klass)
|
261
|
+
klass.is_a?(Class) && defined?(ActiveJob::Enqueuing) &&
|
262
|
+
klass.included_modules.include?(ActiveJob::Enqueuing)
|
230
263
|
end
|
231
|
-
end
|
232
264
|
|
233
|
-
|
234
|
-
|
235
|
-
|
265
|
+
# Convert the given arguments in the format expected to be enqueued.
|
266
|
+
#
|
267
|
+
# @param [Hash] config the options to be converted
|
268
|
+
# @option config [String] class the job class
|
269
|
+
# @option config [Hash/Array] args the arguments to be passed to the job
|
270
|
+
# class
|
271
|
+
#
|
272
|
+
# @return [Hash]
|
273
|
+
def prepare_arguments(config)
|
274
|
+
config['class'] = try_to_constantize(config['class'])
|
275
|
+
|
276
|
+
if config['args'].is_a?(Hash)
|
277
|
+
config['args'].symbolize_keys! if config['args'].respond_to?(:symbolize_keys!)
|
278
|
+
else
|
279
|
+
config['args'] = Array(config['args'])
|
280
|
+
end
|
236
281
|
|
237
|
-
|
238
|
-
|
239
|
-
end
|
282
|
+
config
|
283
|
+
end
|
240
284
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
klass.new(args)
|
285
|
+
def try_to_constantize(klass)
|
286
|
+
klass.is_a?(String) ? klass.constantize : klass
|
287
|
+
rescue NameError
|
288
|
+
klass
|
246
289
|
end
|
247
|
-
end
|
248
290
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
defined?(ActiveJob::Enqueuing) && klass.included_modules.include?(ActiveJob::Enqueuing)
|
257
|
-
end
|
291
|
+
# Returns true if a job's queue is being listened on by sidekiq
|
292
|
+
#
|
293
|
+
# @param [String] job_queue Job's queue name
|
294
|
+
#
|
295
|
+
# @return [Boolean]
|
296
|
+
def enabled_queue?(job_queue)
|
297
|
+
queues = Sidekiq.options[:queues]
|
258
298
|
|
259
|
-
|
260
|
-
#
|
261
|
-
# @param [Hash] config the options to be converted
|
262
|
-
# @option config [String] class the job class
|
263
|
-
# @option config [Hash/Array] args the arguments to be passed to the job
|
264
|
-
# class
|
265
|
-
#
|
266
|
-
# @return [Hash]
|
267
|
-
def self.prepare_arguments(config)
|
268
|
-
config['class'] = config['class'].constantize if config['class'].is_a?(String)
|
269
|
-
|
270
|
-
if config['args'].is_a?(Hash)
|
271
|
-
config['args'].symbolize_keys! if config['args'].respond_to?(:symbolize_keys!)
|
272
|
-
else
|
273
|
-
config['args'] = Array(config['args'])
|
299
|
+
queues.empty? || queues.include?(job_queue)
|
274
300
|
end
|
275
301
|
|
276
|
-
|
277
|
-
|
302
|
+
# Registers a queued job instance
|
303
|
+
#
|
304
|
+
# @param [String] job_name The job's name
|
305
|
+
# @param [Time] time Time at which the job was cleared by the scheduler
|
306
|
+
#
|
307
|
+
# @return [Boolean] true if the job was registered, false when otherwise
|
308
|
+
def register_job_instance(job_name, time)
|
309
|
+
pushed_job_key = pushed_job_key(job_name)
|
310
|
+
|
311
|
+
registered, _ = Sidekiq.redis do |r|
|
312
|
+
r.pipelined do
|
313
|
+
r.zadd(pushed_job_key, time.to_i, time.to_i)
|
314
|
+
r.expire(pushed_job_key, REGISTERED_JOBS_THRESHOLD_IN_SECONDS)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
registered
|
319
|
+
end
|
278
320
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
def self.enabled_queue?(job_queue)
|
285
|
-
queues = Sidekiq.options[:queues]
|
321
|
+
def remove_elder_job_instances(job_name)
|
322
|
+
Sidekiq.redis do |r|
|
323
|
+
r.zremrangebyscore(pushed_job_key(job_name), 0, Time.now.to_i - REGISTERED_JOBS_THRESHOLD_IN_SECONDS)
|
324
|
+
end
|
325
|
+
end
|
286
326
|
|
287
|
-
|
288
|
-
|
327
|
+
# Returns the key of the Redis sorted set used to store job enqueues
|
328
|
+
#
|
329
|
+
# @param [String] job_name The name of the job
|
330
|
+
#
|
331
|
+
# @return [String]
|
332
|
+
def pushed_job_key(job_name)
|
333
|
+
"sidekiq-scheduler:pushed:#{job_name}"
|
334
|
+
end
|
289
335
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
336
|
+
# Returns the key of the Redis hash for job's execution times hash
|
337
|
+
#
|
338
|
+
def next_times_key
|
339
|
+
'sidekiq-scheduler:next_times'
|
340
|
+
end
|
341
|
+
|
342
|
+
private
|
343
|
+
|
344
|
+
def new_rufus_scheduler
|
345
|
+
Rufus::Scheduler.new(rufus_scheduler_options).tap do |scheduler|
|
346
|
+
scheduler.define_singleton_method(:on_post_trigger) do |job, triggered_time|
|
347
|
+
Sidekiq::Scheduler.update_job_next_time(job.tags[0], job.next_time)
|
348
|
+
end
|
303
349
|
end
|
304
350
|
end
|
305
351
|
|
306
|
-
|
307
|
-
|
352
|
+
def new_job(name, interval_type, config, args)
|
353
|
+
opts = { :job => true, :tags => [name] }
|
308
354
|
|
309
|
-
|
310
|
-
|
311
|
-
|
355
|
+
rufus_scheduler.send(interval_type, *args, opts) do |job, time|
|
356
|
+
idempotent_job_enqueue(name, time, sanitize_job_config(config))
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def sanitize_job_config(config)
|
361
|
+
config.reject { |k, _| RUFUS_METADATA_KEYS.include?(k) }
|
312
362
|
end
|
313
|
-
end
|
314
363
|
|
315
|
-
# Returns the key of the Redis sorted set used to store job enqueues
|
316
|
-
#
|
317
|
-
# @param [String] job_name The name of the job
|
318
|
-
#
|
319
|
-
# @return [String]
|
320
|
-
def self.pushed_job_key(job_name)
|
321
|
-
"sidekiq-scheduler:pushed:#{job_name}"
|
322
364
|
end
|
323
365
|
end
|
324
366
|
end
|
data/web/locales/cs.yml
CHANGED
data/web/locales/en.yml
CHANGED
data/web/locales/es.yml
CHANGED
@@ -10,23 +10,25 @@
|
|
10
10
|
<th><%= t('class') %></th>
|
11
11
|
<th><%= t('queue') %></th>
|
12
12
|
<th><%= t('arguments') %></th>
|
13
|
+
<th><%= t('next_time') %></th>
|
13
14
|
<th></th>
|
14
15
|
</tr>
|
15
16
|
</thead>
|
16
17
|
|
17
18
|
<tbody>
|
18
|
-
<% @
|
19
|
+
<% @presented_jobs.each do |job| %>
|
19
20
|
<tr>
|
20
|
-
<td><%= name %></td>
|
21
|
-
<td><%=
|
22
|
-
<td><%=
|
23
|
-
<td><%=
|
21
|
+
<td><%= job.name %></td>
|
22
|
+
<td><%= job['description'] %></td>
|
23
|
+
<td><%= job.interval %></td>
|
24
|
+
<td><%= job['class'] %></td>
|
24
25
|
<td>
|
25
|
-
<a href="<%= root_path %>queues/<%=
|
26
|
+
<a href="<%= root_path %>queues/<%= job.queue %>"><%= job.queue %></a>
|
26
27
|
</td>
|
27
|
-
<td><%=
|
28
|
+
<td><%= job['args'] %></td>
|
29
|
+
<td><%= job.next_time || t('no_next_time') %></td>
|
28
30
|
<td class="text-center">
|
29
|
-
<a class="btn btn-warn btn-xs" href="<%= root_path %>recurring-jobs/<%= URI.escape(name) %>/enqueue">
|
31
|
+
<a class="btn btn-warn btn-xs" href="<%= root_path %>recurring-jobs/<%= URI.escape(job.name) %>/enqueue">
|
30
32
|
<%= t('enqueue_now') %>
|
31
33
|
</a>
|
32
34
|
</td>
|
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.0.
|
4
|
+
version: 2.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Morton Jonuschat
|
@@ -9,216 +9,216 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-09-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sidekiq
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- -
|
18
|
+
- - '>='
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '3'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- -
|
25
|
+
- - '>='
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '3'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: redis
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- -
|
32
|
+
- - ~>
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '3'
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- -
|
39
|
+
- - ~>
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '3'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: rufus-scheduler
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- -
|
46
|
+
- - ~>
|
47
47
|
- !ruby/object:Gem::Version
|
48
48
|
version: 3.1.8
|
49
49
|
type: :runtime
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- -
|
53
|
+
- - ~>
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: 3.1.8
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: multi_json
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
59
59
|
requirements:
|
60
|
-
- -
|
60
|
+
- - ~>
|
61
61
|
- !ruby/object:Gem::Version
|
62
62
|
version: '1'
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
|
-
- -
|
67
|
+
- - ~>
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '1'
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: tilt
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
|
-
- -
|
74
|
+
- - '>='
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: 1.4.0
|
77
77
|
type: :runtime
|
78
78
|
prerelease: false
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
|
-
- -
|
81
|
+
- - '>='
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: 1.4.0
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
85
|
name: rake
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
|
-
- -
|
88
|
+
- - ~>
|
89
89
|
- !ruby/object:Gem::Version
|
90
90
|
version: '10.0'
|
91
91
|
type: :development
|
92
92
|
prerelease: false
|
93
93
|
version_requirements: !ruby/object:Gem::Requirement
|
94
94
|
requirements:
|
95
|
-
- -
|
95
|
+
- - ~>
|
96
96
|
- !ruby/object:Gem::Version
|
97
97
|
version: '10.0'
|
98
98
|
- !ruby/object:Gem::Dependency
|
99
99
|
name: timecop
|
100
100
|
requirement: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
|
-
- -
|
102
|
+
- - ~>
|
103
103
|
- !ruby/object:Gem::Version
|
104
104
|
version: '0'
|
105
105
|
type: :development
|
106
106
|
prerelease: false
|
107
107
|
version_requirements: !ruby/object:Gem::Requirement
|
108
108
|
requirements:
|
109
|
-
- -
|
109
|
+
- - ~>
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: '0'
|
112
112
|
- !ruby/object:Gem::Dependency
|
113
113
|
name: mocha
|
114
114
|
requirement: !ruby/object:Gem::Requirement
|
115
115
|
requirements:
|
116
|
-
- -
|
116
|
+
- - ~>
|
117
117
|
- !ruby/object:Gem::Version
|
118
118
|
version: '0'
|
119
119
|
type: :development
|
120
120
|
prerelease: false
|
121
121
|
version_requirements: !ruby/object:Gem::Requirement
|
122
122
|
requirements:
|
123
|
-
- -
|
123
|
+
- - ~>
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: '0'
|
126
126
|
- !ruby/object:Gem::Dependency
|
127
127
|
name: rspec
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|
129
129
|
requirements:
|
130
|
-
- -
|
130
|
+
- - '>='
|
131
131
|
- !ruby/object:Gem::Version
|
132
132
|
version: '0'
|
133
133
|
type: :development
|
134
134
|
prerelease: false
|
135
135
|
version_requirements: !ruby/object:Gem::Requirement
|
136
136
|
requirements:
|
137
|
-
- -
|
137
|
+
- - '>='
|
138
138
|
- !ruby/object:Gem::Version
|
139
139
|
version: '0'
|
140
140
|
- !ruby/object:Gem::Dependency
|
141
141
|
name: mock_redis
|
142
142
|
requirement: !ruby/object:Gem::Requirement
|
143
143
|
requirements:
|
144
|
-
- -
|
144
|
+
- - ~>
|
145
145
|
- !ruby/object:Gem::Version
|
146
146
|
version: '0'
|
147
147
|
type: :development
|
148
148
|
prerelease: false
|
149
149
|
version_requirements: !ruby/object:Gem::Requirement
|
150
150
|
requirements:
|
151
|
-
- -
|
151
|
+
- - ~>
|
152
152
|
- !ruby/object:Gem::Version
|
153
153
|
version: '0'
|
154
154
|
- !ruby/object:Gem::Dependency
|
155
155
|
name: simplecov
|
156
156
|
requirement: !ruby/object:Gem::Requirement
|
157
157
|
requirements:
|
158
|
-
- -
|
158
|
+
- - ~>
|
159
159
|
- !ruby/object:Gem::Version
|
160
160
|
version: '0'
|
161
161
|
type: :development
|
162
162
|
prerelease: false
|
163
163
|
version_requirements: !ruby/object:Gem::Requirement
|
164
164
|
requirements:
|
165
|
-
- -
|
165
|
+
- - ~>
|
166
166
|
- !ruby/object:Gem::Version
|
167
167
|
version: '0'
|
168
168
|
- !ruby/object:Gem::Dependency
|
169
169
|
name: activejob
|
170
170
|
requirement: !ruby/object:Gem::Requirement
|
171
171
|
requirements:
|
172
|
-
- -
|
172
|
+
- - <
|
173
173
|
- !ruby/object:Gem::Version
|
174
174
|
version: '5'
|
175
175
|
type: :development
|
176
176
|
prerelease: false
|
177
177
|
version_requirements: !ruby/object:Gem::Requirement
|
178
178
|
requirements:
|
179
|
-
- -
|
179
|
+
- - <
|
180
180
|
- !ruby/object:Gem::Version
|
181
181
|
version: '5'
|
182
182
|
- !ruby/object:Gem::Dependency
|
183
183
|
name: coveralls
|
184
184
|
requirement: !ruby/object:Gem::Requirement
|
185
185
|
requirements:
|
186
|
-
- -
|
186
|
+
- - '>='
|
187
187
|
- !ruby/object:Gem::Version
|
188
188
|
version: '0'
|
189
189
|
type: :development
|
190
190
|
prerelease: false
|
191
191
|
version_requirements: !ruby/object:Gem::Requirement
|
192
192
|
requirements:
|
193
|
-
- -
|
193
|
+
- - '>='
|
194
194
|
- !ruby/object:Gem::Version
|
195
195
|
version: '0'
|
196
196
|
- !ruby/object:Gem::Dependency
|
197
197
|
name: rack-test
|
198
198
|
requirement: !ruby/object:Gem::Requirement
|
199
199
|
requirements:
|
200
|
-
- -
|
200
|
+
- - '>='
|
201
201
|
- !ruby/object:Gem::Version
|
202
202
|
version: '0'
|
203
203
|
type: :development
|
204
204
|
prerelease: false
|
205
205
|
version_requirements: !ruby/object:Gem::Requirement
|
206
206
|
requirements:
|
207
|
-
- -
|
207
|
+
- - '>='
|
208
208
|
- !ruby/object:Gem::Version
|
209
209
|
version: '0'
|
210
210
|
- !ruby/object:Gem::Dependency
|
211
211
|
name: sinatra
|
212
212
|
requirement: !ruby/object:Gem::Requirement
|
213
213
|
requirements:
|
214
|
-
- -
|
214
|
+
- - '>='
|
215
215
|
- !ruby/object:Gem::Version
|
216
216
|
version: '0'
|
217
217
|
type: :development
|
218
218
|
prerelease: false
|
219
219
|
version_requirements: !ruby/object:Gem::Requirement
|
220
220
|
requirements:
|
221
|
-
- -
|
221
|
+
- - '>='
|
222
222
|
- !ruby/object:Gem::Version
|
223
223
|
version: '0'
|
224
224
|
description: Light weight job scheduling extension for Sidekiq that adds support for
|
@@ -229,19 +229,20 @@ executables: []
|
|
229
229
|
extensions: []
|
230
230
|
extra_rdoc_files: []
|
231
231
|
files:
|
232
|
-
- MIT-LICENSE
|
233
|
-
- README.md
|
234
|
-
- Rakefile
|
235
232
|
- lib/sidekiq-scheduler.rb
|
236
|
-
- lib/sidekiq
|
233
|
+
- lib/sidekiq/scheduler.rb
|
237
234
|
- lib/sidekiq-scheduler/schedule.rb
|
238
235
|
- lib/sidekiq-scheduler/version.rb
|
236
|
+
- lib/sidekiq-scheduler/job_presenter.rb
|
237
|
+
- lib/sidekiq-scheduler/manager.rb
|
239
238
|
- lib/sidekiq-scheduler/web.rb
|
240
|
-
-
|
239
|
+
- web/views/recurring_jobs.erb
|
241
240
|
- web/locales/cs.yml
|
242
241
|
- web/locales/en.yml
|
243
242
|
- web/locales/es.yml
|
244
|
-
-
|
243
|
+
- MIT-LICENSE
|
244
|
+
- Rakefile
|
245
|
+
- README.md
|
245
246
|
homepage: https://github.com/moove-it/sidekiq-scheduler
|
246
247
|
licenses:
|
247
248
|
- MIT
|
@@ -252,17 +253,17 @@ require_paths:
|
|
252
253
|
- lib
|
253
254
|
required_ruby_version: !ruby/object:Gem::Requirement
|
254
255
|
requirements:
|
255
|
-
- -
|
256
|
+
- - '>='
|
256
257
|
- !ruby/object:Gem::Version
|
257
258
|
version: '0'
|
258
259
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
259
260
|
requirements:
|
260
|
-
- -
|
261
|
+
- - '>='
|
261
262
|
- !ruby/object:Gem::Version
|
262
263
|
version: '0'
|
263
264
|
requirements: []
|
264
265
|
rubyforge_project:
|
265
|
-
rubygems_version: 2.
|
266
|
+
rubygems_version: 2.0.14.1
|
266
267
|
signing_key:
|
267
268
|
specification_version: 4
|
268
269
|
summary: Light weight job scheduling extension for Sidekiq
|