sidekiq 2.17.8 → 3.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.
- checksums.yaml +4 -4
- data/3.0-Upgrade.md +63 -0
- data/Changes.md +66 -3
- data/Contributing.md +1 -3
- data/Pro-Changes.md +18 -0
- data/README.md +2 -2
- data/bin/sidekiqctl +19 -6
- data/lib/sidekiq.rb +53 -11
- data/lib/sidekiq/actor.rb +1 -0
- data/lib/sidekiq/api.rb +145 -58
- data/lib/sidekiq/cli.rb +22 -18
- data/lib/sidekiq/client.rb +44 -14
- data/lib/sidekiq/core_ext.rb +5 -8
- data/lib/sidekiq/exception_handler.rb +19 -28
- data/lib/sidekiq/fetch.rb +3 -3
- data/lib/sidekiq/launcher.rb +30 -3
- data/lib/sidekiq/logging.rb +2 -2
- data/lib/sidekiq/manager.rb +19 -16
- data/lib/sidekiq/middleware/chain.rb +1 -1
- data/lib/sidekiq/middleware/i18n.rb +1 -1
- data/lib/sidekiq/middleware/server/retry_jobs.rb +23 -7
- data/lib/sidekiq/processor.rb +36 -54
- data/lib/sidekiq/redis_connection.rb +1 -3
- data/lib/sidekiq/util.rb +4 -4
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web.rb +57 -8
- data/lib/sidekiq/web_helpers.rb +6 -15
- data/lib/sidekiq/worker.rb +3 -1
- data/sidekiq.gemspec +5 -5
- data/test/test_api.rb +59 -19
- data/test/test_cli.rb +1 -1
- data/test/test_client.rb +44 -5
- data/test/test_exception_handler.rb +4 -87
- data/test/test_middleware.rb +3 -2
- data/test/test_redis_connection.rb +0 -6
- data/test/test_retry.rb +13 -68
- data/test/test_scheduled.rb +1 -1
- data/test/test_scheduling.rb +5 -0
- data/test/test_sidekiq.rb +18 -0
- data/test/test_web.rb +98 -58
- data/web/assets/stylesheets/application.css +5 -0
- data/web/locales/cs.yml +68 -0
- data/web/locales/da.yml +9 -1
- data/web/locales/de.yml +15 -7
- data/web/locales/el.yml +68 -0
- data/web/locales/en.yml +8 -3
- data/web/locales/es.yml +9 -1
- data/web/locales/fr.yml +34 -26
- data/web/locales/it.yml +26 -18
- data/web/locales/ja.yml +8 -2
- data/web/locales/ko.yml +0 -2
- data/web/locales/nl.yml +8 -3
- data/web/locales/no.yml +9 -3
- data/web/locales/pl.yml +0 -1
- data/web/locales/pt-br.yml +11 -4
- data/web/locales/pt.yml +8 -1
- data/web/locales/ru.yml +29 -22
- data/web/locales/sv.yml +68 -0
- data/web/locales/zh-tw.yml +68 -0
- data/web/views/_job_info.erb +8 -2
- data/web/views/_summary.erb +13 -7
- data/web/views/busy.erb +55 -0
- data/web/views/dead.erb +30 -0
- data/web/views/layout.erb +1 -0
- data/web/views/morgue.erb +66 -0
- metadata +29 -30
- data/config.ru +0 -18
- data/lib/sidekiq/capistrano.rb +0 -5
- data/lib/sidekiq/capistrano2.rb +0 -54
- data/lib/sidekiq/tasks/sidekiq.rake +0 -119
- data/lib/sidekiq/yaml_patch.rb +0 -21
- data/test/test_util.rb +0 -18
- data/web/views/_workers.erb +0 -22
- data/web/views/workers.erb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6924f9ac5a08e3bfb8b20204262b281082a590c9
|
4
|
+
data.tar.gz: db0c1c65e0f8ca499771c8f68174cc889a977e79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 541f6b4546da89be32045f56d4e5dd41aa9d97d03860ee4fdde696cfb88d9b6ca68d4fb5f83377696d4ee1a9fe572d129afe8c5ddfebb61809302ed0cd54f88c
|
7
|
+
data.tar.gz: d396cad4b53238a174c722a92e485fb8657664d1965ea826c264c2aab1e5e25991023edce4556e3ceff05250bccf02892190b9fa69a1bc1c40944a71cb20d445
|
data/3.0-Upgrade.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# Upgrading to Sidekiq 3.0
|
2
|
+
|
3
|
+
Sidekiq 3.0 brings several new features but also removes old APIs and
|
4
|
+
changes a few data elements in Redis. To upgrade cleanly:
|
5
|
+
|
6
|
+
* Upgrade to the latest Sidekiq 2.x and run it for a few weeks.
|
7
|
+
`gem 'sidekiq', '< 3'`
|
8
|
+
This is only needed if you have retries pending.
|
9
|
+
* 3rd party gems which use **client-side middleware** will need to update
|
10
|
+
due to an API change. The Redis connection for a particular job is
|
11
|
+
passed thru the middleware to handle sharding where jobs can
|
12
|
+
be pushed to different redis server instances.
|
13
|
+
|
14
|
+
`def call(worker_class, msg, queue, redis_pool)`
|
15
|
+
|
16
|
+
Client-side middleware should use `redis_pool.with { |conn| ... }` to
|
17
|
+
perform Redis operations and **not** `Sidekiq.redis`.
|
18
|
+
* If you used the capistrano integration, you'll need to pull in the
|
19
|
+
new [capistrano-sidekiq](https://github.com/seuros/capistrano-sidekiq)
|
20
|
+
gem and use it in your deploy.rb.
|
21
|
+
* API changes:
|
22
|
+
- `Sidekiq::Client.registered_workers` replaced by `Sidekiq::Workers.new`
|
23
|
+
- `Sidekiq::Client.registered_queues` replaced by `Sidekiq::Queue.all`
|
24
|
+
- `Sidekiq::Worker#retries_exhausted` replaced by `Sidekiq::Worker.sidekiq_retries_exhausted`
|
25
|
+
- `Sidekiq::Workers#each` has changed significantly with a reworking
|
26
|
+
of Sidekiq's internal process/thread data model.
|
27
|
+
* Redis-to-Go is no longer transparently activated on Heroku so as to not play
|
28
|
+
favorites with any particular Redis service. You need to set a config option
|
29
|
+
for your app:
|
30
|
+
`heroku config:set REDIS_PROVIDER=REDISTOGO_URL`
|
31
|
+
* Anyone using Airbrake, Honeybadger, Exceptional or ExceptionNotifier
|
32
|
+
will need to update their error gem version to the latest to pull in
|
33
|
+
Sidekiq support. Sidekiq will not provide explicit support for these
|
34
|
+
services so as to not play favorites with any particular error service.
|
35
|
+
* MRI 1.9 is no longer officially supported. Sidekiq's official
|
36
|
+
support policy is to support the current and previous major releases
|
37
|
+
of MRI and Rails. As of February 2014, that's MRI 2.1, MRI 2.0, JRuby 1.7, Rails 4.0
|
38
|
+
and Rails 3.2. I will consider PRs to fix issues found by users for
|
39
|
+
other platforms/versions.
|
40
|
+
|
41
|
+
## Error Service Providers
|
42
|
+
|
43
|
+
If you previously provided a middleware to capture job errors, you
|
44
|
+
should instead provide a global error handler with Sidekiq 3.0. This
|
45
|
+
ensures **any** error within Sidekiq will be logged appropriately, not
|
46
|
+
just during job execution.
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
if Sidekiq::VERSION < '3'
|
50
|
+
# old behavior
|
51
|
+
Sidekiq.configure_server do |config|
|
52
|
+
config.server_middleware do |chain|
|
53
|
+
chain.add MyErrorService::Middleware
|
54
|
+
end
|
55
|
+
end
|
56
|
+
else
|
57
|
+
Sidekiq.configure_server do |config|
|
58
|
+
config.error_handlers << Proc.new {|ex,context| MyErrorService.notify(ex, context) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
Your error handler must respond to `call(exception, context_hash)`.
|
data/Changes.md
CHANGED
@@ -1,8 +1,71 @@
|
|
1
|
-
|
1
|
+
3.0.0
|
2
2
|
-----------
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
**Not yet released**
|
5
|
+
|
6
|
+
Please see [3.0-Upgrade.md](3.0-Upgrade.md) for more comprehensive upgrade notes.
|
7
|
+
|
8
|
+
- **Dead Job Queue** - jobs which run out of retries are now moved to a dead
|
9
|
+
job queue. These jobs must be retried manually or they will expire
|
10
|
+
after 6 months or 10,000 jobs. The Web UI contains a "Dead" tab
|
11
|
+
exposing these jobs. Use `sidekiq_options :retry => false` if you
|
12
|
+
don't wish jobs to be retried or put in the DJQ. Use
|
13
|
+
`sidekiq_options :retry => 0` if you don't want jobs to retry but go
|
14
|
+
straight to the DJQ.
|
15
|
+
- **Process Lifecycle Events** - you can now register blocks to run at
|
16
|
+
certain points during the Sidekiq process lifecycle: startup, quiet and
|
17
|
+
shutdown.
|
18
|
+
```ruby
|
19
|
+
Sidekiq.configure_server do |config|
|
20
|
+
config.on(:startup) do
|
21
|
+
# do something
|
22
|
+
end
|
23
|
+
end
|
24
|
+
```
|
25
|
+
- **Global Error Handlers** - blocks of code which handle errors that
|
26
|
+
occur anywhere within Sidekiq, not just within middleware.
|
27
|
+
```ruby
|
28
|
+
Sidekiq.configure_server do |config|
|
29
|
+
config.error_handlers << Proc.new {|ex,ctx| ... }
|
30
|
+
end
|
31
|
+
```
|
32
|
+
- **Process Heartbeat** - each Sidekiq process will ping Redis every 5
|
33
|
+
seconds to give a summary of the Sidekiq population at work.
|
34
|
+
- The Workers tab is now renamed to Busy and contains a list of live
|
35
|
+
Sidekiq processes and jobs in progress based on the heartbeat.
|
36
|
+
- **Shardable Client** - Sidekiq::Client instances can use a custom
|
37
|
+
Redis connection pool, allowing very large Sidekiq installations to scale by
|
38
|
+
sharding: sending different jobs to different Redis instances.
|
39
|
+
```ruby
|
40
|
+
client = Sidekiq::Client.new(ConnectionPool.new { Redis.new })
|
41
|
+
client.push(...)
|
42
|
+
```
|
43
|
+
```ruby
|
44
|
+
Sidekiq::Client.via(ConnectionPool.new { Redis.new }) do
|
45
|
+
FooWorker.perform_async
|
46
|
+
BarWorker.perform_async
|
47
|
+
end
|
48
|
+
```
|
49
|
+
**Sharding support does require a breaking change to client-side
|
50
|
+
middleware, see 3.0-Upgrade.md.**
|
51
|
+
- New Chinese, Greek, Swedish and Czech translations for the Web UI.
|
52
|
+
- Updated most languages translations for the new UI features.
|
53
|
+
- **Remove official Capistrano integration** - this integration has been
|
54
|
+
moved into the [capistrano-sidekiq](https://github.com/seuros/capistrano-sidekiq) gem.
|
55
|
+
- **Remove official support for MRI 1.9** - Things still might work but
|
56
|
+
I no longer actively test on it.
|
57
|
+
- **Remove built-in support for Redis-to-Go**.
|
58
|
+
Heroku users: `heroku config:set REDIS_PROVIDER=REDISTOGO_URL`
|
59
|
+
- **Remove built-in error integration for Airbrake, Honeybadger, ExceptionNotifier and Exceptional**.
|
60
|
+
Each error gem should provide its own Sidekiq integration. Update your error gem to the latest
|
61
|
+
version to pick up Sidekiq support.
|
62
|
+
- Upgrade to connection\_pool 2.0 which now creates connections lazily.
|
63
|
+
- Remove deprecated Sidekiq::Client.registered\_\* APIs
|
64
|
+
- Remove deprecated support for the old Sidekiq::Worker#retries\_exhausted method.
|
65
|
+
- Removed 'sidekiq/yaml\_patch', this was never documented or recommended.
|
66
|
+
- Removed --profile option, #1592
|
67
|
+
- Remove usage of the term 'Worker' in the UI for clarity. Users would call both threads and
|
68
|
+
processes 'workers'. Instead, use "Thread", "Process" or "Job".
|
6
69
|
|
7
70
|
2.17.7
|
8
71
|
-----------
|
data/Contributing.md
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# Contributing
|
2
2
|
|
3
|
-
First of all, thank you
|
4
|
-
it worthwhile to help out with Sidekiq.
|
3
|
+
First of all, thank you! I hope you find it worthwhile to help out with Sidekiq.
|
5
4
|
|
6
5
|
## Issues
|
7
6
|
|
@@ -21,7 +20,6 @@ fix or new functionality. Functionality must meet my design goals and
|
|
21
20
|
vision for the project to be accepted; I would be happy to discuss how
|
22
21
|
your idea can best fit into Sidekiq.
|
23
22
|
|
24
|
-
|
25
23
|
## Sponsorship
|
26
24
|
|
27
25
|
If you've got more money than time and want to sponsor Sidekiq's continued support, your company can buy [Sidekiq Pro](http://sidekiq.org/pro). You get great functionality, I continue to fix bugs and enhance Sidekiq for years to come.
|
data/Pro-Changes.md
CHANGED
@@ -3,6 +3,24 @@ Sidekiq Pro Changelog
|
|
3
3
|
|
4
4
|
Please see [http://sidekiq.org/pro](http://sidekiq.org/pro) for more details and how to buy.
|
5
5
|
|
6
|
+
HEAD
|
7
|
+
-----------
|
8
|
+
|
9
|
+
- Compatible with Sidekiq 3.
|
10
|
+
|
11
|
+
1.5.1
|
12
|
+
-----------
|
13
|
+
|
14
|
+
- Due to a breaking API change in Sidekiq 3.0, this version is limited
|
15
|
+
to Sidekiq 2.x.
|
16
|
+
|
17
|
+
1.5.0
|
18
|
+
-----------
|
19
|
+
|
20
|
+
- Compatible with upcoming Sidekiq 3.0 release
|
21
|
+
- Fix issue on Heroku where reliable fetch could orphan jobs [#1573]
|
22
|
+
|
23
|
+
|
6
24
|
1.4.3
|
7
25
|
-----------
|
8
26
|
|
data/README.md
CHANGED
@@ -17,14 +17,14 @@ use the Resque client to enqueue jobs in Redis to be processed by Sidekiq.
|
|
17
17
|
|
18
18
|
At the same time, Sidekiq uses multithreading so it is much more memory efficient than
|
19
19
|
Resque (which forks a new process for every job). You'll find that you might need
|
20
|
-
|
20
|
+
10 200MB resque processes to peg your CPU whereas one 300MB Sidekiq process will peg
|
21
21
|
the same CPU and perform the same amount of work.
|
22
22
|
|
23
23
|
|
24
24
|
Requirements
|
25
25
|
-----------------
|
26
26
|
|
27
|
-
I test with the latest
|
27
|
+
I test with the latest MRI (2.1, 2.0) and JRuby versions (1.7). Other versions/VMs
|
28
28
|
are untested but might work fine.
|
29
29
|
|
30
30
|
The last two major Rails releases (3.2 and 4.0) are officially supported, other
|
data/bin/sidekiqctl
CHANGED
@@ -3,9 +3,19 @@
|
|
3
3
|
require 'fileutils'
|
4
4
|
|
5
5
|
class Sidekiqctl
|
6
|
+
DEFAULT_TIMEOUT = 10
|
6
7
|
|
7
8
|
attr_reader :stage, :pidfile, :timeout
|
8
9
|
|
10
|
+
def self.print_usage
|
11
|
+
puts
|
12
|
+
puts "Usage: #{File.basename($0)} <command> <pidfile> <timeout>"
|
13
|
+
puts " where <command> is either 'quiet', 'stop' or 'shutdown'"
|
14
|
+
puts " <pidfile> is path to a pidfile"
|
15
|
+
puts " <timeout> is number of seconds to wait till Sidekiq exits (default: #{Sidekiqctl::DEFAULT_TIMEOUT})"
|
16
|
+
puts
|
17
|
+
end
|
18
|
+
|
9
19
|
def initialize(stage, pidfile, timeout)
|
10
20
|
@stage = stage
|
11
21
|
@pidfile = pidfile
|
@@ -67,12 +77,15 @@ class Sidekiqctl
|
|
67
77
|
quiet
|
68
78
|
stop
|
69
79
|
end
|
70
|
-
|
71
80
|
end
|
72
81
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
82
|
+
if ARGV.length < 2
|
83
|
+
Sidekiqctl.print_usage
|
84
|
+
else
|
85
|
+
stage = ARGV[0]
|
86
|
+
pidfile = ARGV[1]
|
87
|
+
timeout = ARGV[2].to_i
|
88
|
+
timeout = Sidekiqctl::DEFAULT_TIMEOUT if timeout == 0
|
77
89
|
|
78
|
-
Sidekiqctl.new(stage, pidfile, timeout)
|
90
|
+
Sidekiqctl.new(stage, pidfile, timeout)
|
91
|
+
end
|
data/lib/sidekiq.rb
CHANGED
@@ -4,8 +4,6 @@ require 'sidekiq/logging'
|
|
4
4
|
require 'sidekiq/client'
|
5
5
|
require 'sidekiq/worker'
|
6
6
|
require 'sidekiq/redis_connection'
|
7
|
-
require 'sidekiq/util'
|
8
|
-
require 'sidekiq/api'
|
9
7
|
|
10
8
|
require 'json'
|
11
9
|
|
@@ -19,7 +17,12 @@ module Sidekiq
|
|
19
17
|
:require => '.',
|
20
18
|
:environment => nil,
|
21
19
|
:timeout => 8,
|
22
|
-
:
|
20
|
+
:error_handlers => [],
|
21
|
+
:lifecycle_events => {
|
22
|
+
:startup => [],
|
23
|
+
:quiet => [],
|
24
|
+
:shutdown => [],
|
25
|
+
},
|
23
26
|
}
|
24
27
|
|
25
28
|
def self.❨╯°□°❩╯︵┻━┻
|
@@ -63,17 +66,18 @@ module Sidekiq
|
|
63
66
|
|
64
67
|
def self.redis(&block)
|
65
68
|
raise ArgumentError, "requires a block" if !block
|
66
|
-
|
67
|
-
@redis.with(&block)
|
69
|
+
redis_pool.with(&block)
|
68
70
|
end
|
69
71
|
|
70
|
-
def self.
|
71
|
-
|
72
|
+
def self.redis_pool
|
73
|
+
@redis ||= Sidekiq::RedisConnection.create
|
74
|
+
end
|
72
75
|
|
73
|
-
|
74
|
-
|
76
|
+
def self.redis=(hash)
|
77
|
+
@redis = if hash.is_a?(ConnectionPool)
|
78
|
+
hash
|
75
79
|
else
|
76
|
-
|
80
|
+
Sidekiq::RedisConnection.create(hash)
|
77
81
|
end
|
78
82
|
end
|
79
83
|
|
@@ -94,7 +98,7 @@ module Sidekiq
|
|
94
98
|
end
|
95
99
|
|
96
100
|
def self.default_worker_options
|
97
|
-
@default_worker_options
|
101
|
+
defined?(@default_worker_options) ? @default_worker_options : { 'retry' => true, 'queue' => 'default' }
|
98
102
|
end
|
99
103
|
|
100
104
|
def self.load_json(string)
|
@@ -117,6 +121,44 @@ module Sidekiq
|
|
117
121
|
self.options[:poll_interval] = interval
|
118
122
|
end
|
119
123
|
|
124
|
+
# Register a proc to handle any error which occurs within the Sidekiq process.
|
125
|
+
#
|
126
|
+
# Sidekiq.configure_server do |config|
|
127
|
+
# config.error_handlers << Proc.new {|ex,ctx_hash| MyErrorService.notify(ex, ctx_hash) }
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# The default error handler logs errors to Sidekiq.logger.
|
131
|
+
def self.error_handlers
|
132
|
+
self.options[:error_handlers]
|
133
|
+
end
|
134
|
+
|
135
|
+
# Register a block to run at a point in the Sidekiq lifecycle.
|
136
|
+
# :startup, :quiet or :shutdown are valid events.
|
137
|
+
#
|
138
|
+
# Sidekiq.configure_server do |config|
|
139
|
+
# config.on(:shutdown) do
|
140
|
+
# puts "Goodbye cruel world!"
|
141
|
+
# end
|
142
|
+
# end
|
143
|
+
def self.on(event, &block)
|
144
|
+
raise ArgumentError, "Symbols only please: #{event}" if !event.is_a?(Symbol)
|
145
|
+
raise ArgumentError, "Invalid event name: #{event}" if !options[:lifecycle_events].keys.include?(event)
|
146
|
+
options[:lifecycle_events][event] << block
|
147
|
+
end
|
148
|
+
|
149
|
+
BANNER = %q{ s
|
150
|
+
ss
|
151
|
+
sss sss ss
|
152
|
+
s sss s ssss sss ____ _ _ _ _
|
153
|
+
s sssss ssss / ___|(_) __| | ___| | _(_) __ _
|
154
|
+
s sss \___ \| |/ _` |/ _ \ |/ / |/ _` |
|
155
|
+
s sssss s ___) | | (_| | __/ <| | (_| |
|
156
|
+
ss s s |____/|_|\__,_|\___|_|\_\_|\__, |
|
157
|
+
s s s |_|
|
158
|
+
s s
|
159
|
+
sss
|
160
|
+
sss }
|
161
|
+
|
120
162
|
end
|
121
163
|
|
122
164
|
require 'sidekiq/extensions/class_methods'
|
data/lib/sidekiq/actor.rb
CHANGED
data/lib/sidekiq/api.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'sidekiq'
|
2
3
|
|
3
4
|
module Sidekiq
|
@@ -14,8 +15,13 @@ module Sidekiq
|
|
14
15
|
all = %w(failed processed)
|
15
16
|
stats = stats.empty? ? all : all & stats.flatten.compact.map(&:to_s)
|
16
17
|
|
18
|
+
mset_args = []
|
19
|
+
stats.each do |stat|
|
20
|
+
mset_args << "stat:#{stat}"
|
21
|
+
mset_args << 0
|
22
|
+
end
|
17
23
|
Sidekiq.redis do |conn|
|
18
|
-
|
24
|
+
conn.mset(*mset_args)
|
19
25
|
end
|
20
26
|
end
|
21
27
|
|
@@ -23,8 +29,16 @@ module Sidekiq
|
|
23
29
|
Sidekiq.redis do |conn|
|
24
30
|
queues = conn.smembers('queues')
|
25
31
|
|
32
|
+
lengths = conn.pipelined do
|
33
|
+
queues.each do |queue|
|
34
|
+
conn.llen("queue:#{queue}")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
i = 0
|
26
39
|
array_of_arrays = queues.inject({}) do |memo, queue|
|
27
|
-
memo[queue] =
|
40
|
+
memo[queue] = lengths[i]
|
41
|
+
i += 1
|
28
42
|
memo
|
29
43
|
end.sort_by { |_, size| size }
|
30
44
|
|
@@ -44,6 +58,10 @@ module Sidekiq
|
|
44
58
|
Sidekiq.redis {|c| c.zcard('retry') }
|
45
59
|
end
|
46
60
|
|
61
|
+
def dead_size
|
62
|
+
Sidekiq.redis {|c| c.zcard('dead') }
|
63
|
+
end
|
64
|
+
|
47
65
|
class History
|
48
66
|
def initialize(days_previous, start_date = nil)
|
49
67
|
@days_previous = days_previous
|
@@ -63,15 +81,19 @@ module Sidekiq
|
|
63
81
|
def date_stat_hash(stat)
|
64
82
|
i = 0
|
65
83
|
stat_hash = {}
|
84
|
+
keys = []
|
85
|
+
dates = []
|
86
|
+
|
87
|
+
while i < @days_previous
|
88
|
+
date = @start_date - i
|
89
|
+
keys << "stat:#{stat}:#{date}"
|
90
|
+
dates << date
|
91
|
+
i += 1
|
92
|
+
end
|
66
93
|
|
67
94
|
Sidekiq.redis do |conn|
|
68
|
-
|
69
|
-
|
70
|
-
value = conn.get("stat:#{stat}:#{date}")
|
71
|
-
|
72
|
-
stat_hash[date.to_s] = value ? value.to_i : 0
|
73
|
-
|
74
|
-
i += 1
|
95
|
+
conn.mget(keys).each_with_index do |value, i|
|
96
|
+
stat_hash[dates[i].to_s] = value ? value.to_i : 0
|
75
97
|
end
|
76
98
|
end
|
77
99
|
|
@@ -151,6 +173,7 @@ module Sidekiq
|
|
151
173
|
end
|
152
174
|
end
|
153
175
|
end
|
176
|
+
alias_method :💣, :clear
|
154
177
|
end
|
155
178
|
|
156
179
|
##
|
@@ -209,6 +232,7 @@ module Sidekiq
|
|
209
232
|
|
210
233
|
class SortedEntry < Job
|
211
234
|
attr_reader :score
|
235
|
+
attr_reader :parent
|
212
236
|
|
213
237
|
def initialize(parent, score, item)
|
214
238
|
super(item)
|
@@ -243,11 +267,11 @@ module Sidekiq
|
|
243
267
|
end
|
244
268
|
|
245
269
|
def retry
|
246
|
-
raise "Retry not available on jobs
|
270
|
+
raise "Retry not available on jobs which have not failed" unless item["failed_at"]
|
247
271
|
Sidekiq.redis do |conn|
|
248
272
|
results = conn.multi do
|
249
|
-
conn.zrangebyscore(
|
250
|
-
conn.zremrangebyscore(
|
273
|
+
conn.zrangebyscore(parent.name, score, score)
|
274
|
+
conn.zremrangebyscore(parent.name, score, score)
|
251
275
|
end.first
|
252
276
|
results.map do |message|
|
253
277
|
msg = Sidekiq.load_json(message)
|
@@ -272,6 +296,16 @@ module Sidekiq
|
|
272
296
|
Sidekiq.redis {|c| c.zcard(name) }
|
273
297
|
end
|
274
298
|
|
299
|
+
def clear
|
300
|
+
Sidekiq.redis do |conn|
|
301
|
+
conn.del(name)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
alias_method :💣, :clear
|
305
|
+
end
|
306
|
+
|
307
|
+
class JobSet < SortedSet
|
308
|
+
|
275
309
|
def schedule(timestamp, message)
|
276
310
|
Sidekiq.redis do |conn|
|
277
311
|
conn.zadd(name, timestamp.to_f.to_s, Sidekiq.dump_json(message))
|
@@ -349,11 +383,6 @@ module Sidekiq
|
|
349
383
|
end
|
350
384
|
end
|
351
385
|
|
352
|
-
def clear
|
353
|
-
Sidekiq.redis do |conn|
|
354
|
-
conn.del(name)
|
355
|
-
end
|
356
|
-
end
|
357
386
|
end
|
358
387
|
|
359
388
|
##
|
@@ -368,7 +397,7 @@ module Sidekiq
|
|
368
397
|
# retri.args[0] == 'User' &&
|
369
398
|
# retri.args[1] == 'setup_new_subscriber'
|
370
399
|
# end.map(&:delete)
|
371
|
-
class ScheduledSet <
|
400
|
+
class ScheduledSet < JobSet
|
372
401
|
def initialize
|
373
402
|
super 'schedule'
|
374
403
|
end
|
@@ -386,7 +415,7 @@ module Sidekiq
|
|
386
415
|
# retri.args[0] == 'User' &&
|
387
416
|
# retri.args[1] == 'setup_new_subscriber'
|
388
417
|
# end.map(&:delete)
|
389
|
-
class RetrySet <
|
418
|
+
class RetrySet < JobSet
|
390
419
|
def initialize
|
391
420
|
super 'retry'
|
392
421
|
end
|
@@ -398,6 +427,76 @@ module Sidekiq
|
|
398
427
|
end
|
399
428
|
end
|
400
429
|
|
430
|
+
class DeadSet < JobSet
|
431
|
+
def initialize
|
432
|
+
super 'dead'
|
433
|
+
end
|
434
|
+
|
435
|
+
def retry_all
|
436
|
+
while size > 0
|
437
|
+
each(&:retry)
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
##
|
443
|
+
# Enumerates the set of Sidekiq processes which are actively working
|
444
|
+
# right now. Each process send a heartbeat to Redis every 5 seconds
|
445
|
+
# so this set should be relatively accurate, barring network partitions.
|
446
|
+
#
|
447
|
+
# Yields a hash of data which looks something like this:
|
448
|
+
#
|
449
|
+
# {
|
450
|
+
# 'hostname' => 'app-1.example.com',
|
451
|
+
# 'started_at' => <process start time>,
|
452
|
+
# 'pid' => 12345,
|
453
|
+
# 'tag' => 'myapp'
|
454
|
+
# 'concurrency' => 25,
|
455
|
+
# 'queues' => ['default', 'low'],
|
456
|
+
# 'busy' => 10,
|
457
|
+
# 'beat' => <last heartbeat>,
|
458
|
+
# }
|
459
|
+
|
460
|
+
class ProcessSet
|
461
|
+
include Enumerable
|
462
|
+
|
463
|
+
def each(&block)
|
464
|
+
procs = Sidekiq.redis { |conn| conn.smembers('processes') }
|
465
|
+
|
466
|
+
to_prune = []
|
467
|
+
sorted = procs.sort
|
468
|
+
Sidekiq.redis do |conn|
|
469
|
+
# We're making a tradeoff here between consuming more memory instead of
|
470
|
+
# making more roundtrips to Redis, but if you have hundreds or thousands of workers,
|
471
|
+
# you'll be happier this way
|
472
|
+
result = conn.pipelined do
|
473
|
+
sorted.each do |key|
|
474
|
+
conn.hmget(key, 'info', 'busy', 'beat')
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
result.each_with_index do |(info, busy, at_s), i|
|
479
|
+
# the hash named key has an expiry of 60 seconds.
|
480
|
+
# if it's not found, that means the process has not reported
|
481
|
+
# in to Redis and probably died.
|
482
|
+
(to_prune << sorted[i]; next) if info.nil?
|
483
|
+
hash = Sidekiq.load_json(info)
|
484
|
+
yield hash.merge('busy' => busy.to_i, 'beat' => at_s.to_f)
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
Sidekiq.redis {|conn| conn.srem('processes', to_prune) } unless to_prune.empty?
|
489
|
+
nil
|
490
|
+
end
|
491
|
+
|
492
|
+
# This method is not guaranteed accurate since it does not prune the set
|
493
|
+
# based on current heartbeat. #each does that and ensures the set only
|
494
|
+
# contains Sidekiq processes which have sent a heartbeat within the last
|
495
|
+
# 60 seconds.
|
496
|
+
def size
|
497
|
+
Sidekiq.redis { |conn| conn.scard('processes') }
|
498
|
+
end
|
499
|
+
end
|
401
500
|
|
402
501
|
##
|
403
502
|
# Programmatic access to the current active worker set.
|
@@ -410,62 +509,50 @@ module Sidekiq
|
|
410
509
|
#
|
411
510
|
# workers = Sidekiq::Workers.new
|
412
511
|
# workers.size => 2
|
413
|
-
# workers.each do |
|
414
|
-
# #
|
512
|
+
# workers.each do |process_id, thread_id, work|
|
513
|
+
# # process_id is a unique identifier per Sidekiq process
|
514
|
+
# # thread_id is a unique identifier per thread
|
415
515
|
# # work is a Hash which looks like:
|
416
516
|
# # { 'queue' => name, 'run_at' => timestamp, 'payload' => msg }
|
417
|
-
# #
|
517
|
+
# # run_at is an epoch Integer.
|
418
518
|
# end
|
419
|
-
|
519
|
+
#
|
420
520
|
class Workers
|
421
521
|
include Enumerable
|
422
522
|
|
423
523
|
def each(&block)
|
424
524
|
Sidekiq.redis do |conn|
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
525
|
+
procs = conn.smembers('processes')
|
526
|
+
procs.sort.each do |key|
|
527
|
+
valid, workers = conn.pipelined do
|
528
|
+
conn.exists(key)
|
529
|
+
conn.hgetall("#{key}:workers")
|
530
|
+
end
|
531
|
+
next unless valid
|
532
|
+
workers.each_pair do |tid, json|
|
533
|
+
yield key, tid, Sidekiq.load_json(json)
|
534
|
+
end
|
431
535
|
end
|
432
536
|
end
|
433
537
|
end
|
434
538
|
|
539
|
+
# Note that #size is only as accurate as Sidekiq's heartbeat,
|
540
|
+
# which happens every 5 seconds. It is NOT real-time.
|
541
|
+
#
|
542
|
+
# Not very efficient if you have lots of Sidekiq
|
543
|
+
# processes but the alternative is a global counter
|
544
|
+
# which can easily get out of sync with crashy processes.
|
435
545
|
def size
|
436
546
|
Sidekiq.redis do |conn|
|
437
|
-
conn.
|
438
|
-
|
439
|
-
end
|
547
|
+
procs = conn.smembers('processes')
|
548
|
+
return 0 if procs.empty?
|
440
549
|
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
#
|
445
|
-
# Returns the number of records removed.
|
446
|
-
def prune(older_than=60*60)
|
447
|
-
to_rem = []
|
448
|
-
Sidekiq.redis do |conn|
|
449
|
-
conn.smembers('workers').each do |w|
|
450
|
-
msg = conn.get("worker:#{w}")
|
451
|
-
if !msg
|
452
|
-
to_rem << w
|
453
|
-
else
|
454
|
-
m = Sidekiq.load_json(msg)
|
455
|
-
run_at = Time.at(m['run_at'])
|
456
|
-
# prune jobs older than one hour
|
457
|
-
if run_at < (Time.now - older_than)
|
458
|
-
to_rem << w
|
459
|
-
else
|
460
|
-
end
|
550
|
+
conn.pipelined do
|
551
|
+
procs.each do |key|
|
552
|
+
conn.hget(key, 'busy')
|
461
553
|
end
|
462
|
-
end
|
463
|
-
end
|
464
|
-
|
465
|
-
if to_rem.size > 0
|
466
|
-
Sidekiq.redis { |conn| conn.srem('workers', to_rem) }
|
554
|
+
end.map(&:to_i).inject(:+)
|
467
555
|
end
|
468
|
-
to_rem.size
|
469
556
|
end
|
470
557
|
end
|
471
558
|
|