resque-scheduler 5.0.0 → 5.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 +4 -4
- data/CHANGELOG.md +18 -1
- data/lib/resque/scheduler/delaying_extensions.rb +14 -6
- data/lib/resque/scheduler/env.rb +7 -7
- data/lib/resque/scheduler/server/views/delayed_timestamp.erb +1 -1
- data/lib/resque/scheduler/server/views/requeue-params.erb +5 -5
- data/lib/resque/scheduler/server/views/scheduler.erb +2 -2
- data/lib/resque/scheduler/server/views/search.erb +6 -6
- data/lib/resque/scheduler/version.rb +1 -1
- data/lib/resque/scheduler.rb +17 -5
- metadata +3 -7
- data/.github/funding.yml +0 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 046040c8fa18b0cb7aabc7fdc46ff008f7b0c30f9aa0500137f5c6c987bdbabc
|
|
4
|
+
data.tar.gz: b3e243c3c9ccefa0c1282b492b70a9b26677fa2b4be6ead096e339c721d9f86a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8b6c3caa8b47c24aee900d2c348cabb20d1a32a5fce88e02a974649ca6c711b1703562393902c7638e14c1a11180b1901b126480e905c7cca8b2dbd47d27daa3
|
|
7
|
+
data.tar.gz: 7b2665804103943b3bf9a98b84134ba71a0ac00ace8e0c2ed7c765873445c801bea14c8ae9082fec4133e6f3be0314bdf1485ecd47c7bab8aba6d8d85ef36ec8
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
**ATTN**: This project uses [semantic versioning](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
## [5.0.1] - 2026-10-11
|
|
6
|
+
### Changed
|
|
7
|
+
* Pipeline `count_all_scheduled_jobs` for significantly faster execution by @top-sigrid in #822
|
|
8
|
+
* Avoid unnecessary reloads in static mode by @ekmartin in #823
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
* Skip invalid schedules without interrupting scheduler loading by @taf2 in #543
|
|
12
|
+
* Encode a delayed job once so its timestamps key can be cleaned up by @kamaljoshi in #827 (originally #599)
|
|
13
|
+
* Log to the logfile when backgrounded by @meatballhat in #828 (originally #568)
|
|
14
|
+
* Require redis before using its error classes by @neuromanskie in #829
|
|
15
|
+
* Fix flaky test by using strings instead of symbols for JSON-serialized args by @top-sigrid in #813
|
|
16
|
+
|
|
17
|
+
### Security
|
|
18
|
+
* Escape job class and queue names in the scheduler web UI by @PatrickTulskie in #830
|
|
19
|
+
* Escape schedule names and parameters on the requeue params page by @PatrickTulskie in #831
|
|
20
|
+
* Escape the scheduler environment and master lock holder on the schedule page by @PatrickTulskie in #832
|
|
21
|
+
|
|
5
22
|
## [5.0.0] - 2026-01-15
|
|
6
23
|
### Breaking Changes
|
|
7
24
|
* Ruby requirement: `>= 2.3.0` → `>= 3.0.0`
|
|
@@ -32,7 +49,7 @@
|
|
|
32
49
|
* Fixed test compatibility with Rack 3 by @PatrickTulskie in #809
|
|
33
50
|
* Fixed race condition in scheduler task tests by @PatrickTulskie in #809
|
|
34
51
|
|
|
35
|
-
## [4.
|
|
52
|
+
## [4.11.0] - 2024-12-30
|
|
36
53
|
### Added
|
|
37
54
|
* Expose timeout for lock via environment variable configuration by @pmm4654 in #786
|
|
38
55
|
* Add Ruby 3.3 to the CI matrix by @santiagorodriguez96 in #797
|
|
@@ -83,11 +83,13 @@ module Resque
|
|
|
83
83
|
# insertion time complexity is O(log(n)). Returns true if it's
|
|
84
84
|
# the first job to be scheduled at that time, else false.
|
|
85
85
|
def delayed_push(timestamp, item)
|
|
86
|
+
encoded_job = encode(item)
|
|
87
|
+
|
|
86
88
|
# First add this item to the list for this timestamp
|
|
87
|
-
redis.rpush("delayed:#{timestamp.to_i}",
|
|
89
|
+
redis.rpush("delayed:#{timestamp.to_i}", encoded_job)
|
|
88
90
|
|
|
89
91
|
# Store the timestamps at with this item occurs
|
|
90
|
-
redis.sadd("timestamps:#{
|
|
92
|
+
redis.sadd("timestamps:#{encoded_job}", ["delayed:#{timestamp.to_i}"])
|
|
91
93
|
|
|
92
94
|
# Now, add this timestamp to the zsets. The score and the value are
|
|
93
95
|
# the same since we'll be querying by timestamp, and we don't have
|
|
@@ -261,12 +263,18 @@ module Resque
|
|
|
261
263
|
count
|
|
262
264
|
end
|
|
263
265
|
|
|
266
|
+
# Counts all jobs across all delayed timestamps.
|
|
267
|
+
# Uses pipelined LLEN calls in batches to avoid blocking the Redis thread
|
|
268
|
+
# for too long while being significantly faster than sequential calls.
|
|
264
269
|
def count_all_scheduled_jobs
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
270
|
+
timestamps = Array(redis.zrange(:delayed_queue_schedule, 0, -1))
|
|
271
|
+
return 0 if timestamps.empty?
|
|
272
|
+
|
|
273
|
+
timestamps.each_slice(10_000).sum do |batch|
|
|
274
|
+
redis.pipelined do |pipeline|
|
|
275
|
+
batch.each { |ts| pipeline.llen("delayed:#{ts}") }
|
|
276
|
+
end.sum
|
|
268
277
|
end
|
|
269
|
-
total_jobs
|
|
270
278
|
end
|
|
271
279
|
|
|
272
280
|
# Discover if a job has been delayed.
|
data/lib/resque/scheduler/env.rb
CHANGED
|
@@ -30,18 +30,18 @@ module Resque
|
|
|
30
30
|
def setup_backgrounding
|
|
31
31
|
return unless options[:background]
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
# stderr/stdout to /dev/null
|
|
35
|
-
Resque::Scheduler.quiet = if options.key?(:quiet)
|
|
36
|
-
!!options[:quiet]
|
|
37
|
-
else
|
|
38
|
-
true
|
|
39
|
-
end
|
|
33
|
+
Resque::Scheduler.quiet = background_quiet?
|
|
40
34
|
|
|
41
35
|
Process.daemon(true, !Resque::Scheduler.quiet)
|
|
42
36
|
Resque.redis.reconnect
|
|
43
37
|
end
|
|
44
38
|
|
|
39
|
+
def background_quiet?
|
|
40
|
+
return !!options[:quiet] if options.key?(:quiet)
|
|
41
|
+
return false if options.key?(:logfile)
|
|
42
|
+
!!options[:background]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
45
|
def setup_pid_file
|
|
46
46
|
return unless options[:pidfile]
|
|
47
47
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<% jobs = resque.delayed_timestamp_peek(timestamp, start, 20) %>
|
|
13
13
|
<% jobs.each do |job| %>
|
|
14
14
|
<tr>
|
|
15
|
-
<td class='class'><%= job['class'] %></td>
|
|
15
|
+
<td class='class'><%= h job['class'] %></td>
|
|
16
16
|
<td class='args'><%= h show_job_arguments(job['args']) %></td>
|
|
17
17
|
</tr>
|
|
18
18
|
<% end %>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<h1><%= @job_name %></h1>
|
|
1
|
+
<h1><%= h @job_name %></h1>
|
|
2
2
|
|
|
3
3
|
<p class='intro'>
|
|
4
4
|
This job requires parameters:
|
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
<% value ||= {} %>
|
|
11
11
|
<tr>
|
|
12
12
|
<td>
|
|
13
|
-
<%= key %>
|
|
13
|
+
<%= h key %>
|
|
14
14
|
<% if value['description'] || value[:description] %>
|
|
15
|
-
<span style="border-bottom:1px dotted;" title="<%=value
|
|
15
|
+
<span style="border-bottom:1px dotted;" title="<%= h(value['description'] || value[:description]) %>">(?)</span>
|
|
16
16
|
<% end %>:
|
|
17
17
|
</td>
|
|
18
|
-
<td><input type="text" name="<%= key %>" value="<%= value['default'] || value[:default] %>"></td>
|
|
18
|
+
<td><input type="text" name="<%= h key %>" value="<%= h(value['default'] || value[:default]) %>"></td>
|
|
19
19
|
<% end %>
|
|
20
20
|
</table>
|
|
21
|
-
<input type="hidden" name="job_name" value="<%= @job_name %>">
|
|
21
|
+
<input type="hidden" name="job_name" value="<%= h @job_name %>">
|
|
22
22
|
<input type="submit" value="Queue now">
|
|
23
23
|
</form>
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
The list below contains all scheduled jobs. Click "Queue now" to queue
|
|
5
5
|
a job immediately.
|
|
6
6
|
<br/> Server local time: <%= Time.now %>
|
|
7
|
-
<br/> Server Environment: <%= Resque::Scheduler.env %>
|
|
8
|
-
<br/> Current master: <%= Resque.redis.get(Resque::Scheduler.master_lock.key) %>
|
|
7
|
+
<br/> Server Environment: <%= h Resque::Scheduler.env %>
|
|
8
|
+
<br/> Current master: <%= h 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.
|
|
@@ -22,13 +22,13 @@
|
|
|
22
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
|
-
<input type="hidden" name="klass" value="<%= job['class'] %>">
|
|
25
|
+
<input type="hidden" name="klass" value="<%= h job['class'] %>">
|
|
26
26
|
<input type="hidden" name="args" value="<%= h(Resque.encode job['args']) %>">
|
|
27
27
|
<input type="submit" value="Cancel Job">
|
|
28
28
|
</form>
|
|
29
29
|
</td>
|
|
30
30
|
<td class='args'><%= format_time(Time.at(job['timestamp'])) %></td>
|
|
31
|
-
<td class='class'><%= job['class'] %></td>
|
|
31
|
+
<td class='class'><%= h job['class'] %></td>
|
|
32
32
|
<td class='args'><%= h job['args'].inspect %></td>
|
|
33
33
|
</tr>
|
|
34
34
|
<% end %>
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
</tr>
|
|
45
45
|
<% queued.each do |job| %>
|
|
46
46
|
<tr>
|
|
47
|
-
<td class='class'><%= job['queue'] %></td>
|
|
48
|
-
<td class='class'><%= job['class'] %></td>
|
|
47
|
+
<td class='class'><%= h job['queue'] %></td>
|
|
48
|
+
<td class='class'><%= h job['class'] %></td>
|
|
49
49
|
<td class='args'><%= h job['args'].inspect %></td>
|
|
50
50
|
</tr>
|
|
51
51
|
<% end %>
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
</tr>
|
|
62
62
|
<% working.each do |job| %>
|
|
63
63
|
<tr>
|
|
64
|
-
<td class='class'><%= job['queue'] %></td>
|
|
65
|
-
<td class='class'><%= job['class'] %></td>
|
|
64
|
+
<td class='class'><%= h job['queue'] %></td>
|
|
65
|
+
<td class='class'><%= h job['class'] %></td>
|
|
66
66
|
<td class='args'><%= h job['args'].inspect %></td>
|
|
67
67
|
</tr>
|
|
68
68
|
<% end %>
|
data/lib/resque/scheduler.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# vim:fileencoding=utf-8
|
|
2
2
|
|
|
3
|
-
require 'redis
|
|
3
|
+
require 'redis'
|
|
4
4
|
require 'rufus/scheduler'
|
|
5
5
|
require_relative 'scheduler/configuration'
|
|
6
6
|
require_relative 'scheduler/locking'
|
|
@@ -49,6 +49,7 @@ module Resque
|
|
|
49
49
|
$stderr.sync = true
|
|
50
50
|
|
|
51
51
|
was_master = nil
|
|
52
|
+
schedule_loaded = false
|
|
52
53
|
|
|
53
54
|
begin
|
|
54
55
|
@th = Thread.current
|
|
@@ -61,8 +62,14 @@ module Resque
|
|
|
61
62
|
if am_master != was_master
|
|
62
63
|
procline am_master ? 'Master scheduler' : 'Child scheduler'
|
|
63
64
|
|
|
64
|
-
#
|
|
65
|
-
|
|
65
|
+
# The first pass always loads the schedule - load_schedule!
|
|
66
|
+
# reads am_master, so it cannot run before the check above.
|
|
67
|
+
# After that, only a dynamic schedule can have changed while
|
|
68
|
+
# we were not master.
|
|
69
|
+
if dynamic || !schedule_loaded
|
|
70
|
+
reload_schedule!
|
|
71
|
+
schedule_loaded = true
|
|
72
|
+
end
|
|
66
73
|
end
|
|
67
74
|
|
|
68
75
|
if am_master
|
|
@@ -148,8 +155,13 @@ module Resque
|
|
|
148
155
|
args = optionizate_interval_value(config[interval_type])
|
|
149
156
|
args = [args, nil, job: true] if args.is_a?(::String)
|
|
150
157
|
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
begin
|
|
159
|
+
job = rufus_scheduler.send(interval_type, *args) do
|
|
160
|
+
enqueue_recurring(name, config)
|
|
161
|
+
end
|
|
162
|
+
rescue ArgumentError => e
|
|
163
|
+
log_error "Invalid #{interval_type} schedule for #{name}: #{e.message} - skipping"
|
|
164
|
+
return
|
|
153
165
|
end
|
|
154
166
|
@scheduled_jobs[name] = job
|
|
155
167
|
interval_defined = true
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: resque-scheduler
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.0.
|
|
4
|
+
version: 5.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ben VandenBos
|
|
@@ -10,10 +10,9 @@ authors:
|
|
|
10
10
|
- Dan Buch
|
|
11
11
|
- Michael Bianco
|
|
12
12
|
- Patrick Tulskie
|
|
13
|
-
autorequire:
|
|
14
13
|
bindir: exe
|
|
15
14
|
cert_chain: []
|
|
16
|
-
date:
|
|
15
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
17
16
|
dependencies:
|
|
18
17
|
- !ruby/object:Gem::Dependency
|
|
19
18
|
name: bundler
|
|
@@ -322,7 +321,6 @@ files:
|
|
|
322
321
|
- ".devcontainer/devcontainer.json"
|
|
323
322
|
- ".devcontainer/docker-compose.yml"
|
|
324
323
|
- ".github/dependabot.yml"
|
|
325
|
-
- ".github/funding.yml"
|
|
326
324
|
- ".github/workflows/codeql-analysis.yml"
|
|
327
325
|
- ".github/workflows/rubocop.yml"
|
|
328
326
|
- ".github/workflows/ruby.yml"
|
|
@@ -369,7 +367,6 @@ licenses:
|
|
|
369
367
|
- MIT
|
|
370
368
|
metadata:
|
|
371
369
|
rubygems_mfa_required: 'true'
|
|
372
|
-
post_install_message:
|
|
373
370
|
rdoc_options: []
|
|
374
371
|
require_paths:
|
|
375
372
|
- lib
|
|
@@ -384,8 +381,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
384
381
|
- !ruby/object:Gem::Version
|
|
385
382
|
version: '0'
|
|
386
383
|
requirements: []
|
|
387
|
-
rubygems_version:
|
|
388
|
-
signing_key:
|
|
384
|
+
rubygems_version: 4.0.16
|
|
389
385
|
specification_version: 4
|
|
390
386
|
summary: Light weight job scheduling on top of Resque
|
|
391
387
|
test_files: []
|
data/.github/funding.yml
DELETED