sidekiq 6.5.8 → 7.3.10
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 +359 -15
- data/README.md +43 -35
- data/bin/multi_queue_bench +271 -0
- data/bin/sidekiq +3 -8
- data/bin/sidekiqload +213 -118
- data/bin/sidekiqmon +3 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +75 -0
- data/lib/generators/sidekiq/job_generator.rb +2 -0
- data/lib/sidekiq/api.rb +243 -162
- data/lib/sidekiq/capsule.rb +132 -0
- data/lib/sidekiq/cli.rb +61 -75
- data/lib/sidekiq/client.rb +88 -39
- data/lib/sidekiq/component.rb +26 -1
- data/lib/sidekiq/config.rb +311 -0
- data/lib/sidekiq/deploy.rb +64 -0
- data/lib/sidekiq/embedded.rb +63 -0
- data/lib/sidekiq/fetch.rb +12 -14
- data/lib/sidekiq/iterable_job.rb +55 -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 +294 -0
- data/lib/sidekiq/job.rb +382 -10
- data/lib/sidekiq/job_logger.rb +8 -7
- data/lib/sidekiq/job_retry.rb +42 -19
- data/lib/sidekiq/job_util.rb +53 -15
- data/lib/sidekiq/launcher.rb +71 -65
- data/lib/sidekiq/logger.rb +2 -27
- data/lib/sidekiq/manager.rb +9 -11
- data/lib/sidekiq/metrics/query.rb +10 -5
- data/lib/sidekiq/metrics/shared.rb +21 -9
- data/lib/sidekiq/metrics/tracking.rb +40 -26
- data/lib/sidekiq/middleware/chain.rb +19 -18
- data/lib/sidekiq/middleware/current_attributes.rb +85 -20
- data/lib/sidekiq/middleware/i18n.rb +2 -0
- data/lib/sidekiq/middleware/modules.rb +2 -0
- data/lib/sidekiq/monitor.rb +19 -5
- data/lib/sidekiq/paginator.rb +10 -4
- data/lib/sidekiq/processor.rb +62 -57
- data/lib/sidekiq/rails.rb +32 -16
- data/lib/sidekiq/redis_client_adapter.rb +31 -71
- data/lib/sidekiq/redis_connection.rb +54 -114
- data/lib/sidekiq/ring_buffer.rb +3 -0
- data/lib/sidekiq/scheduled.rb +22 -23
- data/lib/sidekiq/systemd.rb +2 -0
- data/lib/sidekiq/testing.rb +37 -46
- data/lib/sidekiq/transaction_aware_client.rb +23 -9
- data/lib/sidekiq/version.rb +6 -1
- data/lib/sidekiq/web/action.rb +29 -7
- data/lib/sidekiq/web/application.rb +84 -30
- data/lib/sidekiq/web/csrf_protection.rb +10 -7
- data/lib/sidekiq/web/helpers.rb +113 -52
- data/lib/sidekiq/web/router.rb +5 -2
- data/lib/sidekiq/web.rb +70 -17
- data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/lib/sidekiq.rb +78 -274
- data/sidekiq.gemspec +13 -10
- data/web/assets/javascripts/application.js +45 -0
- data/web/assets/javascripts/base-charts.js +106 -0
- data/web/assets/javascripts/dashboard-charts.js +194 -0
- data/web/assets/javascripts/dashboard.js +17 -233
- data/web/assets/javascripts/metrics.js +151 -115
- data/web/assets/stylesheets/application-dark.css +4 -0
- data/web/assets/stylesheets/application-rtl.css +10 -89
- data/web/assets/stylesheets/application.css +56 -296
- data/web/locales/ar.yml +70 -70
- data/web/locales/cs.yml +62 -62
- data/web/locales/da.yml +60 -53
- data/web/locales/de.yml +65 -65
- data/web/locales/el.yml +2 -7
- data/web/locales/en.yml +81 -71
- data/web/locales/es.yml +68 -68
- data/web/locales/fa.yml +65 -65
- data/web/locales/fr.yml +80 -67
- data/web/locales/gd.yml +98 -0
- data/web/locales/he.yml +65 -64
- data/web/locales/hi.yml +59 -59
- data/web/locales/it.yml +85 -54
- data/web/locales/ja.yml +67 -70
- data/web/locales/ko.yml +52 -52
- data/web/locales/lt.yml +66 -66
- data/web/locales/nb.yml +61 -61
- data/web/locales/nl.yml +52 -52
- data/web/locales/pl.yml +45 -45
- data/web/locales/pt-br.yml +78 -69
- data/web/locales/pt.yml +51 -51
- data/web/locales/ru.yml +67 -66
- data/web/locales/sv.yml +53 -53
- data/web/locales/ta.yml +60 -60
- data/web/locales/tr.yml +100 -0
- data/web/locales/uk.yml +85 -61
- data/web/locales/ur.yml +64 -64
- data/web/locales/vi.yml +67 -67
- data/web/locales/zh-cn.yml +20 -19
- data/web/locales/zh-tw.yml +10 -2
- data/web/views/_footer.erb +16 -2
- data/web/views/_job_info.erb +18 -2
- data/web/views/_metrics_period_select.erb +12 -0
- data/web/views/_paging.erb +2 -0
- data/web/views/_poll_link.erb +1 -1
- data/web/views/_summary.erb +7 -7
- data/web/views/busy.erb +46 -35
- data/web/views/dashboard.erb +32 -8
- data/web/views/filtering.erb +6 -0
- data/web/views/layout.erb +6 -6
- data/web/views/metrics.erb +47 -26
- data/web/views/metrics_for_job.erb +43 -71
- data/web/views/morgue.erb +7 -11
- data/web/views/queue.erb +11 -15
- data/web/views/queues.erb +9 -3
- data/web/views/retries.erb +5 -9
- data/web/views/scheduled.erb +12 -13
- metadata +81 -38
- 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/worker.rb +0 -370
- data/web/assets/javascripts/graph.js +0 -16
- /data/{LICENSE → LICENSE.txt} +0 -0
data/lib/sidekiq.rb
CHANGED
|
@@ -1,15 +1,40 @@
|
|
|
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 2.7.0." if RUBY_PLATFORM != "java" && Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.7.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"
|
|
7
32
|
require "sidekiq/client"
|
|
8
33
|
require "sidekiq/transaction_aware_client"
|
|
9
|
-
require "sidekiq/worker"
|
|
10
34
|
require "sidekiq/job"
|
|
11
|
-
require "sidekiq/
|
|
12
|
-
require "sidekiq/
|
|
35
|
+
require "sidekiq/iterable_job"
|
|
36
|
+
require "sidekiq/worker_compatibility_alias"
|
|
37
|
+
require "sidekiq/redis_client_adapter"
|
|
13
38
|
|
|
14
39
|
require "json"
|
|
15
40
|
|
|
@@ -17,320 +42,99 @@ module Sidekiq
|
|
|
17
42
|
NAME = "Sidekiq"
|
|
18
43
|
LICENSE = "See LICENSE and the LGPL-3.0 for licensing details."
|
|
19
44
|
|
|
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
45
|
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
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def self.[](key)
|
|
107
|
-
@config[key]
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def self.[]=(key, val)
|
|
111
|
-
@config[key] = val
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def self.merge!(hash)
|
|
115
|
-
@config.merge!(hash)
|
|
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?
|
|
46
|
+
puts "Take a deep breath and count to ten..."
|
|
153
47
|
end
|
|
154
48
|
|
|
155
49
|
def self.server?
|
|
156
50
|
defined?(Sidekiq::CLI)
|
|
157
51
|
end
|
|
158
52
|
|
|
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
|
|
53
|
+
def self.load_json(string)
|
|
54
|
+
JSON.parse(string)
|
|
195
55
|
end
|
|
196
56
|
|
|
197
|
-
def self.
|
|
198
|
-
|
|
57
|
+
def self.dump_json(object)
|
|
58
|
+
JSON.generate(object)
|
|
199
59
|
end
|
|
200
60
|
|
|
201
|
-
def self.
|
|
202
|
-
|
|
203
|
-
hash
|
|
204
|
-
else
|
|
205
|
-
RedisConnection.create(hash)
|
|
206
|
-
end
|
|
61
|
+
def self.pro?
|
|
62
|
+
defined?(Sidekiq::Pro)
|
|
207
63
|
end
|
|
208
64
|
|
|
209
|
-
def self.
|
|
210
|
-
|
|
211
|
-
yield @client_chain if block_given?
|
|
212
|
-
@client_chain
|
|
65
|
+
def self.ent?
|
|
66
|
+
defined?(Sidekiq::Enterprise)
|
|
213
67
|
end
|
|
214
68
|
|
|
215
|
-
def self.
|
|
216
|
-
|
|
217
|
-
yield @server_chain if block_given?
|
|
218
|
-
@server_chain
|
|
69
|
+
def self.redis_pool
|
|
70
|
+
(Thread.current[:sidekiq_capsule] || default_configuration).redis_pool
|
|
219
71
|
end
|
|
220
72
|
|
|
221
|
-
def self.
|
|
222
|
-
|
|
73
|
+
def self.redis(&block)
|
|
74
|
+
(Thread.current[:sidekiq_capsule] || default_configuration).redis(&block)
|
|
223
75
|
end
|
|
224
76
|
|
|
225
|
-
def self.
|
|
226
|
-
|
|
77
|
+
def self.strict_args!(mode = :raise)
|
|
78
|
+
Sidekiq::Config::DEFAULTS[:on_complex_arguments] = mode
|
|
227
79
|
end
|
|
228
80
|
|
|
229
81
|
def self.default_job_options=(hash)
|
|
230
82
|
@default_job_options = default_job_options.merge(hash.transform_keys(&:to_s))
|
|
231
83
|
end
|
|
232
84
|
|
|
233
|
-
def self.default_worker_options # deprecated
|
|
234
|
-
@default_job_options ||= {"retry" => true, "queue" => "default"}
|
|
235
|
-
end
|
|
236
|
-
|
|
237
85
|
def self.default_job_options
|
|
238
86
|
@default_job_options ||= {"retry" => true, "queue" => "default"}
|
|
239
87
|
end
|
|
240
88
|
|
|
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
|
|
89
|
+
def self.default_configuration
|
|
90
|
+
@config ||= Sidekiq::Config.new
|
|
273
91
|
end
|
|
274
92
|
|
|
275
93
|
def self.logger
|
|
276
|
-
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
def self.logger=(logger)
|
|
280
|
-
if logger.nil?
|
|
281
|
-
self.logger.level = Logger::FATAL
|
|
282
|
-
return self.logger
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
logger.extend(Sidekiq::LoggingUtils)
|
|
286
|
-
|
|
287
|
-
@logger = logger
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
def self.pro?
|
|
291
|
-
defined?(Sidekiq::Pro)
|
|
94
|
+
default_configuration.logger
|
|
292
95
|
end
|
|
293
96
|
|
|
294
|
-
def self.
|
|
295
|
-
|
|
97
|
+
def self.configure_server(&block)
|
|
98
|
+
(@config_blocks ||= []) << block
|
|
99
|
+
yield default_configuration if server?
|
|
296
100
|
end
|
|
297
101
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
# See sidekiq/scheduled.rb for an in-depth explanation of this value
|
|
303
|
-
def self.average_scheduled_poll_interval=(interval)
|
|
304
|
-
self[:average_scheduled_poll_interval] = interval
|
|
102
|
+
def self.freeze!
|
|
103
|
+
@frozen = true
|
|
104
|
+
@config_blocks = nil
|
|
105
|
+
default_configuration.freeze!
|
|
305
106
|
end
|
|
306
107
|
|
|
307
|
-
#
|
|
108
|
+
# Creates a Sidekiq::Config instance that is more tuned for embedding
|
|
109
|
+
# within an arbitrary Ruby process. Notably it reduces concurrency by
|
|
110
|
+
# default so there is less contention for CPU time with other threads.
|
|
308
111
|
#
|
|
309
|
-
# Sidekiq.
|
|
310
|
-
# config.
|
|
112
|
+
# instance = Sidekiq.configure_embed do |config|
|
|
113
|
+
# config.queues = %w[critical default low]
|
|
311
114
|
# end
|
|
115
|
+
# instance.run
|
|
116
|
+
# sleep 10
|
|
117
|
+
# instance.stop
|
|
312
118
|
#
|
|
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.
|
|
119
|
+
# NB: it is really easy to overload a Ruby process with threads due to the GIL.
|
|
120
|
+
# I do not recommend setting concurrency higher than 2-3.
|
|
320
121
|
#
|
|
321
|
-
#
|
|
322
|
-
#
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
122
|
+
# NB: Sidekiq only supports one instance in memory. You will get undefined behavior
|
|
123
|
+
# if you try to embed Sidekiq twice in the same process.
|
|
124
|
+
def self.configure_embed(&block)
|
|
125
|
+
raise "Sidekiq global configuration is frozen, you must create all embedded instances BEFORE calling `run`" if @frozen
|
|
126
|
+
|
|
127
|
+
require "sidekiq/embedded"
|
|
128
|
+
cfg = default_configuration
|
|
129
|
+
cfg.concurrency = 2
|
|
130
|
+
@config_blocks&.each { |block| block.call(cfg) }
|
|
131
|
+
yield cfg
|
|
132
|
+
|
|
133
|
+
Sidekiq::Embedded.new(cfg)
|
|
330
134
|
end
|
|
331
135
|
|
|
332
|
-
def self.
|
|
333
|
-
|
|
136
|
+
def self.configure_client
|
|
137
|
+
yield default_configuration unless server?
|
|
334
138
|
end
|
|
335
139
|
|
|
336
140
|
# We are shutting down Sidekiq but what about threads that
|
data/sidekiq.gemspec
CHANGED
|
@@ -2,27 +2,30 @@ require_relative "lib/sidekiq/version"
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |gem|
|
|
4
4
|
gem.authors = ["Mike Perham"]
|
|
5
|
-
gem.email = ["
|
|
5
|
+
gem.email = ["info@contribsys.com"]
|
|
6
6
|
gem.summary = "Simple, efficient background processing for Ruby"
|
|
7
7
|
gem.description = "Simple, efficient background processing for Ruby."
|
|
8
8
|
gem.homepage = "https://sidekiq.org"
|
|
9
9
|
gem.license = "LGPL-3.0"
|
|
10
10
|
|
|
11
11
|
gem.executables = ["sidekiq", "sidekiqmon"]
|
|
12
|
-
gem.files = [
|
|
12
|
+
gem.files = %w[sidekiq.gemspec README.md Changes.md LICENSE.txt] + `git ls-files | grep -E '^(bin|lib|web)'`.split("\n")
|
|
13
13
|
gem.name = "sidekiq"
|
|
14
14
|
gem.version = Sidekiq::VERSION
|
|
15
|
-
gem.required_ruby_version = ">= 2.
|
|
15
|
+
gem.required_ruby_version = ">= 2.7.0"
|
|
16
16
|
|
|
17
17
|
gem.metadata = {
|
|
18
18
|
"homepage_uri" => "https://sidekiq.org",
|
|
19
|
-
"bug_tracker_uri" => "https://github.com/
|
|
20
|
-
"documentation_uri" => "https://github.com/
|
|
21
|
-
"changelog_uri" => "https://github.com/
|
|
22
|
-
"source_code_uri" => "https://github.com/
|
|
19
|
+
"bug_tracker_uri" => "https://github.com/sidekiq/sidekiq/issues",
|
|
20
|
+
"documentation_uri" => "https://github.com/sidekiq/sidekiq/wiki",
|
|
21
|
+
"changelog_uri" => "https://github.com/sidekiq/sidekiq/blob/main/Changes.md",
|
|
22
|
+
"source_code_uri" => "https://github.com/sidekiq/sidekiq",
|
|
23
|
+
"rubygems_mfa_required" => "true"
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
gem.add_dependency "redis",
|
|
26
|
-
gem.add_dependency "connection_pool",
|
|
27
|
-
gem.add_dependency "rack", "
|
|
26
|
+
gem.add_dependency "redis-client", ">= 0.23.0", "<1"
|
|
27
|
+
gem.add_dependency "connection_pool", ">= 2.3.0", "<3"
|
|
28
|
+
gem.add_dependency "rack", ">= 2.2.4", "<3.3"
|
|
29
|
+
gem.add_dependency "logger"
|
|
30
|
+
gem.add_dependency "base64"
|
|
28
31
|
end
|
|
@@ -31,7 +31,10 @@ function addListeners() {
|
|
|
31
31
|
node.addEventListener("click", addDataToggleListeners)
|
|
32
32
|
})
|
|
33
33
|
|
|
34
|
+
addShiftClickListeners()
|
|
34
35
|
updateFuzzyTimes();
|
|
36
|
+
updateNumbers();
|
|
37
|
+
updateProgressBars();
|
|
35
38
|
setLivePollFromUrl();
|
|
36
39
|
|
|
37
40
|
var buttons = document.querySelectorAll(".live-poll");
|
|
@@ -45,6 +48,8 @@ function addListeners() {
|
|
|
45
48
|
scheduleLivePoll();
|
|
46
49
|
}
|
|
47
50
|
}
|
|
51
|
+
|
|
52
|
+
document.getElementById("locale-select").addEventListener("change", updateLocale);
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
function addPollingListeners(_event) {
|
|
@@ -71,6 +76,23 @@ function addDataToggleListeners(event) {
|
|
|
71
76
|
}
|
|
72
77
|
}
|
|
73
78
|
|
|
79
|
+
function addShiftClickListeners() {
|
|
80
|
+
let checkboxes = Array.from(document.querySelectorAll(".shift_clickable"));
|
|
81
|
+
let lastChecked = null;
|
|
82
|
+
checkboxes.forEach(checkbox => {
|
|
83
|
+
checkbox.addEventListener("click", (e) => {
|
|
84
|
+
if (e.shiftKey && lastChecked) {
|
|
85
|
+
let myIndex = checkboxes.indexOf(checkbox);
|
|
86
|
+
let lastIndex = checkboxes.indexOf(lastChecked);
|
|
87
|
+
let [min, max] = [myIndex, lastIndex].sort();
|
|
88
|
+
let newState = checkbox.checked;
|
|
89
|
+
checkboxes.slice(min, max).forEach(c => c.checked = newState);
|
|
90
|
+
}
|
|
91
|
+
lastChecked = checkbox;
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
74
96
|
function updateFuzzyTimes() {
|
|
75
97
|
var locale = document.body.getAttribute("data-locale");
|
|
76
98
|
var parts = locale.split('-');
|
|
@@ -84,6 +106,20 @@ function updateFuzzyTimes() {
|
|
|
84
106
|
t.cancel();
|
|
85
107
|
}
|
|
86
108
|
|
|
109
|
+
function updateNumbers() {
|
|
110
|
+
document.querySelectorAll("[data-nwp]").forEach(node => {
|
|
111
|
+
let number = parseFloat(node.textContent);
|
|
112
|
+
let precision = parseInt(node.dataset["nwp"] || 0);
|
|
113
|
+
if (typeof number === "number") {
|
|
114
|
+
let formatted = number.toLocaleString(undefined, {
|
|
115
|
+
minimumFractionDigits: precision,
|
|
116
|
+
maximumFractionDigits: precision,
|
|
117
|
+
});
|
|
118
|
+
node.textContent = formatted;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
87
123
|
function setLivePollFromUrl() {
|
|
88
124
|
var url_params = new URL(window.location.href).searchParams
|
|
89
125
|
|
|
@@ -122,6 +158,7 @@ function checkResponse(resp) {
|
|
|
122
158
|
|
|
123
159
|
function scheduleLivePoll() {
|
|
124
160
|
let ti = parseInt(localStorage.sidekiqTimeInterval) || 5000;
|
|
161
|
+
if (ti < 2000) { ti = 2000 }
|
|
125
162
|
livePollTimer = setTimeout(livePollCallback, ti);
|
|
126
163
|
}
|
|
127
164
|
|
|
@@ -141,3 +178,11 @@ function replacePage(text) {
|
|
|
141
178
|
function showError(error) {
|
|
142
179
|
console.error(error)
|
|
143
180
|
}
|
|
181
|
+
|
|
182
|
+
function updateLocale(event) {
|
|
183
|
+
event.target.form.submit();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function updateProgressBars() {
|
|
187
|
+
document.querySelectorAll('.progress-bar').forEach(bar => { bar.style.width = bar.dataset.width + "%"})
|
|
188
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
2
|
+
Chart.defaults.borderColor = "#333";
|
|
3
|
+
Chart.defaults.color = "#aaa";
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
class Colors {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.assignments = {};
|
|
9
|
+
this.success = "#006f68";
|
|
10
|
+
this.failure = "#af0014";
|
|
11
|
+
this.fallback = "#999";
|
|
12
|
+
this.primary = "#537bc4";
|
|
13
|
+
this.available = [
|
|
14
|
+
// Colors taken from https://www.chartjs.org/docs/latest/samples/utils.html
|
|
15
|
+
"#537bc4",
|
|
16
|
+
"#4dc9f6",
|
|
17
|
+
"#f67019",
|
|
18
|
+
"#f53794",
|
|
19
|
+
"#acc236",
|
|
20
|
+
"#166a8f",
|
|
21
|
+
"#00a950",
|
|
22
|
+
"#58595b",
|
|
23
|
+
"#8549ba",
|
|
24
|
+
"#991b1b",
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
checkOut(assignee) {
|
|
29
|
+
const color =
|
|
30
|
+
this.assignments[assignee] || this.available.shift() || this.fallback;
|
|
31
|
+
this.assignments[assignee] = color;
|
|
32
|
+
return color;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
checkIn(assignee) {
|
|
36
|
+
const color = this.assignments[assignee];
|
|
37
|
+
delete this.assignments[assignee];
|
|
38
|
+
|
|
39
|
+
if (color && color != this.fallback) {
|
|
40
|
+
this.available.unshift(color);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class BaseChart {
|
|
46
|
+
constructor(el, options) {
|
|
47
|
+
this.el = el;
|
|
48
|
+
this.options = options;
|
|
49
|
+
this.colors = new Colors();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
init() {
|
|
53
|
+
this.chart = new Chart(this.el, {
|
|
54
|
+
type: this.options.chartType,
|
|
55
|
+
data: { labels: this.options.labels, datasets: this.datasets },
|
|
56
|
+
options: this.chartOptions,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
update() {
|
|
61
|
+
this.chart.options = this.chartOptions;
|
|
62
|
+
this.chart.update();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get chartOptions() {
|
|
66
|
+
let chartOptions = {
|
|
67
|
+
interaction: {
|
|
68
|
+
mode: "nearest",
|
|
69
|
+
axis: "x",
|
|
70
|
+
intersect: false,
|
|
71
|
+
},
|
|
72
|
+
scales: {
|
|
73
|
+
x: {
|
|
74
|
+
ticks: {
|
|
75
|
+
autoSkipPadding: 10,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
plugins: {
|
|
80
|
+
legend: {
|
|
81
|
+
display: false,
|
|
82
|
+
},
|
|
83
|
+
annotation: {
|
|
84
|
+
annotations: {},
|
|
85
|
+
},
|
|
86
|
+
tooltip: {
|
|
87
|
+
animation: false,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
if (this.options.marks) {
|
|
93
|
+
this.options.marks.forEach(([bucket, label], i) => {
|
|
94
|
+
chartOptions.plugins.annotation.annotations[`deploy-${i}`] = {
|
|
95
|
+
type: "line",
|
|
96
|
+
xMin: bucket,
|
|
97
|
+
xMax: bucket,
|
|
98
|
+
borderColor: "rgba(220, 38, 38, 0.4)",
|
|
99
|
+
borderWidth: 2,
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return chartOptions;
|
|
105
|
+
}
|
|
106
|
+
}
|