resque-scheduler 4.4.0 → 4.11.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 +4 -4
- data/.github/dependabot.yml +12 -0
- data/.github/funding.yml +4 -0
- data/.github/workflows/codeql-analysis.yml +59 -0
- data/.github/workflows/rubocop.yml +27 -0
- data/.github/workflows/ruby.yml +82 -0
- data/AUTHORS.md +8 -0
- data/CHANGELOG.md +81 -2
- data/Gemfile +20 -3
- data/README.md +90 -19
- data/Rakefile +2 -5
- data/lib/resque/scheduler/cli.rb +2 -1
- data/lib/resque/scheduler/configuration.rb +45 -9
- data/lib/resque/scheduler/delaying_extensions.rb +86 -47
- data/lib/resque/scheduler/env.rb +5 -5
- data/lib/resque/scheduler/lock/base.rb +2 -2
- data/lib/resque/scheduler/locking.rb +1 -1
- data/lib/resque/scheduler/logger_builder.rb +17 -6
- data/lib/resque/scheduler/plugin.rb +9 -0
- data/lib/resque/scheduler/scheduling_extensions.rb +6 -5
- data/lib/resque/scheduler/server/views/delayed.erb +12 -11
- data/lib/resque/scheduler/server/views/delayed_schedules.erb +1 -1
- data/lib/resque/scheduler/server/views/delayed_timestamp.erb +1 -1
- data/lib/resque/scheduler/server/views/scheduler.erb +2 -2
- data/lib/resque/scheduler/server/views/search.erb +2 -5
- data/lib/resque/scheduler/server/views/search_form.erb +1 -5
- data/lib/resque/scheduler/server.rb +1 -1
- data/lib/resque/scheduler/signal_handling.rb +2 -2
- data/lib/resque/scheduler/util.rb +1 -1
- data/lib/resque/scheduler/version.rb +1 -1
- data/lib/resque/scheduler.rb +81 -18
- data/resque-scheduler.gemspec +15 -7
- metadata +25 -23
@@ -8,13 +8,18 @@ module Resque
|
|
8
8
|
yield self
|
9
9
|
end
|
10
10
|
|
11
|
+
attr_writer :environment
|
12
|
+
def environment
|
13
|
+
@environment ||= ENV
|
14
|
+
end
|
15
|
+
|
11
16
|
# Used in `#load_schedule_job`
|
12
17
|
attr_writer :env
|
13
18
|
|
14
19
|
def env
|
15
20
|
return @env if @env
|
16
21
|
@env ||= Rails.env if defined?(Rails) && Rails.respond_to?(:env)
|
17
|
-
@env ||=
|
22
|
+
@env ||= environment['RAILS_ENV']
|
18
23
|
@env
|
19
24
|
end
|
20
25
|
|
@@ -22,42 +27,48 @@ module Resque
|
|
22
27
|
attr_writer :verbose
|
23
28
|
|
24
29
|
def verbose
|
25
|
-
@verbose ||=
|
30
|
+
@verbose ||= to_bool(environment['VERBOSE'])
|
26
31
|
end
|
27
32
|
|
28
33
|
# If set, produces no output
|
29
34
|
attr_writer :quiet
|
30
35
|
|
31
36
|
def quiet
|
32
|
-
@quiet ||=
|
37
|
+
@quiet ||= to_bool(environment['QUIET'])
|
33
38
|
end
|
34
39
|
|
35
40
|
# If set, will write messages to the file
|
36
41
|
attr_writer :logfile
|
37
42
|
|
38
43
|
def logfile
|
39
|
-
@logfile ||=
|
44
|
+
@logfile ||= environment['LOGFILE']
|
40
45
|
end
|
41
46
|
|
42
|
-
# Sets whether to log in 'text' or '
|
47
|
+
# Sets whether to log in 'text', 'json' or 'logfmt'
|
43
48
|
attr_writer :logformat
|
44
49
|
|
45
50
|
def logformat
|
46
|
-
@logformat ||=
|
51
|
+
@logformat ||= environment['LOGFORMAT']
|
47
52
|
end
|
48
53
|
|
49
54
|
# If set, will try to update the schedule in the loop
|
50
55
|
attr_writer :dynamic
|
51
56
|
|
52
57
|
def dynamic
|
53
|
-
@dynamic ||=
|
58
|
+
@dynamic ||= to_bool(environment['DYNAMIC_SCHEDULE'])
|
54
59
|
end
|
55
60
|
|
56
61
|
# If set, will append the app name to procline
|
57
62
|
attr_writer :app_name
|
58
63
|
|
59
64
|
def app_name
|
60
|
-
@app_name ||=
|
65
|
+
@app_name ||= environment['APP_NAME']
|
66
|
+
end
|
67
|
+
|
68
|
+
def delayed_requeue_batch_size
|
69
|
+
@delayed_requeue_batch_size ||= \
|
70
|
+
ENV['DELAYED_REQUEUE_BATCH_SIZE'].to_i if environment['DELAYED_REQUEUE_BATCH_SIZE']
|
71
|
+
@delayed_requeue_batch_size ||= 100
|
61
72
|
end
|
62
73
|
|
63
74
|
# Amount of time in seconds to sleep between polls of the delayed
|
@@ -66,7 +77,32 @@ module Resque
|
|
66
77
|
|
67
78
|
def poll_sleep_amount
|
68
79
|
@poll_sleep_amount ||=
|
69
|
-
Float(
|
80
|
+
Float(environment.fetch('RESQUE_SCHEDULER_INTERVAL', '5'))
|
81
|
+
end
|
82
|
+
|
83
|
+
# Sets timeout for Resque::Scheduler::Lock::Base
|
84
|
+
attr_writer :lock_timeout
|
85
|
+
|
86
|
+
def lock_timeout
|
87
|
+
@lock_timeout ||= environment.fetch('LOCK_TIMEOUT', 60 * 3).to_i
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Copied from https://github.com/rails/rails/blob/main/activemodel/lib/active_model/type/boolean.rb#L17
|
93
|
+
TRUE_VALUES = [
|
94
|
+
true, 1,
|
95
|
+
'1', :'1',
|
96
|
+
't', :t,
|
97
|
+
'T', :T,
|
98
|
+
'true', :true,
|
99
|
+
'TRUE', :TRUE,
|
100
|
+
'on', :on,
|
101
|
+
'ON', :ON
|
102
|
+
].to_set.freeze
|
103
|
+
|
104
|
+
def to_bool(value)
|
105
|
+
TRUE_VALUES.include?(value)
|
70
106
|
end
|
71
107
|
end
|
72
108
|
end
|
@@ -22,24 +22,22 @@ module Resque
|
|
22
22
|
# timestamp has passed. It respects Resque.inline option, by
|
23
23
|
# creating the job right away instead of adding to the queue.
|
24
24
|
def enqueue_at_with_queue(queue, timestamp, klass, *args)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
25
|
+
plugin.process_schedule_hooks(klass, *args) do
|
26
|
+
if Resque.inline? || timestamp.to_i <= Time.now.to_i
|
27
|
+
# Just create the job and let resque perform it right away with
|
28
|
+
# inline. If the class is a custom job class, call self#scheduled
|
29
|
+
# on it. This allows you to do things like
|
30
|
+
# Resque.enqueue_at(timestamp, CustomJobClass, :opt1 => val1).
|
31
|
+
# Otherwise, pass off to Resque.
|
32
|
+
if klass.respond_to?(:scheduled)
|
33
|
+
klass.scheduled(queue, klass.to_s, *args)
|
34
|
+
else
|
35
|
+
Resque.enqueue_to(queue, klass, *args)
|
36
|
+
end
|
35
37
|
else
|
36
|
-
|
38
|
+
delayed_push(timestamp, job_to_hash_with_queue(queue, klass, args))
|
37
39
|
end
|
38
|
-
else
|
39
|
-
delayed_push(timestamp, job_to_hash_with_queue(queue, klass, args))
|
40
40
|
end
|
41
|
-
|
42
|
-
plugin.run_after_schedule_hooks(klass, *args)
|
43
41
|
end
|
44
42
|
|
45
43
|
# Identical to enqueue_at but takes number_of_seconds_from_now
|
@@ -63,16 +61,34 @@ module Resque
|
|
63
61
|
klass, *args)
|
64
62
|
end
|
65
63
|
|
64
|
+
# Update the delayed timestamp of any matching delayed jobs or enqueue a
|
65
|
+
# new job if no matching jobs are found. Returns the number of delayed or
|
66
|
+
# enqueued jobs.
|
67
|
+
def delay_or_enqueue_at(timestamp, klass, *args)
|
68
|
+
count = remove_delayed(klass, *args)
|
69
|
+
count = 1 if count == 0
|
70
|
+
|
71
|
+
count.times do
|
72
|
+
enqueue_at(timestamp, klass, *args)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Identical to +delay_or_enqueue_at+, except it takes
|
77
|
+
# number_of_seconds_from_now instead of a timestamp
|
78
|
+
def delay_or_enqueue_in(number_of_seconds_from_now, klass, *args)
|
79
|
+
delay_or_enqueue_at(Time.now + number_of_seconds_from_now, klass, *args)
|
80
|
+
end
|
81
|
+
|
66
82
|
# Used internally to stuff the item into the schedule sorted list.
|
67
|
-
# +timestamp+ can be either in seconds or a datetime object
|
68
|
-
#
|
69
|
-
# that time, else false
|
83
|
+
# +timestamp+ can be either in seconds or a datetime object. The
|
84
|
+
# insertion time complexity is O(log(n)). Returns true if it's
|
85
|
+
# the first job to be scheduled at that time, else false.
|
70
86
|
def delayed_push(timestamp, item)
|
71
87
|
# First add this item to the list for this timestamp
|
72
88
|
redis.rpush("delayed:#{timestamp.to_i}", encode(item))
|
73
89
|
|
74
90
|
# Store the timestamps at with this item occurs
|
75
|
-
redis.sadd("timestamps:#{encode(item)}", "delayed:#{timestamp.to_i}")
|
91
|
+
redis.sadd("timestamps:#{encode(item)}", ["delayed:#{timestamp.to_i}"])
|
76
92
|
|
77
93
|
# Now, add this timestamp to the zsets. The score and the value are
|
78
94
|
# the same since we'll be querying by timestamp, and we don't have
|
@@ -88,6 +104,7 @@ module Resque
|
|
88
104
|
end
|
89
105
|
|
90
106
|
# Returns the size of the delayed queue schedule
|
107
|
+
# this does not represent the number of items in the queue to be scheduled
|
91
108
|
def delayed_queue_schedule_size
|
92
109
|
redis.zcard :delayed_queue_schedule
|
93
110
|
end
|
@@ -121,7 +138,7 @@ module Resque
|
|
121
138
|
key = "delayed:#{timestamp.to_i}"
|
122
139
|
|
123
140
|
encoded_item = redis.lpop(key)
|
124
|
-
redis.srem("timestamps:#{encoded_item}", key)
|
141
|
+
redis.srem("timestamps:#{encoded_item}", [key])
|
125
142
|
item = decode(encoded_item)
|
126
143
|
|
127
144
|
# If the list is empty, remove it.
|
@@ -134,10 +151,7 @@ module Resque
|
|
134
151
|
Array(redis.zrange(:delayed_queue_schedule, 0, -1)).each do |item|
|
135
152
|
key = "delayed:#{item}"
|
136
153
|
items = redis.lrange(key, 0, -1)
|
137
|
-
redis.
|
138
|
-
items.each { |ts_item| redis.del("timestamps:#{ts_item}") }
|
139
|
-
end
|
140
|
-
redis.del key
|
154
|
+
redis.del(key, items.map { |ts_item| "timestamps:#{ts_item}" })
|
141
155
|
end
|
142
156
|
|
143
157
|
redis.del :delayed_queue_schedule
|
@@ -149,6 +163,11 @@ module Resque
|
|
149
163
|
remove_delayed_job(search)
|
150
164
|
end
|
151
165
|
|
166
|
+
def remove_delayed_in_queue(klass, queue, *args)
|
167
|
+
search = encode(job_to_hash_with_queue(queue, klass, args))
|
168
|
+
remove_delayed_job(search)
|
169
|
+
end
|
170
|
+
|
152
171
|
# Given an encoded item, enqueue it now
|
153
172
|
def enqueue_delayed(klass, *args)
|
154
173
|
hash = job_to_hash(klass, args)
|
@@ -157,6 +176,13 @@ module Resque
|
|
157
176
|
end
|
158
177
|
end
|
159
178
|
|
179
|
+
def enqueue_delayed_with_queue(klass, queue, *args)
|
180
|
+
hash = job_to_hash_with_queue(queue, klass, args)
|
181
|
+
remove_delayed_in_queue(klass, queue, *args).times do
|
182
|
+
Resque::Scheduler.enqueue_from_config(hash)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
160
186
|
# Given a block, remove jobs that return true from a block
|
161
187
|
#
|
162
188
|
# This allows for removal of delayed jobs that have arguments matching
|
@@ -181,7 +207,15 @@ module Resque
|
|
181
207
|
found_jobs.reduce(0) do |sum, encoded_job|
|
182
208
|
decoded_job = decode(encoded_job)
|
183
209
|
klass = Util.constantize(decoded_job['class'])
|
184
|
-
|
210
|
+
queue = decoded_job['queue']
|
211
|
+
|
212
|
+
if queue
|
213
|
+
jobs_queued = enqueue_delayed_with_queue(klass, queue, *decoded_job['args'])
|
214
|
+
else
|
215
|
+
jobs_queued = enqueue_delayed(klass, *decoded_job['args'])
|
216
|
+
end
|
217
|
+
|
218
|
+
jobs_queued + sum
|
185
219
|
end
|
186
220
|
end
|
187
221
|
|
@@ -196,9 +230,9 @@ module Resque
|
|
196
230
|
|
197
231
|
# Beyond 100 there's almost no improvement in speed
|
198
232
|
found = timestamps.each_slice(100).map do |ts_group|
|
199
|
-
jobs = redis.pipelined do |
|
233
|
+
jobs = redis.pipelined do |pipeline|
|
200
234
|
ts_group.each do |ts|
|
201
|
-
|
235
|
+
pipeline.lrange("delayed:#{ts}", 0, -1)
|
202
236
|
end
|
203
237
|
end
|
204
238
|
|
@@ -221,7 +255,7 @@ module Resque
|
|
221
255
|
key = "delayed:#{timestamp.to_i}"
|
222
256
|
encoded_job = encode(job_to_hash(klass, args))
|
223
257
|
|
224
|
-
redis.srem("timestamps:#{encoded_job}", key)
|
258
|
+
redis.srem("timestamps:#{encoded_job}", [key])
|
225
259
|
count = redis.lrem(key, 0, encoded_job)
|
226
260
|
clean_up_timestamp(key, timestamp)
|
227
261
|
|
@@ -261,6 +295,22 @@ module Resque
|
|
261
295
|
redis.hget('delayed:last_enqueued_at', job_name)
|
262
296
|
end
|
263
297
|
|
298
|
+
def clean_up_timestamp(key, timestamp)
|
299
|
+
# Use a watch here to ensure nobody adds jobs to this delayed
|
300
|
+
# queue while we're removing it.
|
301
|
+
redis.watch(key) do
|
302
|
+
if redis.llen(key).to_i == 0
|
303
|
+
# If the list is empty, remove it.
|
304
|
+
redis.multi do |transaction|
|
305
|
+
transaction.del(key)
|
306
|
+
transaction.zrem(:delayed_queue_schedule, timestamp.to_i)
|
307
|
+
end
|
308
|
+
else
|
309
|
+
redis.redis.unwatch
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
264
314
|
private
|
265
315
|
|
266
316
|
def job_to_hash(klass, args)
|
@@ -271,38 +321,27 @@ module Resque
|
|
271
321
|
{ class: klass.to_s, args: args, queue: queue }
|
272
322
|
end
|
273
323
|
|
324
|
+
# Removes a job from the queue, but not modify the timestamp schedule. This method
|
325
|
+
# will not effect the output of `delayed_queue_schedule_size`
|
274
326
|
def remove_delayed_job(encoded_job)
|
275
327
|
return 0 if Resque.inline?
|
276
328
|
|
277
329
|
timestamps = redis.smembers("timestamps:#{encoded_job}")
|
278
330
|
|
279
|
-
replies = redis.pipelined do
|
331
|
+
replies = redis.pipelined do |pipeline|
|
280
332
|
timestamps.each do |key|
|
281
|
-
|
282
|
-
|
333
|
+
pipeline.lrem(key, 0, encoded_job)
|
334
|
+
pipeline.srem("timestamps:#{encoded_job}", [key])
|
283
335
|
end
|
284
336
|
end
|
285
337
|
|
338
|
+
# timestamp key is not removed from the schedule, this is done later
|
339
|
+
# by the scheduler loop
|
340
|
+
|
286
341
|
return 0 if replies.nil? || replies.empty?
|
287
342
|
replies.each_slice(2).map(&:first).inject(:+)
|
288
343
|
end
|
289
344
|
|
290
|
-
def clean_up_timestamp(key, timestamp)
|
291
|
-
# Use a watch here to ensure nobody adds jobs to this delayed
|
292
|
-
# queue while we're removing it.
|
293
|
-
redis.watch(key) do
|
294
|
-
if redis.llen(key).to_i == 0
|
295
|
-
# If the list is empty, remove it.
|
296
|
-
redis.multi do
|
297
|
-
redis.del(key)
|
298
|
-
redis.zrem(:delayed_queue_schedule, timestamp.to_i)
|
299
|
-
end
|
300
|
-
else
|
301
|
-
redis.redis.unwatch
|
302
|
-
end
|
303
|
-
end
|
304
|
-
end
|
305
|
-
|
306
345
|
def search_first_delayed_timestamp_in_range(start_at, stop_at)
|
307
346
|
start_at = start_at.nil? ? '-inf' : start_at.to_i
|
308
347
|
stop_at = stop_at.nil? ? '+inf' : stop_at.to_i
|
data/lib/resque/scheduler/env.rb
CHANGED
@@ -38,12 +38,8 @@ module Resque
|
|
38
38
|
true
|
39
39
|
end
|
40
40
|
|
41
|
-
unless Process.respond_to?('daemon')
|
42
|
-
abort 'background option is set, which requires ruby >= 1.9'
|
43
|
-
end
|
44
|
-
|
45
41
|
Process.daemon(true, !Resque::Scheduler.quiet)
|
46
|
-
Resque.redis.
|
42
|
+
Resque.redis.reconnect
|
47
43
|
end
|
48
44
|
|
49
45
|
def setup_pid_file
|
@@ -58,6 +54,7 @@ module Resque
|
|
58
54
|
at_exit { cleanup_pid_file }
|
59
55
|
end
|
60
56
|
|
57
|
+
# rubocop:disable Metrics/AbcSize
|
61
58
|
def setup_scheduler_configuration
|
62
59
|
Resque::Scheduler.configure do |c|
|
63
60
|
c.app_name = options[:app_name] if options.key?(:app_name)
|
@@ -70,6 +67,8 @@ module Resque
|
|
70
67
|
|
71
68
|
c.logformat = options[:logformat] if options.key?(:logformat)
|
72
69
|
|
70
|
+
c.lock_timeout = options[:lock_timeout] if options.key?(:lock_timeout)
|
71
|
+
|
73
72
|
if (psleep = options[:poll_sleep_amount]) && !psleep.nil?
|
74
73
|
c.poll_sleep_amount = Float(psleep)
|
75
74
|
end
|
@@ -77,6 +76,7 @@ module Resque
|
|
77
76
|
c.verbose = !!options[:verbose] if options.key?(:verbose)
|
78
77
|
end
|
79
78
|
end
|
79
|
+
# rubocop:enable Metrics/AbcSize
|
80
80
|
|
81
81
|
def cleanup_pid_file
|
82
82
|
return unless pidfile_path
|
@@ -11,7 +11,7 @@ module Resque
|
|
11
11
|
@key = key
|
12
12
|
|
13
13
|
# 3 minute default timeout
|
14
|
-
@timeout = options[:timeout] ||
|
14
|
+
@timeout = options[:timeout] || Resque::Scheduler.lock_timeout
|
15
15
|
end
|
16
16
|
|
17
17
|
# Attempts to acquire the lock. Returns true if successfully acquired.
|
@@ -47,7 +47,7 @@ module Resque
|
|
47
47
|
|
48
48
|
def hostname
|
49
49
|
local_hostname = Socket.gethostname
|
50
|
-
|
50
|
+
Addrinfo.getaddrinfo(local_hostname, 'http').first.getnameinfo.first
|
51
51
|
rescue
|
52
52
|
local_hostname
|
53
53
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# ### Locking the scheduler process
|
4
4
|
#
|
5
|
-
# There are two places in resque-scheduler that need to be
|
5
|
+
# There are two places in resque-scheduler that need to be synchronized in order
|
6
6
|
# to be able to run redundant scheduler processes while ensuring jobs don't get
|
7
7
|
# queued multiple times when the master process changes.
|
8
8
|
#
|
@@ -15,7 +15,7 @@ module Resque
|
|
15
15
|
# - :quiet if logger needs to be silent for all levels. Default - false
|
16
16
|
# - :verbose if there is a need in debug messages. Default - false
|
17
17
|
# - :log_dev to output logs into a desired file. Default - STDOUT
|
18
|
-
# - :format log format, either 'text' or '
|
18
|
+
# - :format log format, either 'text', 'json' or 'logfmt'. Default - 'text'
|
19
19
|
#
|
20
20
|
# Example:
|
21
21
|
#
|
@@ -32,6 +32,7 @@ module Resque
|
|
32
32
|
# Returns an instance of MonoLogger
|
33
33
|
def build
|
34
34
|
logger = MonoLogger.new(@log_dev)
|
35
|
+
logger.progname = 'resque-scheduler'.freeze
|
35
36
|
logger.level = level
|
36
37
|
logger.formatter = send(:"#{@format}_formatter")
|
37
38
|
logger
|
@@ -50,21 +51,31 @@ module Resque
|
|
50
51
|
end
|
51
52
|
|
52
53
|
def text_formatter
|
53
|
-
proc do |severity, datetime,
|
54
|
-
"
|
54
|
+
proc do |severity, datetime, progname, msg|
|
55
|
+
"#{progname}: [#{severity}] #{datetime.iso8601}: #{msg}\n"
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
58
59
|
def json_formatter
|
59
60
|
proc do |severity, datetime, progname, msg|
|
60
61
|
require 'json'
|
61
|
-
|
62
|
-
name:
|
62
|
+
log_data = {
|
63
|
+
name: progname,
|
63
64
|
progname: progname,
|
64
65
|
level: severity,
|
65
66
|
timestamp: datetime.iso8601,
|
66
67
|
msg: msg
|
67
|
-
|
68
|
+
}
|
69
|
+
JSON.dump(log_data) + "\n"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def logfmt_formatter
|
74
|
+
proc do |severity, datetime, progname, msg|
|
75
|
+
"timestamp=\"#{datetime.iso8601}\" " \
|
76
|
+
"level=\"#{severity}\" " \
|
77
|
+
"progname=\"#{progname}\" " \
|
78
|
+
"msg=\"#{msg}\"\n"
|
68
79
|
end
|
69
80
|
end
|
70
81
|
end
|
@@ -3,6 +3,15 @@
|
|
3
3
|
module Resque
|
4
4
|
module Scheduler
|
5
5
|
module Plugin
|
6
|
+
def self.process_schedule_hooks(klass, *args)
|
7
|
+
# the documentation states that if any before_schedule hook returns
|
8
|
+
# false, the process should not be enqueued
|
9
|
+
return unless run_before_schedule_hooks(klass, *args)
|
10
|
+
|
11
|
+
yield
|
12
|
+
run_after_schedule_hooks(klass, *args)
|
13
|
+
end
|
14
|
+
|
6
15
|
def self.hooks(job, pattern)
|
7
16
|
job.methods.grep(/^#{pattern}/).sort
|
8
17
|
end
|
@@ -36,7 +36,7 @@ module Resque
|
|
36
36
|
# :args can be any yaml which will be converted to a ruby literal and
|
37
37
|
# passed in a params. (optional)
|
38
38
|
#
|
39
|
-
# :
|
39
|
+
# :rails_env is the list of envs where the job gets loaded. Envs are
|
40
40
|
# comma separated (optional)
|
41
41
|
#
|
42
42
|
# :description is just that, a description of the job (optional). If
|
@@ -91,7 +91,7 @@ module Resque
|
|
91
91
|
non_persistent_schedules[name] = decode(encode(config))
|
92
92
|
end
|
93
93
|
|
94
|
-
redis.sadd(:schedules_changed, name)
|
94
|
+
redis.sadd(:schedules_changed, [name])
|
95
95
|
reload_schedule! if reload
|
96
96
|
end
|
97
97
|
|
@@ -101,12 +101,13 @@ module Resque
|
|
101
101
|
end
|
102
102
|
|
103
103
|
# remove a given schedule by name
|
104
|
-
|
104
|
+
# Preventing a reload is optional and available to batch operations
|
105
|
+
def remove_schedule(name, reload = true)
|
105
106
|
non_persistent_schedules.delete(name)
|
106
107
|
redis.hdel(:persistent_schedules, name)
|
107
|
-
redis.sadd(:schedules_changed, name)
|
108
|
+
redis.sadd(:schedules_changed, [name])
|
108
109
|
|
109
|
-
reload_schedule!
|
110
|
+
reload_schedule! if reload
|
110
111
|
end
|
111
112
|
|
112
113
|
private
|
@@ -1,9 +1,9 @@
|
|
1
1
|
<h1>Delayed Jobs</h1>
|
2
|
-
|
2
|
+
<% size = resque.delayed_queue_schedule_size %>
|
3
3
|
|
4
4
|
<%= scheduler_view :search_form, layout: false %>
|
5
5
|
|
6
|
-
<p style="
|
6
|
+
<p style="color: red; font-weight: bold;">
|
7
7
|
<%= @error_message %>
|
8
8
|
</p>
|
9
9
|
|
@@ -16,6 +16,14 @@
|
|
16
16
|
Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%= size %></b> timestamps
|
17
17
|
</p>
|
18
18
|
|
19
|
+
<% if size > 0 %>
|
20
|
+
<div style="padding-bottom: 10px">
|
21
|
+
<form method="POST" action="<%= u 'delayed/clear' %>" class='clear-delayed confirmSubmission'>
|
22
|
+
<input type='submit' name='' value='Clear Delayed Jobs' />
|
23
|
+
</form>
|
24
|
+
</div>
|
25
|
+
<% end %>
|
26
|
+
|
19
27
|
<table>
|
20
28
|
<tr>
|
21
29
|
<th></th>
|
@@ -27,7 +35,7 @@
|
|
27
35
|
</tr>
|
28
36
|
<% resque.delayed_queue_peek(start, 20).each do |timestamp| %>
|
29
37
|
<tr>
|
30
|
-
<td>
|
38
|
+
<td style="padding-top: 12px; padding-bottom: 2px; width: 10px">
|
31
39
|
<form action="<%= u "/delayed/queue_now" %>" method="post">
|
32
40
|
<input type="hidden" name="timestamp" value="<%= timestamp.to_i %>">
|
33
41
|
<input type="submit" value="Queue now">
|
@@ -46,18 +54,11 @@
|
|
46
54
|
<td><%= h(show_job_arguments(job['args'])) if job && delayed_timestamp_size == 1 %></td>
|
47
55
|
<td>
|
48
56
|
<% if job %>
|
49
|
-
<a href="<%=u URI("/delayed/jobs/#{
|
57
|
+
<a href="<%= u URI("/delayed/jobs/#{CGI.escape(job['class'])}?args=" + CGI.escape(job['args'].to_json)) %>">All schedules</a>
|
50
58
|
<% end %>
|
51
59
|
</td>
|
52
60
|
</tr>
|
53
61
|
<% end %>
|
54
62
|
</table>
|
55
63
|
|
56
|
-
<% if size > 0 %>
|
57
|
-
<br>
|
58
|
-
<form method="POST" action="<%=u 'delayed/clear'%>" class='clear-delayed'>
|
59
|
-
<input type='submit' name='' value='Clear Delayed Jobs' />
|
60
|
-
</form>
|
61
|
-
<% end %>
|
62
|
-
|
63
64
|
<%= partial :next_more, :start => start, :size => size %>
|
@@ -8,7 +8,7 @@
|
|
8
8
|
<br/> Current master: <%= Resque.redis.get(Resque::Scheduler.master_lock.key) %>
|
9
9
|
</p>
|
10
10
|
<p class='intro'>
|
11
|
-
|
11
|
+
The highlighted jobs are skipped for current environment.
|
12
12
|
</p>
|
13
13
|
<div style="overflow-y: auto; width:100%; padding: 0px 5px;">
|
14
14
|
<table>
|
@@ -29,7 +29,7 @@
|
|
29
29
|
<% Resque.schedule.keys.sort.each_with_index do |name, index| %>
|
30
30
|
<% config = Resque.schedule[name] %>
|
31
31
|
<tr style="<%= scheduled_in_this_env?(name) ? '' : 'color: #9F6000;background: #FEEFB3;' %>">
|
32
|
-
|
32
|
+
<td style="padding-left: 15px;"><%= index + 1 %>.</td>
|
33
33
|
<% if Resque::Scheduler.dynamic %>
|
34
34
|
<td style="padding-top: 12px; padding-bottom: 2px; width: 10px">
|
35
35
|
<form action="<%= u "/schedule" %>" method="post" style="margin-left: 0">
|
@@ -13,13 +13,13 @@
|
|
13
13
|
</tr>
|
14
14
|
<% delayed.each do |job| %>
|
15
15
|
<tr>
|
16
|
-
<td>
|
16
|
+
<td style="padding-top: 12px; padding-bottom: 2px; width: 10px">
|
17
17
|
<form action="<%= u "/delayed/queue_now" %>" method="post">
|
18
18
|
<input type="hidden" name="timestamp" value="<%= job['timestamp'].to_i %>">
|
19
19
|
<input type="submit" value="Queue now">
|
20
20
|
</form>
|
21
21
|
</td>
|
22
|
-
<td>
|
22
|
+
<td style="padding-top: 12px; padding-bottom: 2px; width: 10px">
|
23
23
|
<form action="<%= u "/delayed/cancel_now" %>" method="post">
|
24
24
|
<input type="hidden" name="timestamp" value="<%= job['timestamp'].to_i %>">
|
25
25
|
<input type="hidden" name="klass" value="<%= job['class'] %>">
|
@@ -33,7 +33,6 @@
|
|
33
33
|
</tr>
|
34
34
|
<% end %>
|
35
35
|
</table>
|
36
|
-
</h1>
|
37
36
|
|
38
37
|
<% queued = @jobs.select { |j| j['where_at'] == 'queued' } %>
|
39
38
|
<h1>Queued jobs</h1>
|
@@ -68,5 +67,3 @@
|
|
68
67
|
</tr>
|
69
68
|
<% end %>
|
70
69
|
</table>
|
71
|
-
|
72
|
-
|
@@ -87,7 +87,7 @@ module Resque
|
|
87
87
|
def delayed_jobs_klass
|
88
88
|
begin
|
89
89
|
klass = Resque::Scheduler::Util.constantize(params[:klass])
|
90
|
-
@args = JSON.load(
|
90
|
+
@args = JSON.load(CGI.unescape(params[:args]))
|
91
91
|
@timestamps = Resque.scheduled_at(klass, *@args)
|
92
92
|
rescue
|
93
93
|
@timestamps = []
|