sidekiq 5.0.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.

Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. data/.github/contributing.md +32 -0
  3. data/.github/issue_template.md +9 -0
  4. data/.gitignore +13 -0
  5. data/.travis.yml +18 -0
  6. data/3.0-Upgrade.md +70 -0
  7. data/4.0-Upgrade.md +53 -0
  8. data/5.0-Upgrade.md +56 -0
  9. data/COMM-LICENSE +95 -0
  10. data/Changes.md +1402 -0
  11. data/Ent-Changes.md +174 -0
  12. data/Gemfile +29 -0
  13. data/LICENSE +9 -0
  14. data/Pro-2.0-Upgrade.md +138 -0
  15. data/Pro-3.0-Upgrade.md +44 -0
  16. data/Pro-Changes.md +632 -0
  17. data/README.md +107 -0
  18. data/Rakefile +12 -0
  19. data/bin/sidekiq +18 -0
  20. data/bin/sidekiqctl +99 -0
  21. data/bin/sidekiqload +149 -0
  22. data/code_of_conduct.md +50 -0
  23. data/lib/generators/sidekiq/templates/worker.rb.erb +9 -0
  24. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +6 -0
  25. data/lib/generators/sidekiq/templates/worker_test.rb.erb +8 -0
  26. data/lib/generators/sidekiq/worker_generator.rb +49 -0
  27. data/lib/sidekiq.rb +228 -0
  28. data/lib/sidekiq/api.rb +871 -0
  29. data/lib/sidekiq/cli.rb +413 -0
  30. data/lib/sidekiq/client.rb +238 -0
  31. data/lib/sidekiq/core_ext.rb +119 -0
  32. data/lib/sidekiq/delay.rb +21 -0
  33. data/lib/sidekiq/exception_handler.rb +31 -0
  34. data/lib/sidekiq/extensions/action_mailer.rb +57 -0
  35. data/lib/sidekiq/extensions/active_record.rb +40 -0
  36. data/lib/sidekiq/extensions/class_methods.rb +40 -0
  37. data/lib/sidekiq/extensions/generic_proxy.rb +31 -0
  38. data/lib/sidekiq/fetch.rb +81 -0
  39. data/lib/sidekiq/job_logger.rb +27 -0
  40. data/lib/sidekiq/job_retry.rb +235 -0
  41. data/lib/sidekiq/launcher.rb +167 -0
  42. data/lib/sidekiq/logging.rb +106 -0
  43. data/lib/sidekiq/manager.rb +138 -0
  44. data/lib/sidekiq/middleware/chain.rb +150 -0
  45. data/lib/sidekiq/middleware/i18n.rb +42 -0
  46. data/lib/sidekiq/middleware/server/active_record.rb +22 -0
  47. data/lib/sidekiq/paginator.rb +43 -0
  48. data/lib/sidekiq/processor.rb +238 -0
  49. data/lib/sidekiq/rails.rb +60 -0
  50. data/lib/sidekiq/redis_connection.rb +106 -0
  51. data/lib/sidekiq/scheduled.rb +147 -0
  52. data/lib/sidekiq/testing.rb +324 -0
  53. data/lib/sidekiq/testing/inline.rb +29 -0
  54. data/lib/sidekiq/util.rb +63 -0
  55. data/lib/sidekiq/version.rb +4 -0
  56. data/lib/sidekiq/web.rb +213 -0
  57. data/lib/sidekiq/web/action.rb +89 -0
  58. data/lib/sidekiq/web/application.rb +331 -0
  59. data/lib/sidekiq/web/helpers.rb +286 -0
  60. data/lib/sidekiq/web/router.rb +100 -0
  61. data/lib/sidekiq/worker.rb +144 -0
  62. data/sidekiq.gemspec +32 -0
  63. data/web/assets/images/favicon.ico +0 -0
  64. data/web/assets/images/logo.png +0 -0
  65. data/web/assets/images/status.png +0 -0
  66. data/web/assets/javascripts/application.js +92 -0
  67. data/web/assets/javascripts/dashboard.js +298 -0
  68. data/web/assets/stylesheets/application-rtl.css +246 -0
  69. data/web/assets/stylesheets/application.css +1111 -0
  70. data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  71. data/web/assets/stylesheets/bootstrap.css +5 -0
  72. data/web/locales/ar.yml +80 -0
  73. data/web/locales/cs.yml +78 -0
  74. data/web/locales/da.yml +68 -0
  75. data/web/locales/de.yml +69 -0
  76. data/web/locales/el.yml +68 -0
  77. data/web/locales/en.yml +79 -0
  78. data/web/locales/es.yml +69 -0
  79. data/web/locales/fa.yml +80 -0
  80. data/web/locales/fr.yml +78 -0
  81. data/web/locales/he.yml +79 -0
  82. data/web/locales/hi.yml +75 -0
  83. data/web/locales/it.yml +69 -0
  84. data/web/locales/ja.yml +78 -0
  85. data/web/locales/ko.yml +68 -0
  86. data/web/locales/nb.yml +77 -0
  87. data/web/locales/nl.yml +68 -0
  88. data/web/locales/pl.yml +59 -0
  89. data/web/locales/pt-br.yml +68 -0
  90. data/web/locales/pt.yml +67 -0
  91. data/web/locales/ru.yml +78 -0
  92. data/web/locales/sv.yml +68 -0
  93. data/web/locales/ta.yml +75 -0
  94. data/web/locales/uk.yml +76 -0
  95. data/web/locales/ur.yml +80 -0
  96. data/web/locales/zh-cn.yml +68 -0
  97. data/web/locales/zh-tw.yml +68 -0
  98. data/web/views/_footer.erb +17 -0
  99. data/web/views/_job_info.erb +88 -0
  100. data/web/views/_nav.erb +66 -0
  101. data/web/views/_paging.erb +23 -0
  102. data/web/views/_poll_link.erb +7 -0
  103. data/web/views/_status.erb +4 -0
  104. data/web/views/_summary.erb +40 -0
  105. data/web/views/busy.erb +94 -0
  106. data/web/views/dashboard.erb +75 -0
  107. data/web/views/dead.erb +34 -0
  108. data/web/views/layout.erb +40 -0
  109. data/web/views/morgue.erb +75 -0
  110. data/web/views/queue.erb +45 -0
  111. data/web/views/queues.erb +28 -0
  112. data/web/views/retries.erb +76 -0
  113. data/web/views/retry.erb +34 -0
  114. data/web/views/scheduled.erb +54 -0
  115. data/web/views/scheduled_job_info.erb +8 -0
  116. metadata +366 -0
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ require 'sidekiq/testing'
3
+
4
+ ##
5
+ # The Sidekiq inline infrastructure overrides perform_async so that it
6
+ # actually calls perform instead. This allows workers to be run inline in a
7
+ # testing environment.
8
+ #
9
+ # This is similar to `Resque.inline = true` functionality.
10
+ #
11
+ # Example:
12
+ #
13
+ # require 'sidekiq/testing/inline'
14
+ #
15
+ # $external_variable = 0
16
+ #
17
+ # class ExternalWorker
18
+ # include Sidekiq::Worker
19
+ #
20
+ # def perform
21
+ # $external_variable = 1
22
+ # end
23
+ # end
24
+ #
25
+ # assert_equal 0, $external_variable
26
+ # ExternalWorker.perform_async
27
+ # assert_equal 1, $external_variable
28
+ #
29
+ Sidekiq::Testing.inline!
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+ require 'socket'
3
+ require 'securerandom'
4
+ require 'sidekiq/exception_handler'
5
+ require 'sidekiq/core_ext'
6
+
7
+ module Sidekiq
8
+ ##
9
+ # This module is part of Sidekiq core and not intended for extensions.
10
+ #
11
+ module Util
12
+ include ExceptionHandler
13
+
14
+ EXPIRY = 60 * 60 * 24
15
+
16
+ def watchdog(last_words)
17
+ yield
18
+ rescue Exception => ex
19
+ handle_exception(ex, { context: last_words })
20
+ raise ex
21
+ end
22
+
23
+ def safe_thread(name, &block)
24
+ Thread.new do
25
+ Thread.current['sidekiq_label'] = name
26
+ watchdog(name, &block)
27
+ end
28
+ end
29
+
30
+ def logger
31
+ Sidekiq.logger
32
+ end
33
+
34
+ def redis(&block)
35
+ Sidekiq.redis(&block)
36
+ end
37
+
38
+ def hostname
39
+ ENV['DYNO'] || Socket.gethostname
40
+ end
41
+
42
+ def process_nonce
43
+ @@process_nonce ||= SecureRandom.hex(6)
44
+ end
45
+
46
+ def identity
47
+ @@identity ||= "#{hostname}:#{$$}:#{process_nonce}"
48
+ end
49
+
50
+ def fire_event(event, reverse=false)
51
+ arr = Sidekiq.options[:lifecycle_events][event]
52
+ arr.reverse! if reverse
53
+ arr.each do |block|
54
+ begin
55
+ block.call
56
+ rescue => ex
57
+ handle_exception(ex, { context: "Exception during Sidekiq lifecycle event.", event: event })
58
+ end
59
+ end
60
+ arr.clear
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module Sidekiq
3
+ VERSION = "5.0.0"
4
+ end
@@ -0,0 +1,213 @@
1
+ # frozen_string_literal: true
2
+ require 'erb'
3
+
4
+ require 'sidekiq'
5
+ require 'sidekiq/api'
6
+ require 'sidekiq/paginator'
7
+ require 'sidekiq/web/helpers'
8
+
9
+ require 'sidekiq/web/router'
10
+ require 'sidekiq/web/action'
11
+ require 'sidekiq/web/application'
12
+
13
+ require 'rack/protection'
14
+
15
+ require 'rack/builder'
16
+ require 'rack/file'
17
+ require 'rack/session/cookie'
18
+
19
+ module Sidekiq
20
+ class Web
21
+ ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../web")
22
+ VIEWS = "#{ROOT}/views".freeze
23
+ LOCALES = ["#{ROOT}/locales".freeze]
24
+ LAYOUT = "#{VIEWS}/layout.erb".freeze
25
+ ASSETS = "#{ROOT}/assets".freeze
26
+
27
+ DEFAULT_TABS = {
28
+ "Dashboard" => '',
29
+ "Busy" => 'busy',
30
+ "Queues" => 'queues',
31
+ "Retries" => 'retries',
32
+ "Scheduled" => 'scheduled',
33
+ "Dead" => 'morgue',
34
+ }
35
+
36
+ class << self
37
+ def settings
38
+ self
39
+ end
40
+
41
+ def middlewares
42
+ @middlewares ||= []
43
+ end
44
+
45
+ def use(*middleware_args, &block)
46
+ middlewares << [middleware_args, block]
47
+ end
48
+
49
+ def default_tabs
50
+ DEFAULT_TABS
51
+ end
52
+
53
+ def custom_tabs
54
+ @custom_tabs ||= {}
55
+ end
56
+ alias_method :tabs, :custom_tabs
57
+
58
+ def locales
59
+ @locales ||= LOCALES
60
+ end
61
+
62
+ def views
63
+ @views ||= VIEWS
64
+ end
65
+
66
+ def enable(*opts)
67
+ opts.each {|key| set(key, true) }
68
+ end
69
+
70
+ def disable(*opts)
71
+ opts.each {|key| set(key, false) }
72
+ end
73
+
74
+ # Helper for the Sinatra syntax: Sidekiq::Web.set(:session_secret, Rails.application.secrets...)
75
+ def set(attribute, value)
76
+ send(:"#{attribute}=", value)
77
+ end
78
+
79
+ attr_accessor :app_url, :session_secret, :redis_pool, :sessions
80
+ attr_writer :locales, :views
81
+ end
82
+
83
+ def self.inherited(child)
84
+ child.app_url = self.app_url
85
+ child.session_secret = self.session_secret
86
+ child.redis_pool = self.redis_pool
87
+ child.sessions = self.sessions
88
+ end
89
+
90
+ def settings
91
+ self.class.settings
92
+ end
93
+
94
+ def use(*middleware_args, &block)
95
+ middlewares << [middleware_args, block]
96
+ end
97
+
98
+ def middlewares
99
+ @middlewares ||= Web.middlewares.dup
100
+ end
101
+
102
+ def call(env)
103
+ app.call(env)
104
+ end
105
+
106
+ def self.call(env)
107
+ @app ||= new
108
+ @app.call(env)
109
+ end
110
+
111
+ def app
112
+ @app ||= build
113
+ end
114
+
115
+ def enable(*opts)
116
+ opts.each {|key| set(key, true) }
117
+ end
118
+
119
+ def disable(*opts)
120
+ opts.each {|key| set(key, false) }
121
+ end
122
+
123
+ def set(attribute, value)
124
+ send(:"#{attribute}=", value)
125
+ end
126
+
127
+ # Default values
128
+ set :sessions, true
129
+
130
+ attr_writer :sessions
131
+
132
+ def sessions
133
+ unless instance_variable_defined?("@sessions")
134
+ @sessions = self.class.sessions
135
+ @sessions = @sessions.to_hash.dup if @sessions.respond_to?(:to_hash)
136
+ end
137
+
138
+ @sessions
139
+ end
140
+
141
+ def self.register(extension)
142
+ extension.registered(WebApplication)
143
+ end
144
+
145
+ private
146
+
147
+ def using?(middleware)
148
+ middlewares.any? do |(m,_)|
149
+ m.kind_of?(Array) && (m[0] == middleware || m[0].kind_of?(middleware))
150
+ end
151
+ end
152
+
153
+ def build_sessions
154
+ middlewares = self.middlewares
155
+
156
+ unless using?(::Rack::Protection) || ENV['RACK_ENV'] == 'test'
157
+ middlewares.unshift [[::Rack::Protection, { use: :authenticity_token }], nil]
158
+ end
159
+
160
+ s = sessions
161
+ return unless s
162
+
163
+ unless using? ::Rack::Session::Cookie
164
+ unless secret = Web.session_secret
165
+ require 'securerandom'
166
+ secret = SecureRandom.hex(64)
167
+ end
168
+
169
+ options = { secret: secret }
170
+ options = options.merge(s.to_hash) if s.respond_to? :to_hash
171
+
172
+ middlewares.unshift [[::Rack::Session::Cookie, options], nil]
173
+ end
174
+ end
175
+
176
+ def build
177
+ build_sessions
178
+
179
+ middlewares = self.middlewares
180
+ klass = self.class
181
+
182
+ ::Rack::Builder.new do
183
+ %w(stylesheets javascripts images).each do |asset_dir|
184
+ map "/#{asset_dir}" do
185
+ run ::Rack::File.new("#{ASSETS}/#{asset_dir}", { 'Cache-Control' => 'public, max-age=86400' })
186
+ end
187
+ end
188
+
189
+ middlewares.each {|middleware, block| use(*middleware, &block) }
190
+
191
+ run WebApplication.new(klass)
192
+ end
193
+ end
194
+ end
195
+
196
+ Sidekiq::WebApplication.helpers WebHelpers
197
+ Sidekiq::WebApplication.helpers Sidekiq::Paginator
198
+
199
+ Sidekiq::WebAction.class_eval "def _render\n#{ERB.new(File.read(Web::LAYOUT)).src}\nend"
200
+ end
201
+
202
+ if defined?(::ActionDispatch::Request::Session) &&
203
+ !::ActionDispatch::Request::Session.method_defined?(:each)
204
+ # mperham/sidekiq#2460
205
+ # Rack apps can't reuse the Rails session store without
206
+ # this monkeypatch, fixed in Rails 5.
207
+ class ActionDispatch::Request::Session
208
+ def each(&block)
209
+ hash = self.to_hash
210
+ hash.each(&block)
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidekiq
4
+ class WebAction
5
+ RACK_SESSION = 'rack.session'.freeze
6
+
7
+ attr_accessor :env, :block, :type
8
+
9
+ def settings
10
+ Web.settings
11
+ end
12
+
13
+ def request
14
+ @request ||= ::Rack::Request.new(env)
15
+ end
16
+
17
+ def halt(res)
18
+ throw :halt, res
19
+ end
20
+
21
+ def redirect(location)
22
+ throw :halt, [302, { "Location" => "#{request.base_url}#{location}" }, []]
23
+ end
24
+
25
+ def params
26
+ indifferent_hash = Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
27
+
28
+ indifferent_hash.merge! request.params
29
+ route_params.each {|k,v| indifferent_hash[k.to_s] = v }
30
+
31
+ indifferent_hash
32
+ end
33
+
34
+ def route_params
35
+ env[WebRouter::ROUTE_PARAMS]
36
+ end
37
+
38
+ def session
39
+ env[RACK_SESSION]
40
+ end
41
+
42
+ def erb(content, options = {})
43
+ if content.kind_of? Symbol
44
+ unless respond_to?(:"_erb_#{content}")
45
+ src = ERB.new(File.read("#{Web.settings.views}/#{content}.erb")).src
46
+ WebAction.class_eval("def _erb_#{content}\n#{src}\n end")
47
+ end
48
+ end
49
+
50
+ if @_erb
51
+ _erb(content, options[:locals])
52
+ else
53
+ @_erb = true
54
+ content = _erb(content, options[:locals])
55
+
56
+ _render { content }
57
+ end
58
+ end
59
+
60
+ def render(engine, content, options = {})
61
+ raise "Only erb templates are supported" if engine != :erb
62
+
63
+ erb(content, options)
64
+ end
65
+
66
+ def json(payload)
67
+ [200, { "Content-Type" => "application/json", "Cache-Control" => "no-cache" }, [Sidekiq.dump_json(payload)]]
68
+ end
69
+
70
+ def initialize(env, block)
71
+ @_erb = false
72
+ @env = env
73
+ @block = block
74
+ @@files ||= {}
75
+ end
76
+
77
+ private
78
+
79
+ def _erb(file, locals)
80
+ locals.each {|k, v| define_singleton_method(k){ v } } if locals
81
+
82
+ if file.kind_of?(String)
83
+ ERB.new(file).result(binding)
84
+ else
85
+ send(:"_erb_#{file}")
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,331 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidekiq
4
+ class WebApplication
5
+ extend WebRouter
6
+
7
+ CONTENT_LENGTH = "Content-Length".freeze
8
+ CONTENT_TYPE = "Content-Type".freeze
9
+ REDIS_KEYS = %w(redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human)
10
+
11
+ def initialize(klass)
12
+ @klass = klass
13
+ end
14
+
15
+ def settings
16
+ @klass.settings
17
+ end
18
+
19
+ def self.settings
20
+ Sidekiq::Web.settings
21
+ end
22
+
23
+ def self.tabs
24
+ Sidekiq::Web.tabs
25
+ end
26
+
27
+ def self.set(key, val)
28
+ # nothing, backwards compatibility
29
+ end
30
+
31
+ get "/" do
32
+ @redis_info = redis_info.select{ |k, v| REDIS_KEYS.include? k }
33
+ stats_history = Sidekiq::Stats::History.new((params['days'] || 30).to_i)
34
+ @processed_history = stats_history.processed
35
+ @failed_history = stats_history.failed
36
+
37
+ erb(:dashboard)
38
+ end
39
+
40
+ get "/busy" do
41
+ erb(:busy)
42
+ end
43
+
44
+ post "/busy" do
45
+ if params['identity']
46
+ p = Sidekiq::Process.new('identity' => params['identity'])
47
+ p.quiet! if params['quiet']
48
+ p.stop! if params['stop']
49
+ else
50
+ processes.each do |pro|
51
+ pro.quiet! if params['quiet']
52
+ pro.stop! if params['stop']
53
+ end
54
+ end
55
+
56
+ redirect "#{root_path}busy"
57
+ end
58
+
59
+ get "/queues" do
60
+ @queues = Sidekiq::Queue.all
61
+
62
+ erb(:queues)
63
+ end
64
+
65
+ get "/queues/:name" do
66
+ @name = route_params[:name]
67
+
68
+ halt(404) unless @name
69
+
70
+ @count = (params['count'] || 25).to_i
71
+ @queue = Sidekiq::Queue.new(@name)
72
+ (@current_page, @total_size, @messages) = page("queue:#{@name}", params['page'], @count)
73
+ @messages = @messages.map { |msg| Sidekiq::Job.new(msg, @name) }
74
+
75
+ erb(:queue)
76
+ end
77
+
78
+ post "/queues/:name" do
79
+ Sidekiq::Queue.new(route_params[:name]).clear
80
+
81
+ redirect "#{root_path}queues"
82
+ end
83
+
84
+ post "/queues/:name/delete" do
85
+ name = route_params[:name]
86
+ Sidekiq::Job.new(params['key_val'], name).delete
87
+
88
+ redirect_with_query("#{root_path}queues/#{CGI.escape(name)}")
89
+ end
90
+
91
+ get '/morgue' do
92
+ @count = (params['count'] || 25).to_i
93
+ (@current_page, @total_size, @dead) = page("dead", params['page'], @count, reverse: true)
94
+ @dead = @dead.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
95
+
96
+ erb(:morgue)
97
+ end
98
+
99
+ get "/morgue/:key" do
100
+ halt(404) unless key = route_params[:key]
101
+
102
+ @dead = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
103
+
104
+ if @dead.nil?
105
+ redirect "#{root_path}morgue"
106
+ else
107
+ erb(:dead)
108
+ end
109
+ end
110
+
111
+ post '/morgue' do
112
+ redirect(request.path) unless params['key']
113
+
114
+ params['key'].each do |key|
115
+ job = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
116
+ retry_or_delete_or_kill job, params if job
117
+ end
118
+
119
+ redirect_with_query("#{root_path}morgue")
120
+ end
121
+
122
+ post "/morgue/all/delete" do
123
+ Sidekiq::DeadSet.new.clear
124
+
125
+ redirect "#{root_path}morgue"
126
+ end
127
+
128
+ post "/morgue/all/retry" do
129
+ Sidekiq::DeadSet.new.retry_all
130
+
131
+ redirect "#{root_path}morgue"
132
+ end
133
+
134
+ post "/morgue/:key" do
135
+ halt(404) unless key = route_params[:key]
136
+
137
+ job = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
138
+ retry_or_delete_or_kill job, params if job
139
+
140
+ redirect_with_query("#{root_path}morgue")
141
+ end
142
+
143
+ get '/retries' do
144
+ @count = (params['count'] || 25).to_i
145
+ (@current_page, @total_size, @retries) = page("retry", params['page'], @count)
146
+ @retries = @retries.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
147
+
148
+ erb(:retries)
149
+ end
150
+
151
+ get "/retries/:key" do
152
+ @retry = Sidekiq::RetrySet.new.fetch(*parse_params(route_params[:key])).first
153
+
154
+ if @retry.nil?
155
+ redirect "#{root_path}retries"
156
+ else
157
+ erb(:retry)
158
+ end
159
+ end
160
+
161
+ post '/retries' do
162
+ redirect(request.path) unless params['key']
163
+
164
+ params['key'].each do |key|
165
+ job = Sidekiq::RetrySet.new.fetch(*parse_params(key)).first
166
+ retry_or_delete_or_kill job, params if job
167
+ end
168
+
169
+ redirect_with_query("#{root_path}retries")
170
+ end
171
+
172
+ post "/retries/all/delete" do
173
+ Sidekiq::RetrySet.new.clear
174
+
175
+ redirect "#{root_path}retries"
176
+ end
177
+
178
+ post "/retries/all/retry" do
179
+ Sidekiq::RetrySet.new.retry_all
180
+
181
+ redirect "#{root_path}retries"
182
+ end
183
+
184
+ post "/retries/:key" do
185
+ job = Sidekiq::RetrySet.new.fetch(*parse_params(route_params[:key])).first
186
+
187
+ retry_or_delete_or_kill job, params if job
188
+
189
+ redirect_with_query("#{root_path}retries")
190
+ end
191
+
192
+ get '/scheduled' do
193
+ @count = (params['count'] || 25).to_i
194
+ (@current_page, @total_size, @scheduled) = page("schedule", params['page'], @count)
195
+ @scheduled = @scheduled.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
196
+
197
+ erb(:scheduled)
198
+ end
199
+
200
+ get "/scheduled/:key" do
201
+ @job = Sidekiq::ScheduledSet.new.fetch(*parse_params(route_params[:key])).first
202
+
203
+ if @job.nil?
204
+ redirect "#{root_path}scheduled"
205
+ else
206
+ erb(:scheduled_job_info)
207
+ end
208
+ end
209
+
210
+ post '/scheduled' do
211
+ redirect(request.path) unless params['key']
212
+
213
+ params['key'].each do |key|
214
+ job = Sidekiq::ScheduledSet.new.fetch(*parse_params(key)).first
215
+ delete_or_add_queue job, params if job
216
+ end
217
+
218
+ redirect_with_query("#{root_path}scheduled")
219
+ end
220
+
221
+ post "/scheduled/:key" do
222
+ halt(404) unless key = route_params[:key]
223
+
224
+ job = Sidekiq::ScheduledSet.new.fetch(*parse_params(key)).first
225
+ delete_or_add_queue job, params if job
226
+
227
+ redirect_with_query("#{root_path}scheduled")
228
+ end
229
+
230
+ get '/dashboard/stats' do
231
+ redirect "#{root_path}stats"
232
+ end
233
+
234
+ get '/stats' do
235
+ sidekiq_stats = Sidekiq::Stats.new
236
+ redis_stats = redis_info.select { |k, v| REDIS_KEYS.include? k }
237
+
238
+ json(
239
+ sidekiq: {
240
+ processed: sidekiq_stats.processed,
241
+ failed: sidekiq_stats.failed,
242
+ busy: sidekiq_stats.workers_size,
243
+ processes: sidekiq_stats.processes_size,
244
+ enqueued: sidekiq_stats.enqueued,
245
+ scheduled: sidekiq_stats.scheduled_size,
246
+ retries: sidekiq_stats.retry_size,
247
+ dead: sidekiq_stats.dead_size,
248
+ default_latency: sidekiq_stats.default_queue_latency
249
+ },
250
+ redis: redis_stats
251
+ )
252
+ end
253
+
254
+ get '/stats/queues' do
255
+ json Sidekiq::Stats::Queues.new.lengths
256
+ end
257
+
258
+ def call(env)
259
+ action = self.class.match(env)
260
+ return [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass" }, ["Not Found"]] unless action
261
+
262
+ resp = catch(:halt) do
263
+ app = @klass
264
+ self.class.run_befores(app, action)
265
+ begin
266
+ resp = action.instance_exec env, &action.block
267
+ ensure
268
+ self.class.run_afters(app, action)
269
+ end
270
+
271
+ resp
272
+ end
273
+
274
+ resp = case resp
275
+ when Array
276
+ resp
277
+ else
278
+ headers = {
279
+ "Content-Type" => "text/html",
280
+ "Cache-Control" => "no-cache",
281
+ "Content-Language" => action.locale,
282
+ }
283
+
284
+ [200, headers, [resp]]
285
+ end
286
+
287
+ resp[1] = resp[1].dup
288
+
289
+ resp[1][CONTENT_LENGTH] = resp[2].inject(0) { |l, p| l + p.bytesize }.to_s
290
+
291
+ resp
292
+ end
293
+
294
+ def self.helpers(mod=nil, &block)
295
+ if block_given?
296
+ WebAction.class_eval(&block)
297
+ else
298
+ WebAction.send(:include, mod)
299
+ end
300
+ end
301
+
302
+ def self.before(path=nil, &block)
303
+ befores << [path && Regexp.new("\\A#{path.gsub("*", ".*")}\\z"), block]
304
+ end
305
+
306
+ def self.after(path=nil, &block)
307
+ afters << [path && Regexp.new("\\A#{path.gsub("*", ".*")}\\z"), block]
308
+ end
309
+
310
+ def self.run_befores(app, action)
311
+ run_hooks(befores, app, action)
312
+ end
313
+
314
+ def self.run_afters(app, action)
315
+ run_hooks(afters, app, action)
316
+ end
317
+
318
+ def self.run_hooks(hooks, app, action)
319
+ hooks.select { |p,_| !p || p =~ action.env[WebRouter::PATH_INFO] }.
320
+ each {|_,b| action.instance_exec(action.env, app, &b) }
321
+ end
322
+
323
+ def self.befores
324
+ @befores ||= []
325
+ end
326
+
327
+ def self.afters
328
+ @afters ||= []
329
+ end
330
+ end
331
+ end