sidekiq-scheduler 3.1.0 → 3.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +21 -0
- data/lib/sidekiq-scheduler/extensions/web.rb +10 -1
- data/lib/sidekiq-scheduler/manager.rb +0 -4
- data/lib/sidekiq-scheduler/redis_manager.rb +7 -6
- data/lib/sidekiq-scheduler/schedule.rb +3 -1
- data/lib/sidekiq-scheduler/scheduler.rb +1 -12
- data/lib/sidekiq-scheduler/utils.rb +11 -0
- data/lib/sidekiq-scheduler/version.rb +1 -1
- data/lib/sidekiq-scheduler/web.rb +2 -2
- data/web/assets/stylesheets/recurring_jobs.css +27 -0
- data/web/views/recurring_jobs.erb +70 -44
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f30ce85a9aba1cfa5987dbbd3ec24715b4d6adeadd3e36079be8425d0d673b7
|
4
|
+
data.tar.gz: 92b27fba4b1405a4466ff180ebd60fc981e07cbc15d2497a63af9ae8d1c2ab2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 211ec1b0efa783fec645f9afb598225456a9d2e61a17318e95eb9c3eec71cfb061606829667e51e49badb9b23aca46d905e500eda4d09704af959749eba5b651
|
7
|
+
data.tar.gz: d832b58c8b2526774dd7b54ec7295c89da87b000b105cca465631c628941a7b21e9f864f29c2360d2203f7463412d0c7eab2ee2b7d69f566598a46f56ca94428
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# 3.2.1
|
2
|
+
- Fix CSS not loading on Rails app when Sidekiq < 6 https://github.com/moove-it/sidekiq-scheduler/pull/377
|
3
|
+
# 3.2.0
|
4
|
+
|
5
|
+
- Fix deprecated uses of Redis#pipelined https://github.com/moove-it/sidekiq-scheduler/pull/357
|
6
|
+
- Prevent sidekiq_options from overriding ActiveJob queue settings https://github.com/moove-it/sidekiq-scheduler/pull/367
|
7
|
+
- Highlight disabled jobs https://github.com/moove-it/sidekiq-scheduler/pull/369
|
data/README.md
CHANGED
@@ -179,6 +179,14 @@ Cron, every, and interval types push jobs into sidekiq in a recurrent manner.
|
|
179
179
|
every: '45m' # Runs every 45 minutes
|
180
180
|
```
|
181
181
|
|
182
|
+
The value is parsed by [`Fugit::Duration.parse`](https://github.com/floraison/fugit#fugitduration). It understands quite a number of formats, including human-readable ones:
|
183
|
+
|
184
|
+
``` yaml
|
185
|
+
every: 45 minutes
|
186
|
+
every: 2 hours and 30 minutes
|
187
|
+
every: 1.5 hours
|
188
|
+
```
|
189
|
+
|
182
190
|
`interval` is similar to `every`, the difference between them is that `interval` type schedules the
|
183
191
|
next execution after the interval has elapsed counting from its last job enqueue.
|
184
192
|
|
@@ -313,6 +321,19 @@ Non-normal conditions that could push a specific job multiple times are:
|
|
313
321
|
|
314
322
|
`every`, `interval` and `in` jobs will be pushed once per host.
|
315
323
|
|
324
|
+
## Notes on when sidekiq worker is down
|
325
|
+
|
326
|
+
For a `cron`/`at` (and all other) job to be successfully enqueued, you need at least one sidekiq worker with scheduler to be up at that moment. Handling this is up to you and depends on your application.
|
327
|
+
|
328
|
+
Possible solutions include:
|
329
|
+
- Simply ignoring this fact, if you only run frequent periodic jobs, that can tolerate some increased interval
|
330
|
+
- Abstaining from deploys/restarts during time when critical jobs are usually scheduled
|
331
|
+
- Making your infrequent jobs idempotent (so that they can be enqueued multiple times but still produce result as if was run once) and scheduling them multiple times to reduce likelihood of not being run
|
332
|
+
- Zero downtime deploy for sidekiq workers: keep at least one worker up during whole deploy and only restart/shut it down after when new one has started
|
333
|
+
- Running scheduler inside your unicorn/rails processes (if you already have zero downtime deploy set up for these)
|
334
|
+
|
335
|
+
Each option has it's own pros and cons.
|
336
|
+
|
316
337
|
## Sidekiq Web Integration
|
317
338
|
|
318
339
|
sidekiq-scheduler provides an extension to the Sidekiq web interface that adds a `Recurring Jobs` page.
|
@@ -1,5 +1,14 @@
|
|
1
1
|
require 'sidekiq/web' unless defined?(Sidekiq::Web)
|
2
2
|
|
3
|
+
ASSETS_PATH = File.expand_path('../../../web/assets', __dir__)
|
4
|
+
|
3
5
|
Sidekiq::Web.register(SidekiqScheduler::Web)
|
4
6
|
Sidekiq::Web.tabs['recurring_jobs'] = 'recurring-jobs'
|
5
|
-
Sidekiq::Web.locales << File.expand_path(File.dirname(__FILE__)
|
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'],
|
11
|
+
root: ASSETS_PATH,
|
12
|
+
cascade: true,
|
13
|
+
header_rules: [[:all, { 'Cache-Control' => 'public, max-age=86400' }]]
|
14
|
+
end
|
@@ -97,10 +97,11 @@ module SidekiqScheduler
|
|
97
97
|
# @return [Boolean] true if the schedules key is set, false otherwise
|
98
98
|
def self.schedule_exist?
|
99
99
|
Sidekiq.redis do |r|
|
100
|
-
|
101
|
-
|
100
|
+
case r.exists(:schedules)
|
101
|
+
when true, 1
|
102
|
+
true
|
102
103
|
else
|
103
|
-
|
104
|
+
false
|
104
105
|
end
|
105
106
|
end
|
106
107
|
end
|
@@ -136,9 +137,9 @@ module SidekiqScheduler
|
|
136
137
|
def self.register_job_instance(job_name, time)
|
137
138
|
job_key = pushed_job_key(job_name)
|
138
139
|
registered, _ = Sidekiq.redis do |r|
|
139
|
-
r.pipelined do
|
140
|
-
|
141
|
-
|
140
|
+
r.pipelined do |pipeline|
|
141
|
+
pipeline.zadd(job_key, time.to_i, time.to_i)
|
142
|
+
pipeline.expire(job_key, REGISTERED_JOBS_THRESHOLD_IN_SECONDS)
|
142
143
|
end
|
143
144
|
end
|
144
145
|
|
@@ -140,7 +140,9 @@ module SidekiqScheduler
|
|
140
140
|
def infer_queue(klass)
|
141
141
|
klass = try_to_constantize(klass)
|
142
142
|
|
143
|
-
|
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)
|
144
146
|
klass.sidekiq_options['queue']
|
145
147
|
end
|
146
148
|
end
|
@@ -161,7 +161,7 @@ module SidekiqScheduler
|
|
161
161
|
config['args'] = arguments_with_metadata(config['args'], scheduled_at: time.to_f)
|
162
162
|
end
|
163
163
|
|
164
|
-
if active_job_enqueue?(config['class'])
|
164
|
+
if SidekiqScheduler::Utils.active_job_enqueue?(config['class'])
|
165
165
|
SidekiqScheduler::Utils.enqueue_with_active_job(config)
|
166
166
|
else
|
167
167
|
SidekiqScheduler::Utils.enqueue_with_sidekiq(config)
|
@@ -307,17 +307,6 @@ module SidekiqScheduler
|
|
307
307
|
queues.empty? || queues.include?(job_queue)
|
308
308
|
end
|
309
309
|
|
310
|
-
# Returns true if the enqueuing needs to be done for an ActiveJob
|
311
|
-
# class false otherwise.
|
312
|
-
#
|
313
|
-
# @param [Class] klass the class to check is decendant from ActiveJob
|
314
|
-
#
|
315
|
-
# @return [Boolean]
|
316
|
-
def active_job_enqueue?(klass)
|
317
|
-
klass.is_a?(Class) && defined?(ActiveJob::Enqueuing) &&
|
318
|
-
klass.included_modules.include?(ActiveJob::Enqueuing)
|
319
|
-
end
|
320
|
-
|
321
310
|
# Convert the given arguments in the format expected to be enqueued.
|
322
311
|
#
|
323
312
|
# @param [Hash] config the options to be converted
|
@@ -68,6 +68,17 @@ module SidekiqScheduler
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
+
# Returns true if the enqueuing needs to be done for an ActiveJob
|
72
|
+
# class false otherwise.
|
73
|
+
#
|
74
|
+
# @param [Class] klass the class to check is decendant from ActiveJob
|
75
|
+
#
|
76
|
+
# @return [Boolean]
|
77
|
+
def self.active_job_enqueue?(klass)
|
78
|
+
klass.is_a?(Class) && defined?(ActiveJob::Enqueuing) &&
|
79
|
+
klass.included_modules.include?(ActiveJob::Enqueuing)
|
80
|
+
end
|
81
|
+
|
71
82
|
# Enqueues the job using the Sidekiq client.
|
72
83
|
#
|
73
84
|
# @param [Hash] config The job configuration
|
@@ -15,13 +15,13 @@ module SidekiqScheduler
|
|
15
15
|
erb File.read(File.join(VIEW_PATH, 'recurring_jobs.erb'))
|
16
16
|
end
|
17
17
|
|
18
|
-
app.
|
18
|
+
app.post '/recurring-jobs/:name/enqueue' do
|
19
19
|
schedule = Sidekiq.get_schedule(params[:name])
|
20
20
|
SidekiqScheduler::Scheduler.instance.enqueue_job(schedule)
|
21
21
|
redirect "#{root_path}recurring-jobs"
|
22
22
|
end
|
23
23
|
|
24
|
-
app.
|
24
|
+
app.post '/recurring-jobs/:name/toggle' do
|
25
25
|
Sidekiq.reload_schedule!
|
26
26
|
|
27
27
|
SidekiqScheduler::Scheduler.instance.toggle_job_enabled(params[:name])
|
@@ -0,0 +1,27 @@
|
|
1
|
+
.recurring-jobs { border-top-left-radius: 4px; border-top-right-radius: 4px; }
|
2
|
+
.recurring-jobs .title { margin-bottom: 5px; }
|
3
|
+
.recurring-jobs .title .name { font-weight: bold;}
|
4
|
+
.recurring-jobs .info,
|
5
|
+
.recurring-jobs .description { margin-bottom: 5px; }
|
6
|
+
.recurring-jobs .actions { margin-bottom: 5px; }
|
7
|
+
.recurring-jobs .status,
|
8
|
+
.recurring-jobs .description { font-size: 12px; }
|
9
|
+
.recurring-jobs .enqueue { margin-bottom: 0.5rem }
|
10
|
+
|
11
|
+
.list-group-item {
|
12
|
+
background-color: #f3f3f3;
|
13
|
+
color: #585454;
|
14
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
15
|
+
}
|
16
|
+
|
17
|
+
.list-group-item-disabled {
|
18
|
+
background-color: #f3d3d3;
|
19
|
+
}
|
20
|
+
|
21
|
+
@media (prefers-color-scheme: dark) {
|
22
|
+
.list-group-item {
|
23
|
+
background-color: #222;
|
24
|
+
color: white;
|
25
|
+
border: 1px solid #555;
|
26
|
+
}
|
27
|
+
}
|
@@ -1,48 +1,74 @@
|
|
1
|
-
|
1
|
+
<% if Sidekiq::VERSION >= '6.0.0' %>
|
2
|
+
<link href="<%= root_path %>stylesheets/recurring_jobs.css" media="screen" rel="stylesheet" type="text/css" />
|
3
|
+
<% else %>
|
4
|
+
<style>
|
5
|
+
.recurring-jobs { border-top-left-radius: 4px; border-top-right-radius: 4px; }
|
6
|
+
.recurring-jobs .title { margin-bottom: 5px; }
|
7
|
+
.recurring-jobs .title .name { font-weight: bold;}
|
8
|
+
.recurring-jobs .info,
|
9
|
+
.recurring-jobs .description { margin-bottom: 5px; }
|
10
|
+
.recurring-jobs .actions { margin-bottom: 5px; }
|
11
|
+
.recurring-jobs .status,
|
12
|
+
.recurring-jobs .description { font-size: 12px; }
|
13
|
+
.recurring-jobs .enqueue { margin-bottom: 0.5rem }
|
14
|
+
|
15
|
+
.list-group-item {
|
16
|
+
background-color: #f3f3f3;
|
17
|
+
color: #585454;
|
18
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
19
|
+
}
|
2
20
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
<th><%= t('class') %></th>
|
11
|
-
<th><%= t('queue') %></th>
|
12
|
-
<th><%= t('arguments') %></th>
|
13
|
-
<th><%= t('last_time') %></th>
|
14
|
-
<th><%= t('next_time') %></th>
|
15
|
-
<th></th>
|
16
|
-
</tr>
|
17
|
-
</thead>
|
21
|
+
.list-group-item-disabled {
|
22
|
+
background-color: #f3d3d3;
|
23
|
+
}
|
24
|
+
</style>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<h3><%= t('recurring_jobs') %></h3>
|
18
28
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
<
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
<div class="recurring-jobs">
|
30
|
+
<ul class="list-group">
|
31
|
+
<% @presented_jobs.each do |job| %>
|
32
|
+
<li class="list-group-item <%= !job.enabled? && "list-group-item-disabled" %>">
|
33
|
+
<div class="title">
|
34
|
+
<div class="row">
|
35
|
+
<div class="col-xs-6">
|
36
|
+
<span class="name"><%= job.name %></span>
|
37
|
+
</div>
|
38
|
+
<div class="col-xs-6 text-right">
|
39
|
+
<a href="<%= root_path %>queues/<%= job.queue %>"><%= job.queue %></a>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
<div class="description"><%= job['description'] %></div>
|
44
|
+
<div class="info">
|
45
|
+
<div class="row">
|
46
|
+
<div class="col-md-4 class"><%= job['class'] %></div>
|
47
|
+
<div class="col-md-4 interval text-left"><%= t('interval') %>: <%= job.interval %></div>
|
48
|
+
<div class="col-md-4 args"><%= t('arguments') %>: <%= job['args'] %></div>
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
<div class="status row">
|
52
|
+
<div class="col-md-4 actions">
|
53
|
+
<form action="<%= root_path %>recurring-jobs/<%= ERB::Util.url_encode(job.name) %>/enqueue" method="post" class="enqueue">
|
54
|
+
<%= csrf_tag %>
|
55
|
+
<input type="submit" class="btn btn-warn btn-xs" value="<%= t('enqueue_now') %>" />
|
56
|
+
</form>
|
57
|
+
<form action="<%= root_path %>recurring-jobs/<%= ERB::Util.url_encode(job.name) %>/toggle" method="post">
|
58
|
+
<%= csrf_tag %>
|
59
|
+
<input type="submit" class="btn <%= job.enabled? ? "btn-primary" : "btn-warn"%> btn-xs" value="<%= job.enabled? ? t('disable') : t('enable') %>" />
|
60
|
+
</form>
|
61
|
+
</div>
|
62
|
+
<div class="col-md-4">
|
63
|
+
<span class="last_time"><%= t('last_time') %>: <%= job.last_time %></span>
|
64
|
+
</div>
|
65
|
+
<div class="col-md-4">
|
66
|
+
<span class="next_time text-right" style="<%= 'text-decoration:line-through' unless job.enabled? %>">
|
67
|
+
<%= t('next_time') %>: <%= job.next_time || t('no_next_time') %>
|
34
68
|
</span>
|
35
|
-
</
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
<a class="btn <%= job.enabled? ? "btn-primary" : "btn-warn"%> btn-xs" href="<%= root_path %>recurring-jobs/<%= ERB::Util.url_encode(job.name) %>/toggle">
|
41
|
-
<%= job.enabled? ? t('disable') : t('enable') %>
|
42
|
-
</a>
|
43
|
-
</td>
|
44
|
-
</tr>
|
45
|
-
<% end %>
|
46
|
-
</tbody>
|
47
|
-
</table>
|
69
|
+
</div>
|
70
|
+
</div>
|
71
|
+
</li>
|
72
|
+
<% end %>
|
73
|
+
</ul>
|
48
74
|
</div>
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-scheduler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1
|
4
|
+
version: 3.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Morton Jonuschat
|
8
8
|
- Moove-it
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-04-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sidekiq
|
@@ -263,6 +263,7 @@ executables: []
|
|
263
263
|
extensions: []
|
264
264
|
extra_rdoc_files: []
|
265
265
|
files:
|
266
|
+
- CHANGELOG.md
|
266
267
|
- MIT-LICENSE
|
267
268
|
- README.md
|
268
269
|
- Rakefile
|
@@ -279,6 +280,7 @@ files:
|
|
279
280
|
- lib/sidekiq-scheduler/version.rb
|
280
281
|
- lib/sidekiq-scheduler/web.rb
|
281
282
|
- lib/sidekiq/scheduler.rb
|
283
|
+
- web/assets/stylesheets/recurring_jobs.css
|
282
284
|
- web/locales/cs.yml
|
283
285
|
- web/locales/de.yml
|
284
286
|
- web/locales/en.yml
|
@@ -297,7 +299,7 @@ homepage: https://moove-it.github.io/sidekiq-scheduler/
|
|
297
299
|
licenses:
|
298
300
|
- MIT
|
299
301
|
metadata: {}
|
300
|
-
post_install_message:
|
302
|
+
post_install_message:
|
301
303
|
rdoc_options: []
|
302
304
|
require_paths:
|
303
305
|
- lib
|
@@ -312,8 +314,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
312
314
|
- !ruby/object:Gem::Version
|
313
315
|
version: '0'
|
314
316
|
requirements: []
|
315
|
-
rubygems_version: 3.2.
|
316
|
-
signing_key:
|
317
|
+
rubygems_version: 3.2.19
|
318
|
+
signing_key:
|
317
319
|
specification_version: 4
|
318
320
|
summary: Light weight job scheduling extension for Sidekiq
|
319
321
|
test_files: []
|