sidekiq 6.5.10 → 8.1.7
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/Changes.md +568 -18
- data/README.md +66 -38
- data/bin/kiq +17 -0
- data/bin/lint-herb +13 -0
- data/bin/multi_queue_bench +271 -0
- data/bin/sidekiq +3 -8
- data/bin/sidekiqload +215 -118
- data/bin/sidekiqmon +3 -0
- data/bin/webload +69 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +124 -0
- data/lib/generators/sidekiq/job_generator.rb +17 -3
- data/lib/generators/sidekiq/templates/job.rb.erb +1 -1
- data/lib/sidekiq/api.rb +526 -218
- data/lib/sidekiq/capsule.rb +135 -0
- data/lib/sidekiq/cli.rb +88 -96
- data/lib/sidekiq/client.rb +114 -47
- data/lib/sidekiq/component.rb +71 -4
- data/lib/sidekiq/config.rb +316 -0
- data/lib/sidekiq/deploy.rb +64 -0
- data/lib/sidekiq/embedded.rb +64 -0
- data/lib/sidekiq/fetch.rb +12 -14
- data/lib/sidekiq/iterable_job.rb +56 -0
- data/lib/sidekiq/job/interrupt_handler.rb +24 -0
- data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
- data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
- data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
- data/lib/sidekiq/job/iterable.rb +322 -0
- data/lib/sidekiq/job.rb +397 -10
- data/lib/sidekiq/job_logger.rb +14 -11
- data/lib/sidekiq/job_retry.rb +78 -28
- data/lib/sidekiq/job_util.rb +58 -16
- data/lib/sidekiq/launcher.rb +86 -70
- data/lib/sidekiq/loader.rb +57 -0
- data/lib/sidekiq/logger.rb +25 -94
- data/lib/sidekiq/manager.rb +11 -13
- data/lib/sidekiq/metrics/query.rb +84 -48
- data/lib/sidekiq/metrics/shared.rb +29 -14
- data/lib/sidekiq/metrics/tracking.rb +49 -30
- data/lib/sidekiq/middleware/chain.rb +19 -18
- data/lib/sidekiq/middleware/current_attributes.rb +78 -21
- data/lib/sidekiq/middleware/i18n.rb +2 -0
- data/lib/sidekiq/middleware/modules.rb +2 -0
- data/lib/sidekiq/monitor.rb +15 -5
- data/lib/sidekiq/paginator.rb +26 -4
- data/lib/sidekiq/processor.rb +78 -65
- data/lib/sidekiq/profiler.rb +73 -0
- data/lib/sidekiq/rails.rb +45 -60
- data/lib/sidekiq/redis_client_adapter.rb +35 -72
- data/lib/sidekiq/redis_connection.rb +54 -114
- data/lib/sidekiq/ring_buffer.rb +3 -0
- data/lib/sidekiq/scheduled.rb +28 -30
- data/lib/sidekiq/systemd.rb +2 -0
- data/lib/sidekiq/test_api.rb +331 -0
- data/lib/sidekiq/testing/inline.rb +2 -30
- data/lib/sidekiq/testing.rb +2 -342
- data/lib/sidekiq/transaction_aware_client.rb +23 -9
- data/lib/sidekiq/tui/controls.rb +53 -0
- data/lib/sidekiq/tui/filtering.rb +53 -0
- data/lib/sidekiq/tui/tabs/base_tab.rb +230 -0
- data/lib/sidekiq/tui/tabs/busy.rb +104 -0
- data/lib/sidekiq/tui/tabs/dead.rb +14 -0
- data/lib/sidekiq/tui/tabs/home.rb +115 -0
- data/lib/sidekiq/tui/tabs/metrics.rb +114 -0
- data/lib/sidekiq/tui/tabs/queues.rb +83 -0
- data/lib/sidekiq/tui/tabs/retries.rb +14 -0
- data/lib/sidekiq/tui/tabs/scheduled.rb +14 -0
- data/lib/sidekiq/tui/tabs/set_tab.rb +91 -0
- data/lib/sidekiq/tui/tabs.rb +15 -0
- data/lib/sidekiq/tui.rb +390 -0
- data/lib/sidekiq/version.rb +6 -1
- data/lib/sidekiq/web/action.rb +149 -64
- data/lib/sidekiq/web/application.rb +379 -281
- data/lib/sidekiq/web/config.rb +117 -0
- data/lib/sidekiq/web/helpers.rb +190 -71
- data/lib/sidekiq/web/router.rb +62 -75
- data/lib/sidekiq/web.rb +68 -101
- data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/lib/sidekiq.rb +85 -269
- data/sidekiq.gemspec +15 -11
- data/web/assets/images/logo.png +0 -0
- data/web/assets/images/status.png +0 -0
- data/web/assets/javascripts/application.js +84 -24
- data/web/assets/javascripts/base-charts.js +120 -0
- data/web/assets/javascripts/chartjs-adapter-date-fns.min.js +7 -0
- data/web/assets/javascripts/dashboard-charts.js +194 -0
- data/web/assets/javascripts/dashboard.js +18 -234
- data/web/assets/javascripts/metrics.js +138 -120
- data/web/assets/stylesheets/style.css +776 -0
- data/web/locales/ar.yml +72 -70
- data/web/locales/cs.yml +64 -62
- data/web/locales/da.yml +62 -53
- data/web/locales/de.yml +67 -65
- data/web/locales/el.yml +4 -7
- data/web/locales/en.yml +89 -71
- data/web/locales/es.yml +91 -68
- data/web/locales/fa.yml +67 -65
- data/web/locales/fr.yml +82 -67
- data/web/locales/gd.yml +110 -0
- data/web/locales/he.yml +67 -64
- data/web/locales/hi.yml +95 -59
- data/web/locales/it.yml +94 -54
- data/web/locales/ja.yml +69 -70
- data/web/locales/ko.yml +54 -52
- data/web/locales/lt.yml +68 -66
- data/web/locales/nb.yml +63 -61
- data/web/locales/nl.yml +54 -52
- data/web/locales/pl.yml +47 -45
- data/web/locales/{pt-br.yml → pt-BR.yml} +81 -70
- data/web/locales/pt.yml +53 -51
- data/web/locales/ru.yml +69 -66
- data/web/locales/sv.yml +55 -53
- data/web/locales/ta.yml +100 -64
- data/web/locales/tr.yml +102 -0
- data/web/locales/uk.yml +87 -61
- data/web/locales/ur.yml +66 -64
- data/web/locales/vi.yml +69 -67
- data/web/locales/zh-CN.yml +107 -0
- data/web/locales/{zh-tw.yml → zh-TW.yml} +13 -3
- data/web/views/_footer.html.erb +32 -0
- data/web/views/_job_info.html.erb +115 -0
- data/web/views/_metrics_period_select.html.erb +15 -0
- data/web/views/_nav.html.erb +45 -0
- data/web/views/_paging.html.erb +26 -0
- data/web/views/_poll_link.html.erb +4 -0
- data/web/views/_summary.html.erb +40 -0
- data/web/views/busy.html.erb +155 -0
- data/web/views/dashboard.html.erb +104 -0
- data/web/views/dead.html.erb +38 -0
- data/web/views/filtering.html.erb +6 -0
- data/web/views/layout.html.erb +26 -0
- data/web/views/metrics.html.erb +85 -0
- data/web/views/metrics_for_job.html.erb +58 -0
- data/web/views/morgue.html.erb +69 -0
- data/web/views/profiles.html.erb +43 -0
- data/web/views/queue.html.erb +57 -0
- data/web/views/queues.html.erb +46 -0
- data/web/views/retries.html.erb +77 -0
- data/web/views/retry.html.erb +39 -0
- data/web/views/scheduled.html.erb +64 -0
- data/web/views/{scheduled_job_info.erb → scheduled_job_info.html.erb} +3 -3
- metadata +113 -70
- data/lib/sidekiq/delay.rb +0 -43
- data/lib/sidekiq/extensions/action_mailer.rb +0 -48
- data/lib/sidekiq/extensions/active_record.rb +0 -43
- data/lib/sidekiq/extensions/class_methods.rb +0 -43
- data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
- data/lib/sidekiq/metrics/deploy.rb +0 -47
- data/lib/sidekiq/web/csrf_protection.rb +0 -180
- data/lib/sidekiq/worker.rb +0 -370
- data/web/assets/javascripts/graph.js +0 -16
- data/web/assets/stylesheets/application-dark.css +0 -143
- data/web/assets/stylesheets/application-rtl.css +0 -242
- data/web/assets/stylesheets/application.css +0 -999
- data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
- data/web/assets/stylesheets/bootstrap.css +0 -5
- data/web/locales/zh-cn.yml +0 -93
- data/web/views/_footer.erb +0 -20
- data/web/views/_job_info.erb +0 -89
- data/web/views/_nav.erb +0 -52
- data/web/views/_paging.erb +0 -23
- data/web/views/_poll_link.erb +0 -4
- data/web/views/_status.erb +0 -4
- data/web/views/_summary.erb +0 -40
- data/web/views/busy.erb +0 -137
- data/web/views/dashboard.erb +0 -84
- data/web/views/dead.erb +0 -34
- data/web/views/layout.erb +0 -42
- data/web/views/metrics.erb +0 -69
- data/web/views/metrics_for_job.erb +0 -87
- data/web/views/morgue.erb +0 -78
- data/web/views/queue.erb +0 -59
- data/web/views/queues.erb +0 -38
- data/web/views/retries.erb +0 -83
- data/web/views/retry.erb +0 -34
- data/web/views/scheduled.erb +0 -57
- /data/{LICENSE → LICENSE.txt} +0 -0
data/lib/sidekiq/web.rb
CHANGED
|
@@ -1,27 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "erb"
|
|
4
|
-
|
|
5
|
-
require "sidekiq"
|
|
6
|
-
require "sidekiq/api"
|
|
7
|
-
require "sidekiq/paginator"
|
|
8
|
-
require "sidekiq/web/helpers"
|
|
9
|
-
|
|
10
|
-
require "sidekiq/web/router"
|
|
11
|
-
require "sidekiq/web/action"
|
|
12
|
-
require "sidekiq/web/application"
|
|
13
|
-
require "sidekiq/web/csrf_protection"
|
|
14
|
-
|
|
15
|
-
require "rack/content_length"
|
|
4
|
+
require "securerandom"
|
|
16
5
|
require "rack/builder"
|
|
17
6
|
require "rack/static"
|
|
7
|
+
require "sidekiq"
|
|
8
|
+
require "sidekiq/api"
|
|
9
|
+
require "sidekiq/web/config"
|
|
18
10
|
|
|
19
11
|
module Sidekiq
|
|
20
12
|
class Web
|
|
21
13
|
ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../web")
|
|
22
14
|
VIEWS = "#{ROOT}/views"
|
|
23
15
|
LOCALES = ["#{ROOT}/locales"]
|
|
24
|
-
LAYOUT = "#{VIEWS}/layout.erb"
|
|
16
|
+
LAYOUT = "#{VIEWS}/layout.html.erb"
|
|
25
17
|
ASSETS = "#{ROOT}/assets"
|
|
26
18
|
|
|
27
19
|
DEFAULT_TABS = {
|
|
@@ -30,144 +22,119 @@ module Sidekiq
|
|
|
30
22
|
"Queues" => "queues",
|
|
31
23
|
"Retries" => "retries",
|
|
32
24
|
"Scheduled" => "scheduled",
|
|
33
|
-
"Dead" => "morgue"
|
|
25
|
+
"Dead" => "morgue",
|
|
26
|
+
"Metrics" => "metrics",
|
|
27
|
+
"Profiles" => "profiles"
|
|
34
28
|
}
|
|
35
29
|
|
|
36
|
-
|
|
37
|
-
DEFAULT_TABS["Metrics"] = "metrics"
|
|
38
|
-
end
|
|
30
|
+
@@config = Sidekiq::Web::Config.new
|
|
39
31
|
|
|
40
32
|
class << self
|
|
41
|
-
def
|
|
42
|
-
|
|
33
|
+
def configure
|
|
34
|
+
if block_given?
|
|
35
|
+
yield @@config
|
|
36
|
+
else
|
|
37
|
+
@@config
|
|
38
|
+
end
|
|
43
39
|
end
|
|
44
40
|
|
|
45
|
-
def
|
|
46
|
-
|
|
41
|
+
def app_url=(url)
|
|
42
|
+
@@config.app_url = url
|
|
47
43
|
end
|
|
48
44
|
|
|
49
|
-
def
|
|
50
|
-
|
|
45
|
+
def assets_path=(path)
|
|
46
|
+
@@config.assets_path = path
|
|
51
47
|
end
|
|
52
|
-
alias_method :tabs, :custom_tabs
|
|
53
48
|
|
|
54
|
-
def
|
|
55
|
-
@locales ||= LOCALES
|
|
56
|
-
end
|
|
49
|
+
def assets_path = @@config.assets_path
|
|
57
50
|
|
|
58
|
-
def
|
|
59
|
-
@views ||= VIEWS
|
|
60
|
-
end
|
|
51
|
+
def tabs = @@config.tabs
|
|
61
52
|
|
|
62
|
-
def
|
|
63
|
-
opts.each { |key| set(key, true) }
|
|
64
|
-
end
|
|
53
|
+
def locales = @@config.locales
|
|
65
54
|
|
|
66
|
-
def
|
|
67
|
-
opts.each { |key| set(key, false) }
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def middlewares
|
|
71
|
-
@middlewares ||= []
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def use(*args, &block)
|
|
75
|
-
middlewares << [args, block]
|
|
76
|
-
end
|
|
55
|
+
def views = @@config.views
|
|
77
56
|
|
|
78
|
-
def
|
|
79
|
-
send(:"#{attribute}=", value)
|
|
80
|
-
end
|
|
57
|
+
def custom_job_info_rows = @@config.custom_job_info_rows
|
|
81
58
|
|
|
82
|
-
def
|
|
83
|
-
|
|
59
|
+
def redis_pool
|
|
60
|
+
@pool || Sidekiq.default_configuration.redis_pool
|
|
84
61
|
end
|
|
85
62
|
|
|
86
|
-
def
|
|
87
|
-
|
|
63
|
+
def redis_pool=(pool)
|
|
64
|
+
@pool = pool
|
|
88
65
|
end
|
|
89
66
|
|
|
90
|
-
|
|
91
|
-
attr_writer :locales, :views
|
|
92
|
-
end
|
|
67
|
+
def middlewares = @@config.middlewares
|
|
93
68
|
|
|
94
|
-
|
|
95
|
-
child.app_url = app_url
|
|
96
|
-
child.redis_pool = redis_pool
|
|
97
|
-
end
|
|
69
|
+
def use(*args, &block) = @@config.middlewares << [args, block]
|
|
98
70
|
|
|
99
|
-
|
|
100
|
-
|
|
71
|
+
def register(*args, **kw, &block)
|
|
72
|
+
Sidekiq.logger.warn { "`Sidekiq::Web.register` is deprecated, use `Sidekiq::Web.configure {|cfg| cfg.register(...) }`" }
|
|
73
|
+
@@config.register(*args, **kw, &block)
|
|
74
|
+
end
|
|
101
75
|
end
|
|
102
76
|
|
|
103
|
-
|
|
104
|
-
|
|
77
|
+
# Allow user to say
|
|
78
|
+
# run Sidekiq::Web
|
|
79
|
+
# rather than:
|
|
80
|
+
# run Sidekiq::Web.new
|
|
81
|
+
def self.call(env)
|
|
82
|
+
@inst ||= new
|
|
83
|
+
@inst.call(env)
|
|
105
84
|
end
|
|
106
85
|
|
|
107
|
-
|
|
108
|
-
|
|
86
|
+
# testing, internal use only
|
|
87
|
+
def self.reset!
|
|
88
|
+
@@config.reset!
|
|
89
|
+
@inst = nil
|
|
109
90
|
end
|
|
110
91
|
|
|
111
92
|
def call(env)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
@app ||= new
|
|
117
|
-
@app.call(env)
|
|
93
|
+
env[:web_config] = Sidekiq::Web.configure
|
|
94
|
+
env[:csp_nonce] = SecureRandom.hex(8)
|
|
95
|
+
env[:redis_pool] = self.class.redis_pool
|
|
96
|
+
safe_request?(env) ? app.call(env) : deny(env)
|
|
118
97
|
end
|
|
119
98
|
|
|
120
99
|
def app
|
|
121
|
-
@app ||= build
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def enable(*opts)
|
|
125
|
-
opts.each { |key| set(key, true) }
|
|
100
|
+
@app ||= build(@@config)
|
|
126
101
|
end
|
|
127
102
|
|
|
128
|
-
def
|
|
129
|
-
|
|
103
|
+
def safe_methods?(env)
|
|
104
|
+
%w[GET HEAD OPTIONS TRACE].include? env["REQUEST_METHOD"]
|
|
130
105
|
end
|
|
131
106
|
|
|
132
|
-
def
|
|
133
|
-
|
|
107
|
+
def safe_request?(env)
|
|
108
|
+
return true if safe_methods?(env)
|
|
109
|
+
env["HTTP_SEC_FETCH_SITE"] == "same-origin"
|
|
134
110
|
end
|
|
135
111
|
|
|
136
|
-
def
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
def self.register(extension)
|
|
141
|
-
extension.registered(WebApplication)
|
|
112
|
+
def deny(env)
|
|
113
|
+
Sidekiq.logger.warn "attack prevented by #{self.class}"
|
|
114
|
+
[403, {Rack::CONTENT_TYPE => "text/plain"}, ["Forbidden"]]
|
|
142
115
|
end
|
|
143
116
|
|
|
144
117
|
private
|
|
145
118
|
|
|
146
|
-
def build
|
|
147
|
-
|
|
148
|
-
m = middlewares
|
|
119
|
+
def build(cfg)
|
|
120
|
+
cfg.freeze
|
|
121
|
+
m = cfg.middlewares
|
|
149
122
|
|
|
150
123
|
rules = []
|
|
151
|
-
rules = [[:all, {"cache-control" => "
|
|
124
|
+
rules = [[:all, {"cache-control" => "private, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
|
|
152
125
|
|
|
153
126
|
::Rack::Builder.new do
|
|
154
127
|
use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
|
|
155
|
-
root:
|
|
128
|
+
root: cfg.assets_path,
|
|
156
129
|
cascade: true,
|
|
157
130
|
header_rules: rules
|
|
158
131
|
m.each { |middleware, block| use(*middleware, &block) }
|
|
159
|
-
|
|
160
|
-
run WebApplication.new(klass)
|
|
132
|
+
run Sidekiq::Web::Application.new(self.class)
|
|
161
133
|
end
|
|
162
134
|
end
|
|
163
135
|
end
|
|
164
|
-
|
|
165
|
-
Sidekiq::WebApplication.helpers WebHelpers
|
|
166
|
-
Sidekiq::WebApplication.helpers Sidekiq::Paginator
|
|
167
|
-
|
|
168
|
-
Sidekiq::WebAction.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
169
|
-
def _render
|
|
170
|
-
#{ERB.new(File.read(Web::LAYOUT)).src}
|
|
171
|
-
end
|
|
172
|
-
RUBY
|
|
173
136
|
end
|
|
137
|
+
|
|
138
|
+
require "sidekiq/web/router"
|
|
139
|
+
require "sidekiq/web/action"
|
|
140
|
+
require "sidekiq/web/application"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sidekiq
|
|
4
|
+
# Sidekiq::Job is a new alias for Sidekiq::Worker as of Sidekiq 6.3.0.
|
|
5
|
+
# Use `include Sidekiq::Job` rather than `include Sidekiq::Worker`.
|
|
6
|
+
#
|
|
7
|
+
# The term "worker" is too generic and overly confusing, used in several
|
|
8
|
+
# different contexts meaning different things. Many people call a Sidekiq
|
|
9
|
+
# process a "worker". Some people call the thread that executes jobs a
|
|
10
|
+
# "worker". This change brings Sidekiq closer to ActiveJob where your job
|
|
11
|
+
# classes extend ApplicationJob.
|
|
12
|
+
Worker = Job
|
|
13
|
+
end
|
data/lib/sidekiq.rb
CHANGED
|
@@ -1,15 +1,41 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "sidekiq/version"
|
|
4
|
-
fail "Sidekiq #{Sidekiq::VERSION} does not support Ruby versions below 2.
|
|
4
|
+
fail "Sidekiq #{Sidekiq::VERSION} does not support Ruby versions below 3.2.0." if RUBY_PLATFORM != "java" && Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2.0")
|
|
5
5
|
|
|
6
|
+
begin
|
|
7
|
+
require "sidekiq-ent/version"
|
|
8
|
+
fail <<~EOM if Gem::Version.new(Sidekiq::Enterprise::VERSION).segments[0] != Sidekiq::MAJOR
|
|
9
|
+
|
|
10
|
+
Sidekiq Enterprise #{Sidekiq::Enterprise::VERSION} does not work with Sidekiq #{Sidekiq::VERSION}.
|
|
11
|
+
Starting with Sidekiq 7, major versions are synchronized so Sidekiq Enterprise 7 works with Sidekiq 7.
|
|
12
|
+
Use `bundle up sidekiq-ent` to upgrade.
|
|
13
|
+
|
|
14
|
+
EOM
|
|
15
|
+
rescue LoadError
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
begin
|
|
19
|
+
require "sidekiq/pro/version"
|
|
20
|
+
fail <<~EOM if Gem::Version.new(Sidekiq::Pro::VERSION).segments[0] != Sidekiq::MAJOR
|
|
21
|
+
|
|
22
|
+
Sidekiq Pro #{Sidekiq::Pro::VERSION} does not work with Sidekiq #{Sidekiq::VERSION}.
|
|
23
|
+
Starting with Sidekiq 7, major versions are synchronized so Sidekiq Pro 7 works with Sidekiq 7.
|
|
24
|
+
Use `bundle up sidekiq-pro` to upgrade.
|
|
25
|
+
|
|
26
|
+
EOM
|
|
27
|
+
rescue LoadError
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
require "sidekiq/config"
|
|
6
31
|
require "sidekiq/logger"
|
|
32
|
+
require "sidekiq/loader"
|
|
7
33
|
require "sidekiq/client"
|
|
8
34
|
require "sidekiq/transaction_aware_client"
|
|
9
|
-
require "sidekiq/worker"
|
|
10
35
|
require "sidekiq/job"
|
|
11
|
-
require "sidekiq/
|
|
12
|
-
require "sidekiq/
|
|
36
|
+
require "sidekiq/iterable_job"
|
|
37
|
+
require "sidekiq/worker_compatibility_alias"
|
|
38
|
+
require "sidekiq/redis_client_adapter"
|
|
13
39
|
|
|
14
40
|
require "json"
|
|
15
41
|
|
|
@@ -17,320 +43,110 @@ module Sidekiq
|
|
|
17
43
|
NAME = "Sidekiq"
|
|
18
44
|
LICENSE = "See LICENSE and the LGPL-3.0 for licensing details."
|
|
19
45
|
|
|
20
|
-
DEFAULTS = {
|
|
21
|
-
queues: [],
|
|
22
|
-
labels: [],
|
|
23
|
-
concurrency: 10,
|
|
24
|
-
require: ".",
|
|
25
|
-
strict: true,
|
|
26
|
-
environment: nil,
|
|
27
|
-
timeout: 25,
|
|
28
|
-
poll_interval_average: nil,
|
|
29
|
-
average_scheduled_poll_interval: 5,
|
|
30
|
-
on_complex_arguments: :warn,
|
|
31
|
-
error_handlers: [],
|
|
32
|
-
death_handlers: [],
|
|
33
|
-
lifecycle_events: {
|
|
34
|
-
startup: [],
|
|
35
|
-
quiet: [],
|
|
36
|
-
shutdown: [],
|
|
37
|
-
# triggers when we fire the first heartbeat on startup OR repairing a network partition
|
|
38
|
-
heartbeat: [],
|
|
39
|
-
# triggers on EVERY heartbeat call, every 10 seconds
|
|
40
|
-
beat: []
|
|
41
|
-
},
|
|
42
|
-
dead_max_jobs: 10_000,
|
|
43
|
-
dead_timeout_in_seconds: 180 * 24 * 60 * 60, # 6 months
|
|
44
|
-
reloader: proc { |&block| block.call }
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
FAKE_INFO = {
|
|
48
|
-
"redis_version" => "9.9.9",
|
|
49
|
-
"uptime_in_days" => "9999",
|
|
50
|
-
"connected_clients" => "9999",
|
|
51
|
-
"used_memory_human" => "9P",
|
|
52
|
-
"used_memory_peak_human" => "9P"
|
|
53
|
-
}
|
|
54
|
-
|
|
55
46
|
def self.❨╯°□°❩╯︵┻━┻
|
|
56
|
-
puts "
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# config.concurrency = 5
|
|
60
|
-
def self.concurrency=(val)
|
|
61
|
-
self[:concurrency] = Integer(val)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# config.queues = %w( high default low ) # strict
|
|
65
|
-
# config.queues = %w( high,3 default,2 low,1 ) # weighted
|
|
66
|
-
# config.queues = %w( feature1,1 feature2,1 feature3,1 ) # random
|
|
67
|
-
#
|
|
68
|
-
# With weighted priority, queue will be checked first (weight / total) of the time.
|
|
69
|
-
# high will be checked first (3/6) or 50% of the time.
|
|
70
|
-
# I'd recommend setting weights between 1-10. Weights in the hundreds or thousands
|
|
71
|
-
# are ridiculous and unnecessarily expensive. You can get random queue ordering
|
|
72
|
-
# by explicitly setting all weights to 1.
|
|
73
|
-
def self.queues=(val)
|
|
74
|
-
self[:queues] = Array(val).each_with_object([]) do |qstr, memo|
|
|
75
|
-
name, weight = qstr.split(",")
|
|
76
|
-
self[:strict] = false if weight.to_i > 0
|
|
77
|
-
[weight.to_i, 1].max.times do
|
|
78
|
-
memo << name
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
### Private APIs
|
|
84
|
-
def self.default_error_handler(ex, ctx)
|
|
85
|
-
logger.warn(dump_json(ctx)) unless ctx.empty?
|
|
86
|
-
logger.warn("#{ex.class.name}: #{ex.message}")
|
|
87
|
-
logger.warn(ex.backtrace.join("\n")) unless ex.backtrace.nil?
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# DEFAULT_ERROR_HANDLER is a constant that allows the default error handler to
|
|
91
|
-
# be referenced. It must be defined here, after the default_error_handler
|
|
92
|
-
# method is defined.
|
|
93
|
-
DEFAULT_ERROR_HANDLER = method(:default_error_handler)
|
|
94
|
-
|
|
95
|
-
@config = DEFAULTS.dup
|
|
96
|
-
def self.options
|
|
97
|
-
logger.warn "`config.options[:key] = value` is deprecated, use `config[:key] = value`: #{caller(1..2)}"
|
|
98
|
-
@config
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def self.options=(opts)
|
|
102
|
-
logger.warn "config.options = hash` is deprecated, use `config.merge!(hash)`: #{caller(1..2)}"
|
|
103
|
-
@config = opts
|
|
47
|
+
puts "Take a deep breath and count to ten..."
|
|
104
48
|
end
|
|
105
49
|
|
|
106
|
-
def self.
|
|
107
|
-
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def self.[]=(key, val)
|
|
111
|
-
@config[key] = val
|
|
112
|
-
end
|
|
50
|
+
def self.testing!(mode = :fake, &block)
|
|
51
|
+
raise "Unknown testing mode: #{mode}" unless %i[fake disable inline].include?(mode)
|
|
113
52
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def self.fetch(*args, &block)
|
|
119
|
-
@config.fetch(*args, &block)
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def self.handle_exception(ex, ctx = {})
|
|
123
|
-
self[:error_handlers].each do |handler|
|
|
124
|
-
handler.call(ex, ctx)
|
|
125
|
-
rescue => ex
|
|
126
|
-
logger.error "!!! ERROR HANDLER THREW AN ERROR !!!"
|
|
127
|
-
logger.error ex
|
|
128
|
-
logger.error ex.backtrace.join("\n") unless ex.backtrace.nil?
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
###
|
|
132
|
-
|
|
133
|
-
##
|
|
134
|
-
# Configuration for Sidekiq server, use like:
|
|
135
|
-
#
|
|
136
|
-
# Sidekiq.configure_server do |config|
|
|
137
|
-
# config.server_middleware do |chain|
|
|
138
|
-
# chain.add MyServerHook
|
|
139
|
-
# end
|
|
140
|
-
# end
|
|
141
|
-
def self.configure_server
|
|
142
|
-
yield self if server?
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
##
|
|
146
|
-
# Configuration for Sidekiq client, use like:
|
|
147
|
-
#
|
|
148
|
-
# Sidekiq.configure_client do |config|
|
|
149
|
-
# config.redis = { size: 1, url: 'redis://myhost:8877/0' }
|
|
150
|
-
# end
|
|
151
|
-
def self.configure_client
|
|
152
|
-
yield self unless server?
|
|
53
|
+
require "sidekiq/test_api"
|
|
54
|
+
Sidekiq::Testing.__set_test_mode(mode, &block)
|
|
153
55
|
end
|
|
154
56
|
|
|
155
57
|
def self.server?
|
|
156
58
|
defined?(Sidekiq::CLI)
|
|
157
59
|
end
|
|
158
60
|
|
|
159
|
-
def self.
|
|
160
|
-
|
|
161
|
-
redis_pool.with do |conn|
|
|
162
|
-
retryable = true
|
|
163
|
-
begin
|
|
164
|
-
yield conn
|
|
165
|
-
rescue RedisConnection.adapter::BaseError => ex
|
|
166
|
-
# 2550 Failover can cause the server to become a replica, need
|
|
167
|
-
# to disconnect and reopen the socket to get back to the primary.
|
|
168
|
-
# 4495 Use the same logic if we have a "Not enough replicas" error from the primary
|
|
169
|
-
# 4985 Use the same logic when a blocking command is force-unblocked
|
|
170
|
-
# The same retry logic is also used in client.rb
|
|
171
|
-
if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
|
|
172
|
-
conn.disconnect!
|
|
173
|
-
retryable = false
|
|
174
|
-
retry
|
|
175
|
-
end
|
|
176
|
-
raise
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def self.redis_info
|
|
182
|
-
redis do |conn|
|
|
183
|
-
# admin commands can't go through redis-namespace starting
|
|
184
|
-
# in redis-namespace 2.0
|
|
185
|
-
if conn.respond_to?(:namespace)
|
|
186
|
-
conn.redis.info
|
|
187
|
-
else
|
|
188
|
-
conn.info
|
|
189
|
-
end
|
|
190
|
-
rescue RedisConnection.adapter::CommandError => ex
|
|
191
|
-
# 2850 return fake version when INFO command has (probably) been renamed
|
|
192
|
-
raise unless /unknown command/.match?(ex.message)
|
|
193
|
-
FAKE_INFO
|
|
194
|
-
end
|
|
61
|
+
def self.load_json(string)
|
|
62
|
+
JSON.parse(string)
|
|
195
63
|
end
|
|
196
64
|
|
|
197
|
-
def self.
|
|
198
|
-
|
|
65
|
+
def self.dump_json(object)
|
|
66
|
+
JSON.generate(object)
|
|
199
67
|
end
|
|
200
68
|
|
|
201
|
-
def self.
|
|
202
|
-
|
|
203
|
-
hash
|
|
204
|
-
else
|
|
205
|
-
RedisConnection.create(hash)
|
|
206
|
-
end
|
|
69
|
+
def self.pro?
|
|
70
|
+
defined?(Sidekiq::Pro)
|
|
207
71
|
end
|
|
208
72
|
|
|
209
|
-
def self.
|
|
210
|
-
|
|
211
|
-
yield @client_chain if block_given?
|
|
212
|
-
@client_chain
|
|
73
|
+
def self.ent?
|
|
74
|
+
defined?(Sidekiq::Enterprise)
|
|
213
75
|
end
|
|
214
76
|
|
|
215
|
-
def self.
|
|
216
|
-
|
|
217
|
-
yield @server_chain if block_given?
|
|
218
|
-
@server_chain
|
|
77
|
+
def self.redis_pool
|
|
78
|
+
(Thread.current[:sidekiq_capsule] || default_configuration).redis_pool
|
|
219
79
|
end
|
|
220
80
|
|
|
221
|
-
def self.
|
|
222
|
-
|
|
81
|
+
def self.redis(&block)
|
|
82
|
+
(Thread.current[:sidekiq_capsule] || default_configuration).redis(&block)
|
|
223
83
|
end
|
|
224
84
|
|
|
225
|
-
def self.
|
|
226
|
-
|
|
85
|
+
def self.strict_args!(mode = :raise)
|
|
86
|
+
Sidekiq::Config::DEFAULTS[:on_complex_arguments] = mode
|
|
227
87
|
end
|
|
228
88
|
|
|
229
89
|
def self.default_job_options=(hash)
|
|
230
90
|
@default_job_options = default_job_options.merge(hash.transform_keys(&:to_s))
|
|
231
91
|
end
|
|
232
92
|
|
|
233
|
-
def self.default_worker_options # deprecated
|
|
234
|
-
@default_job_options ||= {"retry" => true, "queue" => "default"}
|
|
235
|
-
end
|
|
236
|
-
|
|
237
93
|
def self.default_job_options
|
|
238
94
|
@default_job_options ||= {"retry" => true, "queue" => "default"}
|
|
239
95
|
end
|
|
240
96
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
# the job dies. It's the notification to your application
|
|
244
|
-
# that this job will not succeed without manual intervention.
|
|
245
|
-
#
|
|
246
|
-
# Sidekiq.configure_server do |config|
|
|
247
|
-
# config.death_handlers << ->(job, ex) do
|
|
248
|
-
# end
|
|
249
|
-
# end
|
|
250
|
-
def self.death_handlers
|
|
251
|
-
self[:death_handlers]
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
def self.load_json(string)
|
|
255
|
-
JSON.parse(string)
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
def self.dump_json(object)
|
|
259
|
-
JSON.generate(object)
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
def self.log_formatter
|
|
263
|
-
@log_formatter ||= if ENV["DYNO"]
|
|
264
|
-
Sidekiq::Logger::Formatters::WithoutTimestamp.new
|
|
265
|
-
else
|
|
266
|
-
Sidekiq::Logger::Formatters::Pretty.new
|
|
267
|
-
end
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
def self.log_formatter=(log_formatter)
|
|
271
|
-
@log_formatter = log_formatter
|
|
272
|
-
logger.formatter = log_formatter
|
|
97
|
+
def self.default_configuration
|
|
98
|
+
@config ||= Sidekiq::Config.new
|
|
273
99
|
end
|
|
274
100
|
|
|
275
101
|
def self.logger
|
|
276
|
-
|
|
102
|
+
default_configuration.logger
|
|
277
103
|
end
|
|
278
104
|
|
|
279
|
-
def self.
|
|
280
|
-
|
|
281
|
-
self.logger.level = Logger::FATAL
|
|
282
|
-
return self.logger
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
logger.extend(Sidekiq::LoggingUtils)
|
|
286
|
-
|
|
287
|
-
@logger = logger
|
|
105
|
+
def self.loader
|
|
106
|
+
@loader ||= Loader.new
|
|
288
107
|
end
|
|
289
108
|
|
|
290
|
-
def self.
|
|
291
|
-
|
|
109
|
+
def self.configure_server(&block)
|
|
110
|
+
(@config_blocks ||= []) << block
|
|
111
|
+
yield default_configuration if server?
|
|
292
112
|
end
|
|
293
113
|
|
|
294
|
-
def self.
|
|
295
|
-
|
|
114
|
+
def self.freeze!
|
|
115
|
+
@frozen = true
|
|
116
|
+
@config_blocks = nil
|
|
117
|
+
default_configuration.freeze!
|
|
296
118
|
end
|
|
297
119
|
|
|
298
|
-
#
|
|
299
|
-
#
|
|
300
|
-
#
|
|
120
|
+
# Creates a Sidekiq::Config instance that is more tuned for embedding
|
|
121
|
+
# within an arbitrary Ruby process. Notably it reduces concurrency by
|
|
122
|
+
# default so there is less contention for CPU time with other threads.
|
|
301
123
|
#
|
|
302
|
-
#
|
|
303
|
-
|
|
304
|
-
self[:average_scheduled_poll_interval] = interval
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
# Register a proc to handle any error which occurs within the Sidekiq process.
|
|
308
|
-
#
|
|
309
|
-
# Sidekiq.configure_server do |config|
|
|
310
|
-
# config.error_handlers << proc {|ex,ctx_hash| MyErrorService.notify(ex, ctx_hash) }
|
|
124
|
+
# instance = Sidekiq.configure_embed do |config|
|
|
125
|
+
# config.queues = %w[critical default low]
|
|
311
126
|
# end
|
|
127
|
+
# instance.run
|
|
128
|
+
# sleep 10
|
|
129
|
+
# instance.stop
|
|
312
130
|
#
|
|
313
|
-
#
|
|
314
|
-
|
|
315
|
-
self[:error_handlers]
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
# Register a block to run at a point in the Sidekiq lifecycle.
|
|
319
|
-
# :startup, :quiet or :shutdown are valid events.
|
|
131
|
+
# NB: it is really easy to overload a Ruby process with threads due to the GIL.
|
|
132
|
+
# I do not recommend setting concurrency higher than 2-3.
|
|
320
133
|
#
|
|
321
|
-
#
|
|
322
|
-
#
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
134
|
+
# NB: Sidekiq only supports one instance in memory. You will get undefined behavior
|
|
135
|
+
# if you try to embed Sidekiq twice in the same process.
|
|
136
|
+
def self.configure_embed(&block)
|
|
137
|
+
raise "Sidekiq global configuration is frozen, you must create all embedded instances BEFORE calling `run`" if @frozen
|
|
138
|
+
|
|
139
|
+
require "sidekiq/embedded"
|
|
140
|
+
cfg = default_configuration
|
|
141
|
+
cfg.concurrency = 2
|
|
142
|
+
@config_blocks&.each { |block| block.call(cfg) }
|
|
143
|
+
yield cfg
|
|
144
|
+
|
|
145
|
+
Sidekiq::Embedded.new(cfg)
|
|
330
146
|
end
|
|
331
147
|
|
|
332
|
-
def self.
|
|
333
|
-
|
|
148
|
+
def self.configure_client
|
|
149
|
+
yield default_configuration unless server?
|
|
334
150
|
end
|
|
335
151
|
|
|
336
152
|
# We are shutting down Sidekiq but what about threads that
|