sidekiq 7.3.9 → 8.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changes.md +21 -4
- data/README.md +16 -13
- data/bin/sidekiqload +10 -10
- data/bin/webload +69 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +5 -5
- data/lib/sidekiq/api.rb +108 -36
- data/lib/sidekiq/capsule.rb +1 -1
- data/lib/sidekiq/cli.rb +15 -19
- data/lib/sidekiq/client.rb +13 -16
- data/lib/sidekiq/component.rb +30 -2
- data/lib/sidekiq/config.rb +18 -15
- data/lib/sidekiq/embedded.rb +1 -0
- data/lib/sidekiq/job/iterable.rb +2 -4
- data/lib/sidekiq/job_retry.rb +2 -2
- data/lib/sidekiq/job_util.rb +5 -1
- data/lib/sidekiq/launcher.rb +1 -1
- data/lib/sidekiq/logger.rb +6 -10
- data/lib/sidekiq/manager.rb +0 -1
- data/lib/sidekiq/metrics/query.rb +1 -3
- data/lib/sidekiq/middleware/current_attributes.rb +5 -17
- data/lib/sidekiq/paginator.rb +8 -1
- data/lib/sidekiq/processor.rb +21 -14
- data/lib/sidekiq/profiler.rb +59 -0
- data/lib/sidekiq/redis_client_adapter.rb +0 -1
- data/lib/sidekiq/testing.rb +2 -2
- data/lib/sidekiq/version.rb +2 -2
- data/lib/sidekiq/web/action.rb +101 -85
- data/lib/sidekiq/web/application.rb +339 -333
- data/lib/sidekiq/web/config.rb +116 -0
- data/lib/sidekiq/web/helpers.rb +40 -15
- data/lib/sidekiq/web/router.rb +60 -76
- data/lib/sidekiq/web.rb +51 -156
- data/sidekiq.gemspec +5 -4
- data/web/assets/javascripts/application.js +6 -13
- data/web/assets/javascripts/base-charts.js +30 -18
- data/web/assets/javascripts/metrics.js +1 -1
- data/web/assets/stylesheets/style.css +750 -0
- data/web/locales/ar.yml +1 -0
- data/web/locales/cs.yml +1 -0
- data/web/locales/da.yml +1 -0
- data/web/locales/de.yml +1 -0
- data/web/locales/el.yml +1 -0
- data/web/locales/en.yml +6 -0
- data/web/locales/es.yml +24 -2
- data/web/locales/fa.yml +1 -0
- data/web/locales/fr.yml +1 -0
- data/web/locales/gd.yml +1 -0
- data/web/locales/he.yml +1 -0
- data/web/locales/hi.yml +1 -0
- data/web/locales/it.yml +1 -0
- data/web/locales/ja.yml +1 -0
- data/web/locales/ko.yml +1 -0
- data/web/locales/lt.yml +1 -0
- data/web/locales/nb.yml +1 -0
- data/web/locales/nl.yml +1 -0
- data/web/locales/pl.yml +1 -0
- data/web/locales/{pt-br.yml → pt-BR.yml} +2 -1
- data/web/locales/pt.yml +1 -0
- data/web/locales/ru.yml +1 -0
- data/web/locales/sv.yml +1 -0
- data/web/locales/ta.yml +1 -0
- data/web/locales/tr.yml +1 -0
- data/web/locales/uk.yml +1 -0
- data/web/locales/ur.yml +1 -0
- data/web/locales/vi.yml +1 -0
- data/web/locales/{zh-cn.yml → zh-CN.yml} +85 -73
- data/web/locales/{zh-tw.yml → zh-TW.yml} +2 -1
- data/web/views/_footer.erb +31 -33
- data/web/views/_job_info.erb +91 -89
- data/web/views/_metrics_period_select.erb +1 -1
- data/web/views/_nav.erb +14 -21
- data/web/views/_paging.erb +23 -21
- data/web/views/_poll_link.erb +2 -2
- data/web/views/_summary.erb +16 -16
- data/web/views/busy.erb +124 -122
- data/web/views/dashboard.erb +61 -66
- data/web/views/dead.erb +31 -27
- data/web/views/filtering.erb +3 -3
- data/web/views/layout.erb +5 -21
- data/web/views/metrics.erb +83 -80
- data/web/views/metrics_for_job.erb +39 -42
- data/web/views/morgue.erb +61 -70
- data/web/views/profiles.erb +43 -0
- data/web/views/queue.erb +54 -52
- data/web/views/queues.erb +43 -41
- data/web/views/retries.erb +66 -75
- data/web/views/retry.erb +32 -27
- data/web/views/scheduled.erb +58 -54
- data/web/views/scheduled_job_info.erb +1 -1
- metadata +31 -18
- data/web/assets/stylesheets/application-dark.css +0 -147
- data/web/assets/stylesheets/application-rtl.css +0 -163
- data/web/assets/stylesheets/application.css +0 -759
- data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
- data/web/assets/stylesheets/bootstrap.css +0 -5
- data/web/views/_status.erb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7280923fe38a56b42bb33c639f9e0a365c1f585f928e8cdfd3ce06fe989fc7e
|
4
|
+
data.tar.gz: e8c6a01e393390f1a3ab3a48c04b3621efd5621c6b184240c3e8316602e24e9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 076a8f70170dfdc6ec78430ca41b182ed986453bdfbcfe24b7dd5ef1d8f2321b560cb94201c2f2c3a30c2ce42a31e38ce14151f0ebbee90bc97a2463b427747a
|
7
|
+
data.tar.gz: 8cc4828931a73da67ae40edb14508b82624781b9732280ce9e8db615c8bed1039b89b8b50db54c35bbffd4a010f904598be2bb4aa037228bbd4b4b325b2b9421
|
data/Changes.md
CHANGED
@@ -2,12 +2,29 @@
|
|
2
2
|
|
3
3
|
[Sidekiq Changes](https://github.com/sidekiq/sidekiq/blob/main/Changes.md) | [Sidekiq Pro Changes](https://github.com/sidekiq/sidekiq/blob/main/Pro-Changes.md) | [Sidekiq Enterprise Changes](https://github.com/sidekiq/sidekiq/blob/main/Ent-Changes.md)
|
4
4
|
|
5
|
-
|
5
|
+
HEAD / main
|
6
6
|
----------
|
7
7
|
|
8
|
-
-
|
9
|
-
-
|
10
|
-
|
8
|
+
- **WARNING** The underlying class name for Active Jobs has changed from `ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper` to `Sidekiq::ActiveJob::Wrapper`.
|
9
|
+
- **WARNING** The `created_at` and `enqueued_at` attributes are now stored as
|
10
|
+
integer milliseconds, rather than epoch floats. This is meant to avoid precision
|
11
|
+
issues with JSON and JavaScript's 53-bit Floats. Example:
|
12
|
+
`"created_at" => 1234567890.123456` -> `"created_at" => 1234567890123`.
|
13
|
+
- **NEW FEATURE** Job Profiling is now supported with [Vernier](https://vernier.prof)
|
14
|
+
which makes it really easy to performance tune your slow jobs.
|
15
|
+
The Web UI contains a new **Profiles** tab to view any collected profile data.
|
16
|
+
Please read the new [Profiling](https://github.com/sidekiq/sidekiq/wiki/Profiling) wiki page for details.
|
17
|
+
- CurrentAttribute support now uses `ActiveJob::Arguments` to serialize the context object, supporting Symbols and GlobalID.
|
18
|
+
The change should be backwards compatible. [#6510]
|
19
|
+
- Freshen up `Sidekiq::Web` to simplify the code and improve security [#6532]
|
20
|
+
The CSS has been rewritten from scratch to remove the Bootstrap framework.
|
21
|
+
- Default error logging has been modified to use Ruby's `Exception#detailed_message` and `#full_message` APIs.
|
22
|
+
- CI now runs against Redis, Dragonfly and Valkey.
|
23
|
+
- The Web UI's language picker now shows options in the native language
|
24
|
+
- Remove global variable usage within the codebase
|
25
|
+
- Adjust Sidekiq's default thread priority to -1 for a 50ms timeslice.
|
26
|
+
This can help avoid TimeoutErrors when Sidekiq is overloaded. [#6543]
|
27
|
+
- Support: Redis 7.2+, Ruby 3.2+, Rails 7.0+
|
11
28
|
|
12
29
|
7.3.8
|
13
30
|
----------
|
data/README.md
CHANGED
@@ -4,21 +4,23 @@ Sidekiq
|
|
4
4
|
[](https://rubygems.org/gems/sidekiq)
|
5
5
|

|
6
6
|
|
7
|
-
Simple, efficient background
|
7
|
+
Simple, efficient background jobs for Ruby.
|
8
8
|
|
9
9
|
Sidekiq uses threads to handle many jobs at the same time in the
|
10
|
-
same process.
|
11
|
-
Rails to make background processing dead simple.
|
10
|
+
same process. Sidekiq can be used by any Ruby application.
|
12
11
|
|
13
12
|
|
14
13
|
Requirements
|
15
14
|
-----------------
|
16
15
|
|
17
|
-
- Redis: Redis
|
18
|
-
- Ruby: MRI 2
|
16
|
+
- Redis: Redis 7.2+, Valkey 7.2+ or Dragonfly 1.13+
|
17
|
+
- Ruby: MRI 3.2+ or JRuby 9.4+.
|
19
18
|
|
20
|
-
Sidekiq
|
21
|
-
|
19
|
+
Sidekiq 8.0 supports Rails and Active Job 7.0+.
|
20
|
+
|
21
|
+
Sidekiq supports [Valkey](https://valkey.io) and [Dragonfly](https://www.dragonflydb.io) as Redis alternatives.
|
22
|
+
Redis 7.2.4 is considered to be the canonical implementation.
|
23
|
+
Incompatibilities with that version are considered bugs.
|
22
24
|
|
23
25
|
Installation
|
24
26
|
-----------------
|
@@ -42,6 +44,7 @@ The benchmark in `bin/sidekiqload` creates 500,000 no-op jobs and drains them as
|
|
42
44
|
This requires a lot of Redis network I/O and JSON parsing.
|
43
45
|
This benchmark is IO-bound so we increase the concurrency to 25.
|
44
46
|
If your application is sending lots of emails or performing other network-intensive work, you could see a similar benefit but be careful not to saturate the CPU.
|
47
|
+
Real world applications will rarely if ever need to use concurrency greater than 10.
|
45
48
|
|
46
49
|
Version | Time to process 500k jobs | Throughput (jobs/sec) | Ruby | Concurrency | Job Type
|
47
50
|
-----------------|------|---------|---------|------------------------|---
|
@@ -62,7 +65,7 @@ Want to Upgrade?
|
|
62
65
|
Use `bundle up sidekiq` to upgrade Sidekiq and all its dependencies.
|
63
66
|
Upgrade notes between each major version can be found in the `docs/` directory.
|
64
67
|
|
65
|
-
I also sell Sidekiq Pro and Sidekiq Enterprise, extensions to Sidekiq which provide more
|
68
|
+
I also sell [Sidekiq Pro](https://billing.contribsys.com/spro/) and [Sidekiq Enterprise](https://billing.contribsys.com/sent/new.cgi), extensions to Sidekiq which provide more
|
66
69
|
features, a commercial-friendly license and allow you to support high
|
67
70
|
quality open source development all at the same time. Please see the
|
68
71
|
[Sidekiq](https://sidekiq.org/) homepage for more detail.
|
@@ -71,7 +74,7 @@ quality open source development all at the same time. Please see the
|
|
71
74
|
Problems?
|
72
75
|
-----------------
|
73
76
|
|
74
|
-
**
|
77
|
+
**Do not directly email any Sidekiq committers with questions or problems.**
|
75
78
|
A community is best served when discussions are held in public.
|
76
79
|
|
77
80
|
If you have a problem, please review the [FAQ](https://github.com/sidekiq/sidekiq/wiki/FAQ) and [Troubleshooting](https://github.com/sidekiq/sidekiq/wiki/Problems-and-Troubleshooting) wiki pages.
|
@@ -86,21 +89,21 @@ Useful resources:
|
|
86
89
|
* Occasional announcements are made to the [@sidekiq](https://ruby.social/@sidekiq) Mastodon account.
|
87
90
|
* The [Sidekiq tag](https://stackoverflow.com/questions/tagged/sidekiq) on Stack Overflow has lots of useful Q & A.
|
88
91
|
|
89
|
-
Every Thursday morning is Sidekiq
|
92
|
+
Every Thursday morning is Sidekiq Office Hour: I video chat and answer questions.
|
90
93
|
See the [Sidekiq support page](https://sidekiq.org/support.html) for details.
|
91
94
|
|
92
95
|
Contributing
|
93
96
|
-----------------
|
94
97
|
|
95
|
-
|
98
|
+
See [the contributing guidelines](https://github.com/sidekiq/sidekiq/blob/main/.github/contributing.md).
|
96
99
|
|
97
100
|
License
|
98
101
|
-----------------
|
99
102
|
|
100
|
-
|
103
|
+
See [LICENSE.txt](https://github.com/sidekiq/sidekiq/blob/main/LICENSE.txt) for licensing details.
|
101
104
|
The license for Sidekiq Pro and Sidekiq Enterprise can be found in [COMM-LICENSE.txt](https://github.com/sidekiq/sidekiq/blob/main/COMM-LICENSE.txt).
|
102
105
|
|
103
106
|
Author
|
104
107
|
-----------------
|
105
108
|
|
106
|
-
Mike Perham, [
|
109
|
+
Mike Perham, [mastodon](https://ruby.social/@getajobmike), [https://www.mikeperham.com](https://www.mikeperham.com) / [https://www.contribsys.com](https://www.contribsys.com)
|
data/bin/sidekiqload
CHANGED
@@ -28,7 +28,7 @@ require "ruby-prof" if ENV["PROFILE"]
|
|
28
28
|
require "bundler/setup"
|
29
29
|
Bundler.require(:default, :load_test)
|
30
30
|
|
31
|
-
latency = Integer(ENV["LATENCY"] ||
|
31
|
+
latency = Integer(ENV["LATENCY"] || 0)
|
32
32
|
if latency > 0
|
33
33
|
# brew tap shopify/shopify
|
34
34
|
# brew install toxiproxy
|
@@ -86,7 +86,7 @@ class Loader
|
|
86
86
|
def initialize
|
87
87
|
@iter = ENV["GC"] ? 10 : 500
|
88
88
|
@count = Integer(ENV["COUNT"] || 1_000)
|
89
|
-
@latency = Integer(ENV["LATENCY"] ||
|
89
|
+
@latency = Integer(ENV["LATENCY"] || 0)
|
90
90
|
end
|
91
91
|
|
92
92
|
def configure
|
@@ -137,7 +137,7 @@ class Loader
|
|
137
137
|
end
|
138
138
|
|
139
139
|
def setup
|
140
|
-
Sidekiq.logger.
|
140
|
+
Sidekiq.logger.warn("Setup RSS: #{Process.rss}")
|
141
141
|
Sidekiq.redis { |c| c.flushdb }
|
142
142
|
start = Time.now
|
143
143
|
if ENV["AJ"]
|
@@ -167,9 +167,9 @@ class Loader
|
|
167
167
|
if total == 0
|
168
168
|
ending = Time.now - @start
|
169
169
|
size = @iter * @count
|
170
|
-
Sidekiq.logger.
|
171
|
-
Sidekiq.logger.
|
172
|
-
Sidekiq.logger.
|
170
|
+
Sidekiq.logger.warn("Done, #{size} jobs in #{ending} sec, #{(size / ending).to_i} jobs/sec")
|
171
|
+
Sidekiq.logger.warn("Ending RSS: #{Process.rss}")
|
172
|
+
Sidekiq.logger.warn("Now here's the latency for three jobs")
|
173
173
|
|
174
174
|
if ENV["AJ"]
|
175
175
|
LoadJob.perform_later("", 1, {}, Time.now.to_f)
|
@@ -191,7 +191,7 @@ class Loader
|
|
191
191
|
end
|
192
192
|
|
193
193
|
def with_latency(latency, &block)
|
194
|
-
Sidekiq.logger.
|
194
|
+
Sidekiq.logger.warn "Simulating #{latency}ms of latency between Sidekiq and redis"
|
195
195
|
if latency > 0
|
196
196
|
Toxiproxy[:redis].downstream(:latency, latency: latency).apply(&block)
|
197
197
|
else
|
@@ -204,8 +204,8 @@ class Loader
|
|
204
204
|
monitor
|
205
205
|
|
206
206
|
if ENV["PROFILE"]
|
207
|
-
RubyProf.exclude_threads
|
208
|
-
|
207
|
+
$profile = RubyProf::Profile.new(exclude_threads: [@monitor])
|
208
|
+
$profile.start
|
209
209
|
elsif ENV["GC"]
|
210
210
|
GC.start
|
211
211
|
GC.compact
|
@@ -236,7 +236,7 @@ class Loader
|
|
236
236
|
Sidekiq.logger.error("GC End RSS: #{Process.rss}") if ENV["GC"]
|
237
237
|
if ENV["PROFILE"]
|
238
238
|
Sidekiq.logger.error("Profiling...")
|
239
|
-
result =
|
239
|
+
result = $profile.stop
|
240
240
|
printer = RubyProf::GraphHtmlPrinter.new(result)
|
241
241
|
printer.print(File.new("output.html", "w"), min_percent: 1)
|
242
242
|
end
|
data/bin/webload
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.setup
|
5
|
+
|
6
|
+
# This skeleton allows you to run Sidekiq::Web page rendering
|
7
|
+
# through Vernier for tuning.
|
8
|
+
require "sidekiq/web"
|
9
|
+
require "rack/test"
|
10
|
+
require "vernier"
|
11
|
+
|
12
|
+
Sidekiq::Web.configure do |config|
|
13
|
+
config.middlewares.clear # remove csrf
|
14
|
+
end
|
15
|
+
|
16
|
+
class SomeJob
|
17
|
+
include Sidekiq::Job
|
18
|
+
end
|
19
|
+
|
20
|
+
class BenchWeb
|
21
|
+
include Rack::Test::Methods
|
22
|
+
|
23
|
+
def app
|
24
|
+
Sidekiq::Web.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def warmup(page = "/scheduled")
|
28
|
+
# Sidekiq.redis {|c| c.flushdb }
|
29
|
+
|
30
|
+
# 100.times do |idx|
|
31
|
+
# SomeJob.perform_at(idx, 1, 3, "mike", {"foo" => "bar"})
|
32
|
+
# end
|
33
|
+
|
34
|
+
100.times do
|
35
|
+
get page
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def load(page = "/scheduled", count = 10_000)
|
40
|
+
profile do
|
41
|
+
count.times do
|
42
|
+
get page
|
43
|
+
raise last_response.inspect unless last_response.status == 200
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def profile(&)
|
49
|
+
if ENV["PROF"]
|
50
|
+
Vernier.profile(out: "profile.json.gz", &)
|
51
|
+
else
|
52
|
+
yield
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def timer(name="block", count = 10_000)
|
58
|
+
a = Time.now
|
59
|
+
yield count
|
60
|
+
b = Time.now
|
61
|
+
puts "#{name} in #{b - a} sec"
|
62
|
+
end
|
63
|
+
|
64
|
+
page = "/busy"
|
65
|
+
b = BenchWeb.new
|
66
|
+
b.warmup(page)
|
67
|
+
timer(page) do |count|
|
68
|
+
b.load(page, count)
|
69
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveJob
|
4
4
|
module QueueAdapters
|
5
|
-
# Explicitly remove the implementation existing in older
|
5
|
+
# Explicitly remove the implementation existing in older Rails.
|
6
6
|
remove_const(:SidekiqAdapter) if const_defined?(:SidekiqAdapter)
|
7
7
|
|
8
8
|
# Sidekiq adapter for Active Job
|
@@ -20,7 +20,7 @@ module ActiveJob
|
|
20
20
|
|
21
21
|
# @api private
|
22
22
|
def enqueue(job)
|
23
|
-
job.provider_job_id =
|
23
|
+
job.provider_job_id = Sidekiq::ActiveJob::Wrapper.set(
|
24
24
|
wrapped: job.class,
|
25
25
|
queue: job.queue_name
|
26
26
|
).perform_async(job.serialize)
|
@@ -28,7 +28,7 @@ module ActiveJob
|
|
28
28
|
|
29
29
|
# @api private
|
30
30
|
def enqueue_at(job, timestamp)
|
31
|
-
job.provider_job_id =
|
31
|
+
job.provider_job_id = Sidekiq::ActiveJob::Wrapper.set(
|
32
32
|
wrapped: job.class,
|
33
33
|
queue: job.queue_name
|
34
34
|
).perform_at(timestamp, job.serialize)
|
@@ -43,7 +43,7 @@ module ActiveJob
|
|
43
43
|
|
44
44
|
if immediate_jobs.any?
|
45
45
|
jids = Sidekiq::Client.push_bulk(
|
46
|
-
"class" =>
|
46
|
+
"class" => Sidekiq::ActiveJob::Wrapper,
|
47
47
|
"wrapped" => job_class,
|
48
48
|
"queue" => queue,
|
49
49
|
"args" => immediate_jobs.map { |job| [job.serialize] }
|
@@ -53,7 +53,7 @@ module ActiveJob
|
|
53
53
|
|
54
54
|
if scheduled_jobs.any?
|
55
55
|
jids = Sidekiq::Client.push_bulk(
|
56
|
-
"class" =>
|
56
|
+
"class" => Sidekiq::ActiveJob::Wrapper,
|
57
57
|
"wrapped" => job_class,
|
58
58
|
"queue" => queue,
|
59
59
|
"args" => scheduled_jobs.map { |job| [job.serialize] },
|
data/lib/sidekiq/api.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "sidekiq"
|
4
|
-
|
5
3
|
require "zlib"
|
6
|
-
require "set"
|
7
4
|
|
5
|
+
require "sidekiq"
|
8
6
|
require "sidekiq/metrics/query"
|
9
7
|
|
10
8
|
#
|
@@ -101,11 +99,22 @@ module Sidekiq
|
|
101
99
|
rescue
|
102
100
|
{}
|
103
101
|
end
|
104
|
-
|
105
|
-
|
106
|
-
|
102
|
+
|
103
|
+
enqueued_at = job["enqueued_at"]
|
104
|
+
if enqueued_at
|
105
|
+
if enqueued_at.is_a?(Float)
|
106
|
+
# old format
|
107
|
+
now = Time.now.to_f
|
108
|
+
now - enqueued_at
|
109
|
+
else
|
110
|
+
now = ::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond)
|
111
|
+
(now - enqueued_at) / 1000.0
|
112
|
+
end
|
113
|
+
else
|
114
|
+
0.0
|
115
|
+
end
|
107
116
|
else
|
108
|
-
0
|
117
|
+
0.0
|
109
118
|
end
|
110
119
|
|
111
120
|
@stats = {
|
@@ -265,11 +274,22 @@ module Sidekiq
|
|
265
274
|
entry = Sidekiq.redis { |conn|
|
266
275
|
conn.lindex(@rname, -1)
|
267
276
|
}
|
268
|
-
return 0 unless entry
|
277
|
+
return 0.0 unless entry
|
278
|
+
|
269
279
|
job = Sidekiq.load_json(entry)
|
270
|
-
|
271
|
-
|
272
|
-
|
280
|
+
enqueued_at = job["enqueued_at"]
|
281
|
+
if enqueued_at
|
282
|
+
if enqueued_at.is_a?(Float)
|
283
|
+
# old format
|
284
|
+
now = Time.now.to_f
|
285
|
+
now - enqueued_at
|
286
|
+
else
|
287
|
+
now = ::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond)
|
288
|
+
(now - enqueued_at) / 1000.0
|
289
|
+
end
|
290
|
+
else
|
291
|
+
0.0
|
292
|
+
end
|
273
293
|
end
|
274
294
|
|
275
295
|
def each
|
@@ -422,11 +442,13 @@ module Sidekiq
|
|
422
442
|
end
|
423
443
|
|
424
444
|
def enqueued_at
|
425
|
-
|
445
|
+
if self["enqueued_at"]
|
446
|
+
time_from_timestamp(self["enqueued_at"])
|
447
|
+
end
|
426
448
|
end
|
427
449
|
|
428
450
|
def created_at
|
429
|
-
|
451
|
+
time_from_timestamp(self["created_at"] || self["enqueued_at"] || 0)
|
430
452
|
end
|
431
453
|
|
432
454
|
def tags
|
@@ -444,8 +466,17 @@ module Sidekiq
|
|
444
466
|
end
|
445
467
|
|
446
468
|
def latency
|
447
|
-
|
448
|
-
|
469
|
+
timestamp = @item["enqueued_at"] || @item["created_at"]
|
470
|
+
if timestamp
|
471
|
+
if timestamp.is_a?(Float)
|
472
|
+
# old format
|
473
|
+
Time.now.to_f - timestamp
|
474
|
+
else
|
475
|
+
(::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond) - timestamp) / 1000.0
|
476
|
+
end
|
477
|
+
else
|
478
|
+
0.0
|
479
|
+
end
|
449
480
|
end
|
450
481
|
|
451
482
|
# Remove this job from the queue
|
@@ -494,6 +525,15 @@ module Sidekiq
|
|
494
525
|
uncompressed = Zlib::Inflate.inflate(strict_base64_decoded)
|
495
526
|
Sidekiq.load_json(uncompressed)
|
496
527
|
end
|
528
|
+
|
529
|
+
def time_from_timestamp(timestamp)
|
530
|
+
if timestamp.is_a?(Float)
|
531
|
+
# old format, timestamps were stored as fractional seconds since the epoch
|
532
|
+
Time.at(timestamp).utc
|
533
|
+
else
|
534
|
+
Time.at(timestamp / 1000, timestamp % 1000, :millisecond)
|
535
|
+
end
|
536
|
+
end
|
497
537
|
end
|
498
538
|
|
499
539
|
# Represents a job within a Redis sorted set where the score
|
@@ -1142,7 +1182,7 @@ module Sidekiq
|
|
1142
1182
|
end
|
1143
1183
|
end
|
1144
1184
|
|
1145
|
-
results.sort_by { |(_, _,
|
1185
|
+
results.sort_by { |(_, _, work)| work.run_at }.each(&block)
|
1146
1186
|
end
|
1147
1187
|
|
1148
1188
|
# Note that #size is only as accurate as Sidekiq's heartbeat,
|
@@ -1172,13 +1212,14 @@ module Sidekiq
|
|
1172
1212
|
#
|
1173
1213
|
# @param jid [String] the job identifier
|
1174
1214
|
# @return [Sidekiq::Work] the work or nil
|
1175
|
-
def
|
1215
|
+
def find_work(jid)
|
1176
1216
|
each do |_process_id, _thread_id, work|
|
1177
1217
|
job = work.job
|
1178
1218
|
return work if job.jid == jid
|
1179
1219
|
end
|
1180
1220
|
nil
|
1181
1221
|
end
|
1222
|
+
alias_method :find_work_by_jid, :find_work
|
1182
1223
|
end
|
1183
1224
|
|
1184
1225
|
# Sidekiq::Work represents a job which is currently executing.
|
@@ -1208,33 +1249,64 @@ module Sidekiq
|
|
1208
1249
|
def payload
|
1209
1250
|
@hsh["payload"]
|
1210
1251
|
end
|
1252
|
+
end
|
1211
1253
|
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
warn("Direct access to `Sidekiq::Work` attributes is deprecated, please use `#payload`, `#queue`, `#run_at` or `#job` instead", **kwargs)
|
1254
|
+
# Since "worker" is a nebulous term, we've deprecated the use of this class name.
|
1255
|
+
# Is "worker" a process, a type of job, a thread? Undefined!
|
1256
|
+
# WorkSet better describes the data.
|
1257
|
+
Workers = WorkSet
|
1217
1258
|
|
1218
|
-
|
1219
|
-
|
1259
|
+
class ProfileSet
|
1260
|
+
include Enumerable
|
1220
1261
|
|
1221
|
-
#
|
1222
|
-
#
|
1223
|
-
def
|
1224
|
-
@
|
1262
|
+
# This is a point in time/snapshot API, you'll need to instantiate a new instance
|
1263
|
+
# if you want to fetch newer records.
|
1264
|
+
def initialize
|
1265
|
+
@records = Sidekiq.redis do |c|
|
1266
|
+
# This throws away expired profiles
|
1267
|
+
c.zremrangebyscore("profiles", "-inf", Time.now.to_f.to_s)
|
1268
|
+
# retreive records, newest to oldest
|
1269
|
+
c.zrange("profiles", "+inf", 0, "byscore", "rev")
|
1270
|
+
end
|
1225
1271
|
end
|
1226
1272
|
|
1227
|
-
def
|
1228
|
-
@
|
1273
|
+
def size
|
1274
|
+
@records.size
|
1229
1275
|
end
|
1230
1276
|
|
1231
|
-
def
|
1232
|
-
|
1277
|
+
def each(&block)
|
1278
|
+
fetch_keys = %w[started_at jid type token size elapsed].freeze
|
1279
|
+
arrays = Sidekiq.redis do |c|
|
1280
|
+
c.pipelined do |p|
|
1281
|
+
@records.each do |key|
|
1282
|
+
p.hmget(key, *fetch_keys)
|
1283
|
+
end
|
1284
|
+
end
|
1285
|
+
end
|
1286
|
+
|
1287
|
+
arrays.compact.map { |arr| ProfileRecord.new(arr) }.each(&block)
|
1233
1288
|
end
|
1234
1289
|
end
|
1235
1290
|
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1291
|
+
class ProfileRecord
|
1292
|
+
attr_reader :started_at, :jid, :type, :token, :size, :elapsed
|
1293
|
+
|
1294
|
+
def initialize(arr)
|
1295
|
+
# Must be same order as fetch_keys above
|
1296
|
+
@started_at = Time.at(Integer(arr[0]))
|
1297
|
+
@jid = arr[1]
|
1298
|
+
@type = arr[2]
|
1299
|
+
@token = arr[3]
|
1300
|
+
@size = Integer(arr[4])
|
1301
|
+
@elapsed = Float(arr[5])
|
1302
|
+
end
|
1303
|
+
|
1304
|
+
def key
|
1305
|
+
"#{token}-#{jid}"
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
def data
|
1309
|
+
Sidekiq.redis { |c| c.hget(key, "data") }
|
1310
|
+
end
|
1311
|
+
end
|
1240
1312
|
end
|
data/lib/sidekiq/capsule.rb
CHANGED
@@ -27,7 +27,7 @@ module Sidekiq
|
|
27
27
|
attr_reader :mode
|
28
28
|
attr_reader :weights
|
29
29
|
|
30
|
-
def_delegators :@config, :[], :[]=, :fetch, :key?, :has_key?, :merge!, :dig
|
30
|
+
def_delegators :@config, :[], :[]=, :fetch, :key?, :has_key?, :merge!, :dig, :thread_priority
|
31
31
|
|
32
32
|
def initialize(name, config)
|
33
33
|
@name = name
|
data/lib/sidekiq/cli.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
$stdout.sync = true
|
4
4
|
|
5
5
|
require "yaml"
|
6
|
-
require "singleton"
|
7
6
|
require "optparse"
|
8
7
|
require "erb"
|
9
8
|
require "fileutils"
|
@@ -17,7 +16,6 @@ require "sidekiq/launcher"
|
|
17
16
|
module Sidekiq # :nodoc:
|
18
17
|
class CLI
|
19
18
|
include Sidekiq::Component
|
20
|
-
include Singleton unless $TESTING
|
21
19
|
|
22
20
|
attr_accessor :launcher
|
23
21
|
attr_accessor :environment
|
@@ -31,6 +29,10 @@ module Sidekiq # :nodoc:
|
|
31
29
|
validate!
|
32
30
|
end
|
33
31
|
|
32
|
+
def self.instance
|
33
|
+
@instance ||= new
|
34
|
+
end
|
35
|
+
|
34
36
|
def jruby?
|
35
37
|
defined?(::JRUBY_VERSION)
|
36
38
|
end
|
@@ -74,7 +76,7 @@ module Sidekiq # :nodoc:
|
|
74
76
|
# fire startup and start multithreading.
|
75
77
|
info = @config.redis_info
|
76
78
|
ver = Gem::Version.new(info["redis_version"])
|
77
|
-
raise "You are
|
79
|
+
raise "You are connected to Redis #{ver}, Sidekiq requires Redis 7.2.0 or greater" if ver < Gem::Version.new("7.2.0")
|
78
80
|
|
79
81
|
maxmemory_policy = info["maxmemory_policy"]
|
80
82
|
if maxmemory_policy != "noeviction" && maxmemory_policy != ""
|
@@ -298,29 +300,18 @@ module Sidekiq # :nodoc:
|
|
298
300
|
|
299
301
|
if File.directory?(@config[:require])
|
300
302
|
require "rails"
|
301
|
-
if ::Rails::VERSION::MAJOR <
|
302
|
-
warn "Sidekiq #{Sidekiq::VERSION} only supports Rails
|
303
|
+
if ::Rails::VERSION::MAJOR < 7
|
304
|
+
warn "Sidekiq #{Sidekiq::VERSION} only supports Rails 7+"
|
303
305
|
end
|
304
306
|
require "sidekiq/rails"
|
305
307
|
require File.expand_path("#{@config[:require]}/config/environment.rb")
|
306
|
-
@config[:tag] ||= default_tag
|
308
|
+
@config[:tag] ||= default_tag(::Rails.root)
|
307
309
|
else
|
308
310
|
require @config[:require]
|
311
|
+
@config[:tag] ||= default_tag
|
309
312
|
end
|
310
313
|
end
|
311
314
|
|
312
|
-
def default_tag
|
313
|
-
dir = ::Rails.root
|
314
|
-
name = File.basename(dir)
|
315
|
-
prevdir = File.dirname(dir) # Capistrano release directory?
|
316
|
-
if name.to_i != 0 && prevdir
|
317
|
-
if File.basename(prevdir) == "releases"
|
318
|
-
return File.basename(File.dirname(prevdir))
|
319
|
-
end
|
320
|
-
end
|
321
|
-
name
|
322
|
-
end
|
323
|
-
|
324
315
|
def validate!
|
325
316
|
if !File.exist?(@config[:require]) ||
|
326
317
|
(File.directory?(@config[:require]) && !File.exist?("#{@config[:require]}/config/application.rb"))
|
@@ -395,7 +386,12 @@ module Sidekiq # :nodoc:
|
|
395
386
|
end
|
396
387
|
|
397
388
|
def initialize_logger
|
398
|
-
|
389
|
+
if @config[:verbose] || ENV["DEBUG_INVOCATION"] == "1"
|
390
|
+
# DEBUG_INVOCATION is a systemd-ism triggered by
|
391
|
+
# RestartMode=debug. We turn on debugging when the
|
392
|
+
# sidekiq process crashes and is restarted with this flag.
|
393
|
+
@config.logger.level = ::Logger::DEBUG
|
394
|
+
end
|
399
395
|
end
|
400
396
|
|
401
397
|
def parse_config(path)
|
data/lib/sidekiq/client.rb
CHANGED
@@ -67,9 +67,7 @@ module Sidekiq
|
|
67
67
|
c.pipelined do |p|
|
68
68
|
p.hsetnx(key, "cancelled", Time.now.to_i)
|
69
69
|
p.hget(key, "cancelled")
|
70
|
-
p.expire(key, Sidekiq::Job::Iterable::STATE_TTL)
|
71
|
-
# TODO When Redis 7.2 is required
|
72
|
-
# p.expire(key, Sidekiq::Job::Iterable::STATE_TTL, "nx")
|
70
|
+
p.expire(key, Sidekiq::Job::Iterable::STATE_TTL, "nx")
|
73
71
|
end
|
74
72
|
end
|
75
73
|
result.to_i
|
@@ -266,22 +264,21 @@ module Sidekiq
|
|
266
264
|
if payloads.first.key?("at")
|
267
265
|
conn.zadd("schedule", payloads.flat_map { |hash|
|
268
266
|
at = hash["at"].to_s
|
269
|
-
# ActiveJob sets
|
270
|
-
hash.
|
271
|
-
# TODO: Use hash.except("at") when support for Ruby 2.7 is dropped
|
272
|
-
hash = hash.dup
|
273
|
-
hash.delete("at")
|
267
|
+
# ActiveJob sets enqueued_at but the job has not been enqueued yet
|
268
|
+
hash = hash.except("enqueued_at", "at")
|
274
269
|
[at, Sidekiq.dump_json(hash)]
|
275
270
|
})
|
276
271
|
else
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
272
|
+
now = ::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond) # milliseconds since the epoch
|
273
|
+
grouped_queues = payloads.group_by { |job| job["queue"] }
|
274
|
+
conn.sadd("queues", grouped_queues.keys)
|
275
|
+
grouped_queues.each do |queue, grouped_payloads|
|
276
|
+
to_push = grouped_payloads.map { |entry|
|
277
|
+
entry["enqueued_at"] = now
|
278
|
+
Sidekiq.dump_json(entry)
|
279
|
+
}
|
280
|
+
conn.lpush("queue:#{queue}", to_push)
|
281
|
+
end
|
285
282
|
end
|
286
283
|
end
|
287
284
|
end
|