sidekiq 3.0.2 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sidekiq might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/Changes.md +23 -0
- data/Pro-Changes.md +8 -0
- data/lib/sidekiq/api.rb +77 -13
- data/lib/sidekiq/cli.rb +14 -17
- data/lib/sidekiq/logging.rb +1 -1
- data/lib/sidekiq/manager.rb +4 -1
- data/lib/sidekiq/processor.rb +0 -3
- data/lib/sidekiq/rails.rb +15 -0
- data/lib/sidekiq/util.rb +11 -0
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web.rb +39 -42
- data/sidekiq.gemspec +2 -2
- data/test/helper.rb +1 -1
- data/test/test_api.rb +24 -4
- data/test/test_cli.rb +16 -16
- data/test/test_client.rb +1 -1
- data/test/test_fetch.rb +4 -0
- data/test/test_manager.rb +4 -3
- data/test/test_retry.rb +4 -0
- data/test/test_scheduling.rb +1 -0
- data/test/test_sidekiq.rb +2 -0
- data/test/test_web.rb +52 -15
- data/web/assets/javascripts/dashboard.js +134 -21
- data/web/assets/stylesheets/application.css +78 -310
- data/web/locales/en.yml +5 -0
- data/web/views/_job_info.erb +4 -4
- data/web/views/_poll.erb +1 -1
- data/web/views/busy.erb +27 -6
- data/web/views/dashboard.erb +19 -11
- data/web/views/morgue.erb +3 -3
- data/web/views/queue.erb +8 -7
- data/web/views/retries.erb +3 -3
- data/web/views/scheduled.erb +3 -3
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c396adca6a73c134f306d045e66ee17a6f462f5
|
4
|
+
data.tar.gz: aa1be6ef05116a539c57390f4ca6f23dd035cd74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e5b673786330ff3a01c5f5577d194fb34feba04a579853036ba41ef6e612440745cf340a7f279da7bb82e1712466ee864d6688dca7c23e79631e94d520acdcd4
|
7
|
+
data.tar.gz: 33c32fbea3ea8f04a85c1134f2181c4549d630631dd1894dfb7e04ae9d872fbbcfc0ce22d79051e9ea2e7a348060eefd7cf1c6be560f8d06bb2293c79b6474f8
|
data/.travis.yml
CHANGED
data/Changes.md
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
3.1.0
|
2
|
+
-----------
|
3
|
+
|
4
|
+
- New **remote control** feature: you can remotely trigger Sidekiq to quiet
|
5
|
+
or terminate via API, without signals. This is most useful on JRuby
|
6
|
+
or Heroku which does not support the USR1 'quiet' signal. Now you can
|
7
|
+
run a rake task like this at the start of your deploy to quiet your
|
8
|
+
set of Sidekiq processes. [#1703]
|
9
|
+
```ruby
|
10
|
+
namespace :sidekiq do
|
11
|
+
task :quiet => :environment do
|
12
|
+
Sidekiq::ProcessSet.new.each(&:quiet!)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
```
|
16
|
+
- The Web UI can use the API to quiet or stop all processes via the Busy page.
|
17
|
+
- The Web UI understands and hides the `Sidekiq::Extensions::Delay*`
|
18
|
+
classes, instead showing `Class.method` as the Job. [#1718]
|
19
|
+
- The poll interval is now configurable in the Web UI [madebydna, #1713]
|
20
|
+
- Delay extensions can be removed so they don't conflict with
|
21
|
+
DelayedJob: put `Sidekiq.remove_delay!` in your initializer. [devaroop, #1674]
|
22
|
+
|
23
|
+
|
1
24
|
3.0.2
|
2
25
|
-----------
|
3
26
|
|
data/Pro-Changes.md
CHANGED
@@ -3,6 +3,14 @@ Sidekiq Pro Changelog
|
|
3
3
|
|
4
4
|
Please see [http://sidekiq.org/pro](http://sidekiq.org/pro) for more details and how to buy.
|
5
5
|
|
6
|
+
1.7.1
|
7
|
+
-----------
|
8
|
+
|
9
|
+
- Fix for paused queues being processed for a few seconds when starting
|
10
|
+
a new Sidekiq process.
|
11
|
+
- Add a 5 sec delay when starting reliable fetch on Heroku to minimize
|
12
|
+
any duplicate job processing with another process shutting down.
|
13
|
+
|
6
14
|
1.7.0
|
7
15
|
-----------
|
8
16
|
|
data/lib/sidekiq/api.rb
CHANGED
@@ -189,11 +189,12 @@ module Sidekiq
|
|
189
189
|
# removed from the queue via Job#delete.
|
190
190
|
#
|
191
191
|
class Job
|
192
|
+
KNOWN_WRAPPERS = [/\ASidekiq::Extensions::Delayed/, "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"]
|
192
193
|
attr_reader :item
|
193
194
|
|
194
195
|
def initialize(item, queue_name=nil)
|
195
196
|
@value = item
|
196
|
-
@item = Sidekiq.load_json(item)
|
197
|
+
@item = item.is_a?(Hash) ? item : Sidekiq.load_json(item)
|
197
198
|
@queue = queue_name || @item['queue']
|
198
199
|
end
|
199
200
|
|
@@ -201,6 +202,32 @@ module Sidekiq
|
|
201
202
|
@item['class']
|
202
203
|
end
|
203
204
|
|
205
|
+
def display_class
|
206
|
+
# Unwrap known wrappers so they show up in a human-friendly manner in the Web UI
|
207
|
+
@klass ||= case klass
|
208
|
+
when /\ASidekiq::Extensions::Delayed/
|
209
|
+
(target, method, _) = YAML.load(args[0])
|
210
|
+
"#{target}.#{method}"
|
211
|
+
when "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
|
212
|
+
args[0]
|
213
|
+
else
|
214
|
+
klass
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def display_args
|
219
|
+
# Unwrap known wrappers so they show up in a human-friendly manner in the Web UI
|
220
|
+
@args ||= case klass
|
221
|
+
when /\ASidekiq::Extensions::Delayed/
|
222
|
+
(_, _, arg) = YAML.load(args[0])
|
223
|
+
arg
|
224
|
+
when "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
|
225
|
+
args[1..-1]
|
226
|
+
else
|
227
|
+
args
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
204
231
|
def args
|
205
232
|
@item['args']
|
206
233
|
end
|
@@ -449,18 +476,8 @@ module Sidekiq
|
|
449
476
|
# right now. Each process send a heartbeat to Redis every 5 seconds
|
450
477
|
# so this set should be relatively accurate, barring network partitions.
|
451
478
|
#
|
452
|
-
# Yields a
|
479
|
+
# Yields a Sidekiq::Process.
|
453
480
|
#
|
454
|
-
# {
|
455
|
-
# 'hostname' => 'app-1.example.com',
|
456
|
-
# 'started_at' => <process start time>,
|
457
|
-
# 'pid' => 12345,
|
458
|
-
# 'tag' => 'myapp'
|
459
|
-
# 'concurrency' => 25,
|
460
|
-
# 'queues' => ['default', 'low'],
|
461
|
-
# 'busy' => 10,
|
462
|
-
# 'beat' => <last heartbeat>,
|
463
|
-
# }
|
464
481
|
|
465
482
|
class ProcessSet
|
466
483
|
include Enumerable
|
@@ -486,7 +503,7 @@ module Sidekiq
|
|
486
503
|
# in to Redis and probably died.
|
487
504
|
(to_prune << sorted[i]; next) if info.nil?
|
488
505
|
hash = Sidekiq.load_json(info)
|
489
|
-
yield hash.merge('busy' => busy.to_i, 'beat' => at_s.to_f)
|
506
|
+
yield Process.new(hash.merge('busy' => busy.to_i, 'beat' => at_s.to_f))
|
490
507
|
end
|
491
508
|
end
|
492
509
|
|
@@ -503,6 +520,53 @@ module Sidekiq
|
|
503
520
|
end
|
504
521
|
end
|
505
522
|
|
523
|
+
#
|
524
|
+
# Sidekiq::Process has a set of attributes which look like this:
|
525
|
+
#
|
526
|
+
# {
|
527
|
+
# 'hostname' => 'app-1.example.com',
|
528
|
+
# 'started_at' => <process start time>,
|
529
|
+
# 'pid' => 12345,
|
530
|
+
# 'tag' => 'myapp'
|
531
|
+
# 'concurrency' => 25,
|
532
|
+
# 'queues' => ['default', 'low'],
|
533
|
+
# 'busy' => 10,
|
534
|
+
# 'beat' => <last heartbeat>,
|
535
|
+
# }
|
536
|
+
class Process
|
537
|
+
def initialize(hash)
|
538
|
+
@attribs = hash
|
539
|
+
end
|
540
|
+
|
541
|
+
def [](key)
|
542
|
+
@attribs[key]
|
543
|
+
end
|
544
|
+
|
545
|
+
def quiet!
|
546
|
+
signal('USR1')
|
547
|
+
end
|
548
|
+
|
549
|
+
def stop!
|
550
|
+
signal('TERM')
|
551
|
+
end
|
552
|
+
|
553
|
+
private
|
554
|
+
|
555
|
+
def signal(sig)
|
556
|
+
key = "#{identity}-signals"
|
557
|
+
Sidekiq.redis do |c|
|
558
|
+
c.multi do
|
559
|
+
c.lpush(key, sig)
|
560
|
+
c.expire(key, 60)
|
561
|
+
end
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
def identity
|
566
|
+
@id ||= "#{self['hostname']}:#{self['pid']}"
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
506
570
|
##
|
507
571
|
# Programmatic access to the current active worker set.
|
508
572
|
#
|
data/lib/sidekiq/cli.rb
CHANGED
@@ -20,7 +20,7 @@ module Sidekiq
|
|
20
20
|
|
21
21
|
class CLI
|
22
22
|
include Util
|
23
|
-
include Singleton
|
23
|
+
include Singleton unless $TESTING
|
24
24
|
|
25
25
|
# Used for CLI testing
|
26
26
|
attr_accessor :code
|
@@ -40,16 +40,14 @@ module Sidekiq
|
|
40
40
|
daemonize
|
41
41
|
write_pid
|
42
42
|
load_celluloid
|
43
|
-
|
43
|
+
print_banner
|
44
44
|
end
|
45
45
|
|
46
|
+
# Code within this method is not tested because it alters
|
47
|
+
# global process state irreversibly. PRs which improve the
|
48
|
+
# test coverage of Sidekiq::CLI are welcomed.
|
46
49
|
def run
|
47
|
-
|
48
|
-
if environment == 'development' && $stdout.tty?
|
49
|
-
puts "\e[#{31}m"
|
50
|
-
puts Sidekiq::BANNER
|
51
|
-
puts "\e[0m"
|
52
|
-
end
|
50
|
+
boot_system
|
53
51
|
|
54
52
|
self_read, self_write = IO.pipe
|
55
53
|
|
@@ -95,13 +93,12 @@ module Sidekiq
|
|
95
93
|
|
96
94
|
private
|
97
95
|
|
98
|
-
def
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end
|
96
|
+
def print_banner
|
97
|
+
# Print logo and banner for development
|
98
|
+
if environment == 'development' && $stdout.tty?
|
99
|
+
puts "\e[#{31}m"
|
100
|
+
puts Sidekiq::BANNER
|
101
|
+
puts "\e[0m"
|
105
102
|
end
|
106
103
|
end
|
107
104
|
|
@@ -158,7 +155,7 @@ module Sidekiq
|
|
158
155
|
files_to_reopen << file unless file.closed?
|
159
156
|
end
|
160
157
|
|
161
|
-
Process.daemon(true, true)
|
158
|
+
::Process.daemon(true, true)
|
162
159
|
|
163
160
|
files_to_reopen.each do |file|
|
164
161
|
begin
|
@@ -323,7 +320,7 @@ module Sidekiq
|
|
323
320
|
if path = options[:pidfile]
|
324
321
|
pidfile = File.expand_path(path)
|
325
322
|
File.open(pidfile, 'w') do |f|
|
326
|
-
f.puts Process.pid
|
323
|
+
f.puts ::Process.pid
|
327
324
|
end
|
328
325
|
end
|
329
326
|
end
|
data/lib/sidekiq/logging.rb
CHANGED
@@ -7,7 +7,7 @@ module Sidekiq
|
|
7
7
|
class Pretty < Logger::Formatter
|
8
8
|
# Provide a call() method that returns the formatted message.
|
9
9
|
def call(severity, time, program_name, message)
|
10
|
-
"#{time.utc.iso8601} #{Process.pid} TID-#{Thread.current.object_id.to_s(36)}#{context} #{severity}: #{message}\n"
|
10
|
+
"#{time.utc.iso8601} #{::Process.pid} TID-#{Thread.current.object_id.to_s(36)}#{context} #{severity}: #{message}\n"
|
11
11
|
end
|
12
12
|
|
13
13
|
def context
|
data/lib/sidekiq/manager.rb
CHANGED
@@ -149,12 +149,15 @@ module Sidekiq
|
|
149
149
|
|
150
150
|
def ❤(key)
|
151
151
|
begin
|
152
|
-
Sidekiq.redis do |conn|
|
152
|
+
_, _, msg = Sidekiq.redis do |conn|
|
153
153
|
conn.multi do
|
154
154
|
conn.hmset(key, 'busy', @busy.size, 'beat', Time.now.to_f)
|
155
155
|
conn.expire(key, 60)
|
156
|
+
conn.rpop("#{key}-signals")
|
156
157
|
end
|
157
158
|
end
|
159
|
+
|
160
|
+
::Process.kill(msg, $$) if msg
|
158
161
|
rescue => e
|
159
162
|
# ignore all redis/network issues
|
160
163
|
logger.error("heartbeat: #{e.message}")
|
data/lib/sidekiq/processor.rb
CHANGED
@@ -119,9 +119,6 @@ module Sidekiq
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
-
# Singleton classes are not clonable.
|
123
|
-
SINGLETON_CLASSES = [ NilClass, TrueClass, FalseClass, Symbol, Fixnum, Float, Bignum ].freeze
|
124
|
-
|
125
122
|
# Deep clone the arguments passed to the worker so that if
|
126
123
|
# the message fails, what is pushed back onto Redis hasn't
|
127
124
|
# been mutated by the worker.
|
data/lib/sidekiq/rails.rb
CHANGED
@@ -9,6 +9,21 @@ module Sidekiq
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
+
# Removes the generic aliases which MAY clash with names of already
|
13
|
+
# created methods by other applications. The methods `sidekiq_delay`,
|
14
|
+
# `sidekiq_delay_for` and `sidekiq_delay_until` can be used instead.
|
15
|
+
def self.remove_delay!
|
16
|
+
[Extensions::ActiveRecord,
|
17
|
+
Extensions::ActionMailer,
|
18
|
+
Extensions::Klass].each do |mod|
|
19
|
+
mod.module_eval do
|
20
|
+
remove_method :delay if respond_to?(:delay)
|
21
|
+
remove_method :delay_for if respond_to?(:delay_for)
|
22
|
+
remove_method :delay_until if respond_to?(:delay_until)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
12
27
|
class Rails < ::Rails::Engine
|
13
28
|
initializer 'sidekiq' do
|
14
29
|
Sidekiq.hook_rails!
|
data/lib/sidekiq/util.rb
CHANGED
@@ -33,5 +33,16 @@ module Sidekiq
|
|
33
33
|
def identity
|
34
34
|
@@identity ||= "#{hostname}:#{$$}"
|
35
35
|
end
|
36
|
+
|
37
|
+
def fire_event(event)
|
38
|
+
Sidekiq.options[:lifecycle_events][event].each do |block|
|
39
|
+
begin
|
40
|
+
block.call
|
41
|
+
rescue => ex
|
42
|
+
handle_exception(ex, { :event => event })
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
36
47
|
end
|
37
48
|
end
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web.rb
CHANGED
@@ -42,6 +42,20 @@ module Sidekiq
|
|
42
42
|
erb :busy
|
43
43
|
end
|
44
44
|
|
45
|
+
post "/busy" do
|
46
|
+
if params['hostname']
|
47
|
+
pro = Sidekiq::Process.new('hostname' => params["hostname"], 'pid' => params['pid'])
|
48
|
+
pro.quiet! if params[:quiet]
|
49
|
+
pro.stop! if params[:stop]
|
50
|
+
else
|
51
|
+
Sidekiq::ProcessSet.new.each do |pro|
|
52
|
+
pro.quiet! if params[:quiet]
|
53
|
+
pro.stop! if params[:stop]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
redirect "#{root_path}busy"
|
57
|
+
end
|
58
|
+
|
45
59
|
get "/queues" do
|
46
60
|
@queues = Sidekiq::Queue.all
|
47
61
|
erb :queues
|
@@ -53,7 +67,7 @@ module Sidekiq
|
|
53
67
|
@name = params[:name]
|
54
68
|
@queue = Sidekiq::Queue.new(@name)
|
55
69
|
(@current_page, @total_size, @messages) = page("queue:#{@name}", params[:page], @count)
|
56
|
-
@messages = @messages.map {|msg| Sidekiq.
|
70
|
+
@messages = @messages.map {|msg| Sidekiq::Job.new(msg, @name) }
|
57
71
|
erb :queue
|
58
72
|
end
|
59
73
|
|
@@ -86,12 +100,7 @@ module Sidekiq
|
|
86
100
|
|
87
101
|
params['key'].each do |key|
|
88
102
|
job = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
|
89
|
-
|
90
|
-
if params['retry']
|
91
|
-
job.retry
|
92
|
-
elsif params['delete']
|
93
|
-
job.delete
|
94
|
-
end
|
103
|
+
retry_or_delete job, params if job
|
95
104
|
end
|
96
105
|
redirect_with_query("#{root_path}morgue")
|
97
106
|
end
|
@@ -109,13 +118,7 @@ module Sidekiq
|
|
109
118
|
post "/morgue/:key" do
|
110
119
|
halt 404 unless params['key']
|
111
120
|
job = Sidekiq::DeadSet.new.fetch(*parse_params(params['key'])).first
|
112
|
-
if job
|
113
|
-
if params['retry']
|
114
|
-
job.retry
|
115
|
-
elsif params['delete']
|
116
|
-
job.delete
|
117
|
-
end
|
118
|
-
end
|
121
|
+
retry_or_delete job, params if job
|
119
122
|
redirect_with_query("#{root_path}morgue")
|
120
123
|
end
|
121
124
|
|
@@ -139,12 +142,7 @@ module Sidekiq
|
|
139
142
|
|
140
143
|
params['key'].each do |key|
|
141
144
|
job = Sidekiq::RetrySet.new.fetch(*parse_params(key)).first
|
142
|
-
|
143
|
-
if params['retry']
|
144
|
-
job.retry
|
145
|
-
elsif params['delete']
|
146
|
-
job.delete
|
147
|
-
end
|
145
|
+
retry_or_delete job, params if job
|
148
146
|
end
|
149
147
|
redirect_with_query("#{root_path}retries")
|
150
148
|
end
|
@@ -162,13 +160,7 @@ module Sidekiq
|
|
162
160
|
post "/retries/:key" do
|
163
161
|
halt 404 unless params['key']
|
164
162
|
job = Sidekiq::RetrySet.new.fetch(*parse_params(params['key'])).first
|
165
|
-
if job
|
166
|
-
if params['retry']
|
167
|
-
job.retry
|
168
|
-
elsif params['delete']
|
169
|
-
job.delete
|
170
|
-
end
|
171
|
-
end
|
163
|
+
retry_or_delete job, params if job
|
172
164
|
redirect_with_query("#{root_path}retries")
|
173
165
|
end
|
174
166
|
|
@@ -191,13 +183,7 @@ module Sidekiq
|
|
191
183
|
|
192
184
|
params['key'].each do |key|
|
193
185
|
job = Sidekiq::ScheduledSet.new.fetch(*parse_params(key)).first
|
194
|
-
if job
|
195
|
-
if params['delete']
|
196
|
-
job.delete
|
197
|
-
elsif params['add_to_queue']
|
198
|
-
job.add_to_queue
|
199
|
-
end
|
200
|
-
end
|
186
|
+
delete_or_add_queue job, params if job
|
201
187
|
end
|
202
188
|
redirect_with_query("#{root_path}scheduled")
|
203
189
|
end
|
@@ -205,13 +191,7 @@ module Sidekiq
|
|
205
191
|
post "/scheduled/:key" do
|
206
192
|
halt 404 unless params['key']
|
207
193
|
job = Sidekiq::ScheduledSet.new.fetch(*parse_params(params['key'])).first
|
208
|
-
if job
|
209
|
-
if params['add_to_queue']
|
210
|
-
job.add_to_queue
|
211
|
-
elsif params['delete']
|
212
|
-
job.delete
|
213
|
-
end
|
214
|
-
end
|
194
|
+
delete_or_add_queue job, params if job
|
215
195
|
redirect_with_query("#{root_path}scheduled")
|
216
196
|
end
|
217
197
|
|
@@ -223,7 +203,7 @@ module Sidekiq
|
|
223
203
|
erb :dashboard
|
224
204
|
end
|
225
205
|
|
226
|
-
REDIS_KEYS = %w(
|
206
|
+
REDIS_KEYS = %w(redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human)
|
227
207
|
|
228
208
|
get '/dashboard/stats' do
|
229
209
|
sidekiq_stats = Sidekiq::Stats.new
|
@@ -245,5 +225,22 @@ module Sidekiq
|
|
245
225
|
})
|
246
226
|
end
|
247
227
|
|
228
|
+
private
|
229
|
+
|
230
|
+
def retry_or_delete job, params
|
231
|
+
if params['retry']
|
232
|
+
job.retry
|
233
|
+
elsif params['delete']
|
234
|
+
job.delete
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def delete_or_add_queue job, params
|
239
|
+
if params['delete']
|
240
|
+
job.delete
|
241
|
+
elsif params['add_to_queue']
|
242
|
+
job.add_to_queue
|
243
|
+
end
|
244
|
+
end
|
248
245
|
end
|
249
246
|
end
|