sidekiq 2.4.0 → 2.5.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.
- data/.gitignore +1 -0
- data/Changes.md +11 -0
- data/Gemfile +6 -0
- data/lib/sidekiq.rb +1 -0
- data/lib/sidekiq/api.rb +184 -0
- data/lib/sidekiq/cli.rb +3 -2
- data/lib/sidekiq/client.rb +16 -10
- data/lib/sidekiq/core_ext.rb +30 -0
- data/lib/sidekiq/exception_handler.rb +9 -0
- data/lib/sidekiq/processor.rb +1 -1
- data/lib/sidekiq/testing/inline.rb +3 -3
- data/lib/sidekiq/util.rb +1 -11
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web.rb +49 -1
- data/lib/sidekiq/worker.rb +3 -9
- data/sidekiq.gemspec +3 -0
- data/test/config.yml +1 -1
- data/test/test_api.rb +75 -0
- data/test/test_cli.rb +6 -1
- data/test/test_exception_handler.rb +16 -0
- data/test/test_testing_inline.rb +3 -3
- data/test/test_web.rb +27 -6
- data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
- data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
- data/web/assets/images/logo.png +0 -0
- data/web/assets/images/status-sd8051fd480.png +0 -0
- data/web/assets/images/status/active.png +0 -0
- data/web/assets/images/status/idle.png +0 -0
- data/web/assets/javascripts/application.js +44 -16
- data/web/assets/javascripts/vendor/bootstrap.js +0 -0
- data/web/assets/javascripts/vendor/jquery.js +0 -0
- data/web/assets/javascripts/vendor/jquery.timeago.js +0 -0
- data/web/assets/stylesheets/application.css +1 -3
- data/web/assets/stylesheets/layout.css +0 -0
- data/web/assets/stylesheets/partials/_base.scss +116 -0
- data/web/assets/stylesheets/partials/_colors.scss +46 -0
- data/web/assets/stylesheets/partials/_fonts.scss +3 -0
- data/web/assets/stylesheets/partials/_h5bp.scss +293 -0
- data/web/assets/stylesheets/partials/_layout.scss +197 -0
- data/web/assets/stylesheets/partials/_navbar.scss +100 -0
- data/web/assets/stylesheets/partials/_normalize.scss +504 -0
- data/web/assets/stylesheets/partials/_prettify.css +30 -0
- data/web/assets/stylesheets/partials/_variables.scss +28 -0
- data/web/assets/stylesheets/public.scss +74 -0
- data/web/assets/stylesheets/style.scss +69 -0
- data/web/assets/stylesheets/vendor/bootstrap-responsive.css +0 -0
- data/web/assets/stylesheets/vendor/bootstrap.css +0 -0
- data/web/assets/stylesheets/vendors.scss +2 -0
- data/web/views/_nav.slim +13 -0
- data/web/views/_paging.slim +0 -0
- data/web/views/_status.slim +3 -0
- data/web/views/_summary.slim +19 -8
- data/web/views/_workers.slim +12 -5
- data/web/views/index.slim +8 -9
- data/web/views/layout.slim +27 -19
- data/web/views/poll.slim +1 -0
- data/web/views/queue.slim +22 -9
- data/web/views/queues.slim +7 -7
- data/web/views/retries.slim +13 -10
- data/web/views/retry.slim +5 -5
- data/web/views/scheduled.slim +13 -10
- metadata +71 -2
data/.gitignore
CHANGED
data/Changes.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
2.5.0
|
2
|
+
-----------
|
3
|
+
|
4
|
+
- REDESIGNED WEB UI! [unity, cavneb]
|
5
|
+
- Support Honeybadger for error delivery
|
6
|
+
- Inline testing runs the client middleware before executing jobs [#465]
|
7
|
+
- Web UI can now remove jobs from queue. [#466, dleung]
|
8
|
+
- Web UI can now show the full message, not just 100 chars [#464, dleung]
|
9
|
+
- Add APIs for manipulating the retry and job queues. See sidekiq/api. [#457]
|
10
|
+
|
11
|
+
|
1
12
|
2.4.0
|
2
13
|
-----------
|
3
14
|
|
data/Gemfile
CHANGED
data/lib/sidekiq.rb
CHANGED
data/lib/sidekiq/api.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'sidekiq'
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
|
5
|
+
##
|
6
|
+
# Encapsulates a queue within Sidekiq.
|
7
|
+
# Allows enumeration of all jobs within the queue
|
8
|
+
# and deletion of jobs.
|
9
|
+
#
|
10
|
+
# queue = Sidekiq::Queue.new("mailer")
|
11
|
+
# queue.each do |job|
|
12
|
+
# job.klass # => 'MyWorker'
|
13
|
+
# job.args # => [1, 2, 3]
|
14
|
+
# job.delete if job.jid == 'abcdef1234567890'
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
class Queue
|
18
|
+
include Enumerable
|
19
|
+
|
20
|
+
attr_reader :name
|
21
|
+
|
22
|
+
def initialize(name="default")
|
23
|
+
@name = name
|
24
|
+
@rname = "queue:#{name}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def size
|
28
|
+
Sidekiq.redis { |con| con.llen(@rname) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def each(&block)
|
32
|
+
page = 0
|
33
|
+
page_size = 50
|
34
|
+
|
35
|
+
loop do
|
36
|
+
entries = Sidekiq.redis do |conn|
|
37
|
+
conn.lrange @rname, page * page_size, (page * page_size) + page_size - 1
|
38
|
+
end
|
39
|
+
break if entries.empty?
|
40
|
+
page += 1
|
41
|
+
entries.each do |entry|
|
42
|
+
block.call Job.new(entry, @name)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Encapsulates a pending job within a Sidekiq queue or
|
50
|
+
# sorted set.
|
51
|
+
#
|
52
|
+
# The job should be considered immutable but may be
|
53
|
+
# removed from the queue via Job#delete.
|
54
|
+
#
|
55
|
+
class Job
|
56
|
+
attr_reader :item
|
57
|
+
|
58
|
+
def initialize(item, queue_name=nil)
|
59
|
+
@value = item
|
60
|
+
@item = Sidekiq.load_json(item)
|
61
|
+
@queue = queue_name || @item['queue']
|
62
|
+
end
|
63
|
+
|
64
|
+
def klass
|
65
|
+
@item['class']
|
66
|
+
end
|
67
|
+
|
68
|
+
def args
|
69
|
+
@item['args']
|
70
|
+
end
|
71
|
+
|
72
|
+
def jid
|
73
|
+
@item['jid']
|
74
|
+
end
|
75
|
+
|
76
|
+
def queue
|
77
|
+
@queue
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Remove this job from the queue.
|
82
|
+
def delete
|
83
|
+
count = Sidekiq.redis do |conn|
|
84
|
+
conn.lrem("queue:#{@queue}", 0, @value)
|
85
|
+
end
|
86
|
+
count != 0
|
87
|
+
end
|
88
|
+
|
89
|
+
def [](name)
|
90
|
+
@item.send(:[], name)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class SortedEntry < Job
|
95
|
+
attr_reader :score
|
96
|
+
|
97
|
+
def initialize(parent, score, item)
|
98
|
+
super(item)
|
99
|
+
@score = score
|
100
|
+
@parent = parent
|
101
|
+
end
|
102
|
+
|
103
|
+
def at
|
104
|
+
Time.at(@score)
|
105
|
+
end
|
106
|
+
|
107
|
+
def delete
|
108
|
+
@parent.delete(@score)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class SortedSet
|
113
|
+
include Enumerable
|
114
|
+
|
115
|
+
def initialize(name)
|
116
|
+
@zset = name
|
117
|
+
end
|
118
|
+
|
119
|
+
def size
|
120
|
+
Sidekiq.redis {|c| c.zcard(@zset) }
|
121
|
+
end
|
122
|
+
|
123
|
+
def each(&block)
|
124
|
+
# page thru the sorted set backwards so deleting entries doesn't screw up indexing
|
125
|
+
page = -1
|
126
|
+
page_size = 50
|
127
|
+
|
128
|
+
loop do
|
129
|
+
elements = Sidekiq.redis do |conn|
|
130
|
+
conn.zrange @zset, page * page_size, (page * page_size) + (page_size - 1), :with_scores => true
|
131
|
+
end
|
132
|
+
break if elements.empty?
|
133
|
+
page -= 1
|
134
|
+
elements.each do |element, score|
|
135
|
+
block.call SortedEntry.new(self, score, element)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def delete(score)
|
141
|
+
count = Sidekiq.redis do |conn|
|
142
|
+
conn.zremrangebyscore(@zset, score, score)
|
143
|
+
end
|
144
|
+
count != 0
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# Allows enumeration of scheduled jobs within Sidekiq.
|
150
|
+
# Based on this, you can search/filter for jobs. Here's an
|
151
|
+
# example where I'm selecting all jobs of a certain type
|
152
|
+
# and deleting them from the retry queue.
|
153
|
+
#
|
154
|
+
# r = Sidekiq::ScheduledSet.new
|
155
|
+
# r.select do |retri|
|
156
|
+
# retri.klass == 'Sidekiq::Extensions::DelayedClass' &&
|
157
|
+
# retri.args[0] == 'User' &&
|
158
|
+
# retri.args[1] == 'setup_new_subscriber'
|
159
|
+
# end.map(&:delete)
|
160
|
+
class ScheduledSet < SortedSet
|
161
|
+
def initialize
|
162
|
+
super 'schedule'
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
##
|
167
|
+
# Allows enumeration of retries within Sidekiq.
|
168
|
+
# Based on this, you can search/filter for jobs. Here's an
|
169
|
+
# example where I'm selecting all jobs of a certain type
|
170
|
+
# and deleting them from the retry queue.
|
171
|
+
#
|
172
|
+
# r = Sidekiq::RetrySet.new
|
173
|
+
# r.select do |retri|
|
174
|
+
# retri.klass == 'Sidekiq::Extensions::DelayedClass' &&
|
175
|
+
# retri.args[0] == 'User' &&
|
176
|
+
# retri.args[1] == 'setup_new_subscriber'
|
177
|
+
# end.map(&:delete)
|
178
|
+
class RetrySet < SortedSet
|
179
|
+
def initialize
|
180
|
+
super 'retry'
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
data/lib/sidekiq/cli.rb
CHANGED
@@ -32,6 +32,7 @@ require 'yaml'
|
|
32
32
|
require 'singleton'
|
33
33
|
require 'optparse'
|
34
34
|
require 'celluloid'
|
35
|
+
require 'erb'
|
35
36
|
|
36
37
|
require 'sidekiq'
|
37
38
|
require 'sidekiq/util'
|
@@ -149,7 +150,7 @@ module Sidekiq
|
|
149
150
|
|
150
151
|
@parser = OptionParser.new do |o|
|
151
152
|
o.on "-q", "--queue QUEUE[,WEIGHT]...", "Queues to process with optional weights" do |arg|
|
152
|
-
queues_and_weights = arg.scan(/([\w
|
153
|
+
queues_and_weights = arg.scan(/([\w\.-]+),?(\d*)/)
|
153
154
|
queues_and_weights.each {|queue_and_weight| parse_queues(opts, *queue_and_weight)}
|
154
155
|
opts[:strict] = queues_and_weights.collect(&:last).none? {|weight| weight != ''}
|
155
156
|
end
|
@@ -208,7 +209,7 @@ module Sidekiq
|
|
208
209
|
def parse_config(cli)
|
209
210
|
opts = {}
|
210
211
|
if cli[:config_file] && File.exist?(cli[:config_file])
|
211
|
-
opts = YAML.
|
212
|
+
opts = YAML.load(ERB.new(IO.read(cli[:config_file])).result)
|
212
213
|
queues = opts.delete(:queues) || []
|
213
214
|
queues.each { |name, weight| parse_queues(opts, name, weight) }
|
214
215
|
end
|
data/lib/sidekiq/client.rb
CHANGED
@@ -40,16 +40,7 @@ module Sidekiq
|
|
40
40
|
normed, payload = process_single(item['class'], normed)
|
41
41
|
|
42
42
|
pushed = false
|
43
|
-
|
44
|
-
if normed['at']
|
45
|
-
pushed = conn.zadd('schedule', normed['at'].to_s, payload)
|
46
|
-
else
|
47
|
-
_, pushed = conn.multi do
|
48
|
-
conn.sadd('queues', normed['queue'])
|
49
|
-
conn.rpush("queue:#{normed['queue']}", payload)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end if normed
|
43
|
+
pushed = raw_push(normed, payload) if normed
|
53
44
|
pushed ? normed['jid'] : nil
|
54
45
|
end
|
55
46
|
|
@@ -104,6 +95,21 @@ module Sidekiq
|
|
104
95
|
|
105
96
|
private
|
106
97
|
|
98
|
+
def self.raw_push(normed, payload) # :nodoc:
|
99
|
+
pushed = false
|
100
|
+
Sidekiq.redis do |conn|
|
101
|
+
if normed['at']
|
102
|
+
pushed = conn.zadd('schedule', normed['at'].to_s, payload)
|
103
|
+
else
|
104
|
+
_, pushed = conn.multi do
|
105
|
+
conn.sadd('queues', normed['queue'])
|
106
|
+
conn.rpush("queue:#{normed['queue']}", payload)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
pushed
|
111
|
+
end
|
112
|
+
|
107
113
|
def self.process_single(worker_class, item)
|
108
114
|
queue = item['queue']
|
109
115
|
|
data/lib/sidekiq/core_ext.rb
CHANGED
@@ -51,4 +51,34 @@ rescue LoadError
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
begin
|
55
|
+
require 'active_support/core_ext/hash/keys'
|
56
|
+
rescue LoadError
|
57
|
+
class Hash
|
58
|
+
def stringify_keys(hash)
|
59
|
+
hash.keys.each do |key|
|
60
|
+
hash[key.to_s] = hash.delete(key)
|
61
|
+
end
|
62
|
+
hash
|
63
|
+
end
|
64
|
+
end if !{}.responds_to(:stringify_keys)
|
65
|
+
end
|
66
|
+
|
67
|
+
begin
|
68
|
+
require 'active_support/core_ext/string/inflections'
|
69
|
+
rescue LoadError
|
70
|
+
class String
|
71
|
+
def constantize
|
72
|
+
names = self.split('::')
|
73
|
+
names.shift if names.empty? || names.first.empty?
|
74
|
+
|
75
|
+
constant = Object
|
76
|
+
names.each do |name|
|
77
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
78
|
+
end
|
79
|
+
constant
|
80
|
+
end
|
81
|
+
end if !"".responds_to(:constantize)
|
82
|
+
end
|
83
|
+
|
54
84
|
|
@@ -5,7 +5,12 @@ module Sidekiq
|
|
5
5
|
Sidekiq.logger.warn msg
|
6
6
|
Sidekiq.logger.warn ex
|
7
7
|
Sidekiq.logger.warn ex.backtrace.join("\n")
|
8
|
+
# This list of services is getting a bit ridiculous.
|
9
|
+
# For future error services, please add your own
|
10
|
+
# middleware like BugSnag does:
|
11
|
+
# https://github.com/bugsnag/bugsnag-ruby/blob/master/lib/bugsnag/sidekiq.rb
|
8
12
|
send_to_airbrake(msg, ex) if defined?(::Airbrake)
|
13
|
+
send_to_honeybadger(msg, ex) if defined?(::Honeybadger)
|
9
14
|
send_to_exceptional(msg, ex) if defined?(::Exceptional)
|
10
15
|
send_to_exception_notifier(msg, ex) if defined?(::ExceptionNotifier)
|
11
16
|
end
|
@@ -16,6 +21,10 @@ module Sidekiq
|
|
16
21
|
::Airbrake.notify_or_ignore(ex, :parameters => msg)
|
17
22
|
end
|
18
23
|
|
24
|
+
def send_to_honeybadger(msg, ex)
|
25
|
+
::Honeybadger.notify_or_ignore(ex, :parameters => msg)
|
26
|
+
end
|
27
|
+
|
19
28
|
def send_to_exceptional(msg, ex)
|
20
29
|
if ::Exceptional::Config.should_send_to_api?
|
21
30
|
::Exceptional.context(msg)
|
data/lib/sidekiq/processor.rb
CHANGED
@@ -27,9 +27,9 @@ module Sidekiq
|
|
27
27
|
# assert_equal 1, $external_variable
|
28
28
|
#
|
29
29
|
singleton_class.class_eval do
|
30
|
-
alias_method :
|
31
|
-
def
|
32
|
-
hash['class'].new.perform(*Sidekiq.load_json(Sidekiq.dump_json(hash['args'])))
|
30
|
+
alias_method :raw_push_old, :raw_push
|
31
|
+
def raw_push(hash, _)
|
32
|
+
hash['class'].constantize.new.perform(*Sidekiq.load_json(Sidekiq.dump_json(hash['args'])))
|
33
33
|
true
|
34
34
|
end
|
35
35
|
end
|
data/lib/sidekiq/util.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'sidekiq/exception_handler'
|
3
|
+
require 'sidekiq/core_ext'
|
3
4
|
|
4
5
|
module Sidekiq
|
5
6
|
##
|
@@ -10,17 +11,6 @@ module Sidekiq
|
|
10
11
|
|
11
12
|
EXPIRY = 60 * 60 * 24
|
12
13
|
|
13
|
-
def constantize(camel_cased_word)
|
14
|
-
names = camel_cased_word.split('::')
|
15
|
-
names.shift if names.empty? || names.first.empty?
|
16
|
-
|
17
|
-
constant = Object
|
18
|
-
names.each do |name|
|
19
|
-
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
20
|
-
end
|
21
|
-
constant
|
22
|
-
end
|
23
|
-
|
24
14
|
def watchdog(last_words)
|
25
15
|
yield
|
26
16
|
rescue Exception => ex
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'sinatra/base'
|
2
2
|
require 'slim'
|
3
3
|
require 'sprockets'
|
4
|
+
require 'sprockets-sass'
|
5
|
+
require 'sass'
|
6
|
+
require 'compass'
|
4
7
|
require 'sidekiq/paginator'
|
5
8
|
|
6
9
|
module Sidekiq
|
@@ -16,6 +19,33 @@ module Sidekiq
|
|
16
19
|
@environment.append_path 'assets/stylesheets'
|
17
20
|
@environment.append_path 'assets/stylesheets/vendor'
|
18
21
|
@environment.append_path 'assets/images'
|
22
|
+
|
23
|
+
Compass.configuration do |config|
|
24
|
+
|
25
|
+
config.project_path = "#{@root}/assets"
|
26
|
+
|
27
|
+
config.images_dir = 'images'
|
28
|
+
config.sass_dir = 'stylesheets'
|
29
|
+
config.css_dir = 'stylesheets'
|
30
|
+
config.javascripts_dir = 'javascripts'
|
31
|
+
config.fonts_dir = 'stylesheets/fonts'
|
32
|
+
|
33
|
+
config.http_images_path = '/assets'
|
34
|
+
config.http_generated_images_path = '/assets'
|
35
|
+
config.http_javascripts_path = '/assets'
|
36
|
+
config.http_stylesheets_path = '/assets'
|
37
|
+
|
38
|
+
# You can select your preferred output style here (can be overridden via the command line):
|
39
|
+
output_style = :compressed
|
40
|
+
|
41
|
+
# To enable relative paths to assets via compass helper functions. Uncomment:
|
42
|
+
relative_assets = true
|
43
|
+
|
44
|
+
# To disable debugging comments that display the original location of your selectors. Uncomment:
|
45
|
+
line_comments = false
|
46
|
+
end
|
47
|
+
|
48
|
+
|
19
49
|
end
|
20
50
|
|
21
51
|
def call(env)
|
@@ -36,8 +66,10 @@ module Sidekiq
|
|
36
66
|
set :views, "#{dir}/views"
|
37
67
|
set :root, "#{dir}/public"
|
38
68
|
set :slim, :pretty => true
|
69
|
+
|
39
70
|
use SprocketsMiddleware, :root => dir
|
40
71
|
|
72
|
+
|
41
73
|
helpers do
|
42
74
|
|
43
75
|
def reset_worker_list
|
@@ -99,6 +131,10 @@ module Sidekiq
|
|
99
131
|
"#{env['SCRIPT_NAME']}/"
|
100
132
|
end
|
101
133
|
|
134
|
+
def current_path
|
135
|
+
@current_path ||= request.path_info.gsub(/^\//,'')
|
136
|
+
end
|
137
|
+
|
102
138
|
def current_status
|
103
139
|
return 'idle' if workers.size == 0
|
104
140
|
return 'active'
|
@@ -165,6 +201,13 @@ module Sidekiq
|
|
165
201
|
redirect "#{root_path}queues"
|
166
202
|
end
|
167
203
|
|
204
|
+
post "/queues/:name/delete" do
|
205
|
+
Sidekiq.redis do |conn|
|
206
|
+
conn.lrem("queue:#{params[:name]}", 0, params[:key_val])
|
207
|
+
end
|
208
|
+
redirect "#{root_path}queues/#{params[:name]}"
|
209
|
+
end
|
210
|
+
|
168
211
|
get "/retries/:score" do
|
169
212
|
halt 404 unless params[:score]
|
170
213
|
@score = params[:score].to_f
|
@@ -241,7 +284,12 @@ module Sidekiq
|
|
241
284
|
end
|
242
285
|
|
243
286
|
def self.tabs
|
244
|
-
@tabs ||=
|
287
|
+
@tabs ||= {
|
288
|
+
"Workers" =>'',
|
289
|
+
"Queues" =>'queues',
|
290
|
+
"Retries" =>'retries',
|
291
|
+
"Scheduled" =>'scheduled'
|
292
|
+
}
|
245
293
|
end
|
246
294
|
|
247
295
|
end
|