sidekiq 5.2.8 → 6.2.2
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/Changes.md +248 -0
- data/LICENSE +1 -1
- data/README.md +18 -34
- data/bin/sidekiq +26 -2
- data/bin/sidekiqload +32 -24
- data/bin/sidekiqmon +8 -0
- data/lib/generators/sidekiq/templates/worker_test.rb.erb +1 -1
- data/lib/generators/sidekiq/worker_generator.rb +21 -13
- data/lib/sidekiq/api.rb +310 -249
- data/lib/sidekiq/cli.rb +144 -180
- data/lib/sidekiq/client.rb +64 -48
- data/lib/sidekiq/delay.rb +5 -6
- data/lib/sidekiq/exception_handler.rb +10 -12
- data/lib/sidekiq/extensions/action_mailer.rb +13 -22
- data/lib/sidekiq/extensions/active_record.rb +13 -10
- data/lib/sidekiq/extensions/class_methods.rb +14 -11
- data/lib/sidekiq/extensions/generic_proxy.rb +6 -4
- data/lib/sidekiq/fetch.rb +38 -31
- data/lib/sidekiq/job.rb +8 -0
- data/lib/sidekiq/job_logger.rb +45 -7
- data/lib/sidekiq/job_retry.rb +64 -67
- data/lib/sidekiq/launcher.rb +146 -60
- data/lib/sidekiq/logger.rb +166 -0
- data/lib/sidekiq/manager.rb +11 -13
- data/lib/sidekiq/middleware/chain.rb +20 -8
- data/lib/sidekiq/middleware/i18n.rb +5 -7
- data/lib/sidekiq/monitor.rb +133 -0
- data/lib/sidekiq/paginator.rb +18 -14
- data/lib/sidekiq/processor.rb +71 -70
- data/lib/sidekiq/rails.rb +29 -37
- data/lib/sidekiq/redis_connection.rb +50 -48
- data/lib/sidekiq/scheduled.rb +35 -30
- data/lib/sidekiq/sd_notify.rb +149 -0
- data/lib/sidekiq/systemd.rb +24 -0
- data/lib/sidekiq/testing/inline.rb +2 -1
- data/lib/sidekiq/testing.rb +36 -27
- data/lib/sidekiq/util.rb +45 -16
- data/lib/sidekiq/version.rb +2 -1
- data/lib/sidekiq/web/action.rb +15 -11
- data/lib/sidekiq/web/application.rb +86 -76
- data/lib/sidekiq/web/csrf_protection.rb +180 -0
- data/lib/sidekiq/web/helpers.rb +114 -86
- data/lib/sidekiq/web/router.rb +23 -19
- data/lib/sidekiq/web.rb +61 -105
- data/lib/sidekiq/worker.rb +126 -102
- data/lib/sidekiq.rb +69 -44
- data/sidekiq.gemspec +23 -16
- data/web/assets/images/apple-touch-icon.png +0 -0
- data/web/assets/javascripts/application.js +25 -27
- data/web/assets/javascripts/dashboard.js +4 -23
- data/web/assets/stylesheets/application-dark.css +147 -0
- data/web/assets/stylesheets/application.css +37 -128
- data/web/locales/ar.yml +8 -2
- data/web/locales/de.yml +14 -2
- data/web/locales/en.yml +5 -0
- data/web/locales/es.yml +18 -2
- data/web/locales/fr.yml +10 -3
- data/web/locales/ja.yml +7 -1
- data/web/locales/lt.yml +83 -0
- data/web/locales/pl.yml +4 -4
- data/web/locales/ru.yml +4 -0
- data/web/locales/vi.yml +83 -0
- data/web/views/_job_info.erb +3 -2
- data/web/views/busy.erb +54 -20
- data/web/views/dashboard.erb +14 -6
- data/web/views/dead.erb +3 -3
- data/web/views/layout.erb +2 -0
- data/web/views/morgue.erb +9 -6
- data/web/views/queue.erb +11 -2
- data/web/views/queues.erb +10 -2
- data/web/views/retries.erb +11 -8
- data/web/views/retry.erb +3 -3
- data/web/views/scheduled.erb +5 -2
- metadata +32 -64
- data/.circleci/config.yml +0 -61
- data/.github/contributing.md +0 -32
- data/.github/issue_template.md +0 -11
- data/.gitignore +0 -15
- data/.travis.yml +0 -11
- data/3.0-Upgrade.md +0 -70
- data/4.0-Upgrade.md +0 -53
- data/5.0-Upgrade.md +0 -56
- data/COMM-LICENSE +0 -97
- data/Ent-Changes.md +0 -238
- data/Gemfile +0 -23
- data/Pro-2.0-Upgrade.md +0 -138
- data/Pro-3.0-Upgrade.md +0 -44
- data/Pro-4.0-Upgrade.md +0 -35
- data/Pro-Changes.md +0 -759
- data/Rakefile +0 -9
- data/bin/sidekiqctl +0 -20
- data/code_of_conduct.md +0 -50
- data/lib/sidekiq/core_ext.rb +0 -1
- data/lib/sidekiq/ctl.rb +0 -221
- data/lib/sidekiq/logging.rb +0 -122
- data/lib/sidekiq/middleware/server/active_record.rb +0 -23
@@ -0,0 +1,166 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
require "time"
|
5
|
+
|
6
|
+
module Sidekiq
|
7
|
+
module Context
|
8
|
+
def self.with(hash)
|
9
|
+
orig_context = current.dup
|
10
|
+
current.merge!(hash)
|
11
|
+
yield
|
12
|
+
ensure
|
13
|
+
Thread.current[:sidekiq_context] = orig_context
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.current
|
17
|
+
Thread.current[:sidekiq_context] ||= {}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module LoggingUtils
|
22
|
+
LEVELS = {
|
23
|
+
"debug" => 0,
|
24
|
+
"info" => 1,
|
25
|
+
"warn" => 2,
|
26
|
+
"error" => 3,
|
27
|
+
"fatal" => 4
|
28
|
+
}
|
29
|
+
LEVELS.default_proc = proc do |_, level|
|
30
|
+
Sidekiq.logger.warn("Invalid log level: #{level.inspect}")
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def debug?
|
35
|
+
level <= 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def info?
|
39
|
+
level <= 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def warn?
|
43
|
+
level <= 2
|
44
|
+
end
|
45
|
+
|
46
|
+
def error?
|
47
|
+
level <= 3
|
48
|
+
end
|
49
|
+
|
50
|
+
def fatal?
|
51
|
+
level <= 4
|
52
|
+
end
|
53
|
+
|
54
|
+
def local_level
|
55
|
+
Thread.current[:sidekiq_log_level]
|
56
|
+
end
|
57
|
+
|
58
|
+
def local_level=(level)
|
59
|
+
case level
|
60
|
+
when Integer
|
61
|
+
Thread.current[:sidekiq_log_level] = level
|
62
|
+
when Symbol, String
|
63
|
+
Thread.current[:sidekiq_log_level] = LEVELS[level.to_s]
|
64
|
+
when nil
|
65
|
+
Thread.current[:sidekiq_log_level] = nil
|
66
|
+
else
|
67
|
+
raise ArgumentError, "Invalid log level: #{level.inspect}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def level
|
72
|
+
local_level || super
|
73
|
+
end
|
74
|
+
|
75
|
+
# Change the thread-local level for the duration of the given block.
|
76
|
+
def log_at(level)
|
77
|
+
old_local_level = local_level
|
78
|
+
self.local_level = level
|
79
|
+
yield
|
80
|
+
ensure
|
81
|
+
self.local_level = old_local_level
|
82
|
+
end
|
83
|
+
|
84
|
+
# Redefined to check severity against #level, and thus the thread-local level, rather than +@level+.
|
85
|
+
# FIXME: Remove when the minimum Ruby version supports overriding Logger#level.
|
86
|
+
def add(severity, message = nil, progname = nil, &block)
|
87
|
+
severity ||= ::Logger::UNKNOWN
|
88
|
+
progname ||= @progname
|
89
|
+
|
90
|
+
return true if @logdev.nil? || severity < level
|
91
|
+
|
92
|
+
if message.nil?
|
93
|
+
if block
|
94
|
+
message = yield
|
95
|
+
else
|
96
|
+
message = progname
|
97
|
+
progname = @progname
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
@logdev.write format_message(format_severity(severity), Time.now, progname, message)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class Logger < ::Logger
|
106
|
+
include LoggingUtils
|
107
|
+
|
108
|
+
def initialize(*args, **kwargs)
|
109
|
+
super
|
110
|
+
self.formatter = Sidekiq.log_formatter
|
111
|
+
end
|
112
|
+
|
113
|
+
module Formatters
|
114
|
+
class Base < ::Logger::Formatter
|
115
|
+
def tid
|
116
|
+
Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
|
117
|
+
end
|
118
|
+
|
119
|
+
def ctx
|
120
|
+
Sidekiq::Context.current
|
121
|
+
end
|
122
|
+
|
123
|
+
def format_context
|
124
|
+
if ctx.any?
|
125
|
+
" " + ctx.compact.map { |k, v|
|
126
|
+
case v
|
127
|
+
when Array
|
128
|
+
"#{k}=#{v.join(",")}"
|
129
|
+
else
|
130
|
+
"#{k}=#{v}"
|
131
|
+
end
|
132
|
+
}.join(" ")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class Pretty < Base
|
138
|
+
def call(severity, time, program_name, message)
|
139
|
+
"#{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context} #{severity}: #{message}\n"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class WithoutTimestamp < Pretty
|
144
|
+
def call(severity, time, program_name, message)
|
145
|
+
"pid=#{::Process.pid} tid=#{tid}#{format_context} #{severity}: #{message}\n"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class JSON < Base
|
150
|
+
def call(severity, time, program_name, message)
|
151
|
+
hash = {
|
152
|
+
ts: time.utc.iso8601(3),
|
153
|
+
pid: ::Process.pid,
|
154
|
+
tid: tid,
|
155
|
+
lvl: severity,
|
156
|
+
msg: message
|
157
|
+
}
|
158
|
+
c = ctx
|
159
|
+
hash["ctx"] = c unless c.empty?
|
160
|
+
|
161
|
+
Sidekiq.dump_json(hash) << "\n"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
data/lib/sidekiq/manager.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'sidekiq/util'
|
3
|
-
require 'sidekiq/processor'
|
4
|
-
require 'sidekiq/fetch'
|
5
|
-
require 'thread'
|
6
|
-
require 'set'
|
7
2
|
|
8
|
-
|
3
|
+
require "sidekiq/util"
|
4
|
+
require "sidekiq/processor"
|
5
|
+
require "sidekiq/fetch"
|
6
|
+
require "set"
|
9
7
|
|
8
|
+
module Sidekiq
|
10
9
|
##
|
11
10
|
# The Manager is the central coordination point in Sidekiq, controlling
|
12
11
|
# the lifecycle of the Processors.
|
@@ -27,7 +26,7 @@ module Sidekiq
|
|
27
26
|
attr_reader :workers
|
28
27
|
attr_reader :options
|
29
28
|
|
30
|
-
def initialize(options={})
|
29
|
+
def initialize(options = {})
|
31
30
|
logger.debug { options.inspect }
|
32
31
|
@options = options
|
33
32
|
@count = options[:concurrency] || 10
|
@@ -36,7 +35,7 @@ module Sidekiq
|
|
36
35
|
@done = false
|
37
36
|
@workers = Set.new
|
38
37
|
@count.times do
|
39
|
-
@workers << Processor.new(self)
|
38
|
+
@workers << Processor.new(self, options)
|
40
39
|
end
|
41
40
|
@plock = Mutex.new
|
42
41
|
end
|
@@ -57,7 +56,7 @@ module Sidekiq
|
|
57
56
|
end
|
58
57
|
|
59
58
|
# hack for quicker development / testing environment #2774
|
60
|
-
PAUSE_TIME =
|
59
|
+
PAUSE_TIME = $stdout.tty? ? 0.1 : 0.5
|
61
60
|
|
62
61
|
def stop(deadline)
|
63
62
|
quiet
|
@@ -91,7 +90,7 @@ module Sidekiq
|
|
91
90
|
@plock.synchronize do
|
92
91
|
@workers.delete(processor)
|
93
92
|
unless @done
|
94
|
-
p = Processor.new(self)
|
93
|
+
p = Processor.new(self, options)
|
95
94
|
@workers << p
|
96
95
|
p.start
|
97
96
|
end
|
@@ -113,7 +112,7 @@ module Sidekiq
|
|
113
112
|
end
|
114
113
|
|
115
114
|
if cleanup.size > 0
|
116
|
-
jobs = cleanup.map {|p| p.job }.compact
|
115
|
+
jobs = cleanup.map { |p| p.job }.compact
|
117
116
|
|
118
117
|
logger.warn { "Terminating #{cleanup.size} busy worker threads" }
|
119
118
|
logger.warn { "Work still in progress #{jobs.inspect}" }
|
@@ -124,7 +123,7 @@ module Sidekiq
|
|
124
123
|
# contract says that jobs are run AT LEAST once. Process termination
|
125
124
|
# is delayed until we're certain the jobs are back in Redis because
|
126
125
|
# it is worse to lose a job than to run it twice.
|
127
|
-
strategy =
|
126
|
+
strategy = @options[:fetch]
|
128
127
|
strategy.bulk_requeue(jobs, @options)
|
129
128
|
end
|
130
129
|
|
@@ -132,6 +131,5 @@ module Sidekiq
|
|
132
131
|
processor.kill
|
133
132
|
end
|
134
133
|
end
|
135
|
-
|
136
134
|
end
|
137
135
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Sidekiq
|
3
4
|
# Middleware is code configured to run before/after
|
4
5
|
# a message is processed. It is patterned after Rack
|
@@ -66,7 +67,6 @@ module Sidekiq
|
|
66
67
|
module Middleware
|
67
68
|
class Chain
|
68
69
|
include Enumerable
|
69
|
-
attr_reader :entries
|
70
70
|
|
71
71
|
def initialize_copy(copy)
|
72
72
|
copy.instance_variable_set(:@entries, entries.dup)
|
@@ -77,21 +77,25 @@ module Sidekiq
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def initialize
|
80
|
-
@entries =
|
80
|
+
@entries = nil
|
81
81
|
yield self if block_given?
|
82
82
|
end
|
83
83
|
|
84
|
+
def entries
|
85
|
+
@entries ||= []
|
86
|
+
end
|
87
|
+
|
84
88
|
def remove(klass)
|
85
89
|
entries.delete_if { |entry| entry.klass == klass }
|
86
90
|
end
|
87
91
|
|
88
92
|
def add(klass, *args)
|
89
|
-
remove(klass)
|
93
|
+
remove(klass)
|
90
94
|
entries << Entry.new(klass, *args)
|
91
95
|
end
|
92
96
|
|
93
97
|
def prepend(klass, *args)
|
94
|
-
remove(klass)
|
98
|
+
remove(klass)
|
95
99
|
entries.insert(0, Entry.new(klass, *args))
|
96
100
|
end
|
97
101
|
|
@@ -106,13 +110,17 @@ module Sidekiq
|
|
106
110
|
i = entries.index { |entry| entry.klass == newklass }
|
107
111
|
new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
|
108
112
|
i = entries.index { |entry| entry.klass == oldklass } || entries.count - 1
|
109
|
-
entries.insert(i+1, new_entry)
|
113
|
+
entries.insert(i + 1, new_entry)
|
110
114
|
end
|
111
115
|
|
112
116
|
def exists?(klass)
|
113
117
|
any? { |entry| entry.klass == klass }
|
114
118
|
end
|
115
119
|
|
120
|
+
def empty?
|
121
|
+
@entries.nil? || @entries.empty?
|
122
|
+
end
|
123
|
+
|
116
124
|
def retrieve
|
117
125
|
map(&:make_new)
|
118
126
|
end
|
@@ -122,8 +130,10 @@ module Sidekiq
|
|
122
130
|
end
|
123
131
|
|
124
132
|
def invoke(*args)
|
125
|
-
|
126
|
-
|
133
|
+
return yield if empty?
|
134
|
+
|
135
|
+
chain = retrieve
|
136
|
+
traverse_chain = proc do
|
127
137
|
if chain.empty?
|
128
138
|
yield
|
129
139
|
else
|
@@ -134,12 +144,14 @@ module Sidekiq
|
|
134
144
|
end
|
135
145
|
end
|
136
146
|
|
147
|
+
private
|
148
|
+
|
137
149
|
class Entry
|
138
150
|
attr_reader :klass
|
139
151
|
|
140
152
|
def initialize(klass, *args)
|
141
153
|
@klass = klass
|
142
|
-
@args
|
154
|
+
@args = args
|
143
155
|
end
|
144
156
|
|
145
157
|
def make_new
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
#
|
3
4
|
# Simple middleware to save the current locale and restore it when the job executes.
|
4
5
|
# Use it by requiring it in your initializer:
|
@@ -9,19 +10,16 @@ module Sidekiq::Middleware::I18n
|
|
9
10
|
# Get the current locale and store it in the message
|
10
11
|
# to be sent to Sidekiq.
|
11
12
|
class Client
|
12
|
-
def call(
|
13
|
-
msg[
|
13
|
+
def call(_worker, msg, _queue, _redis)
|
14
|
+
msg["locale"] ||= I18n.locale
|
14
15
|
yield
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
18
19
|
# Pull the msg locale out and set the current thread to use it.
|
19
20
|
class Server
|
20
|
-
def call(
|
21
|
-
I18n.
|
22
|
-
yield
|
23
|
-
ensure
|
24
|
-
I18n.locale = I18n.default_locale
|
21
|
+
def call(_worker, msg, _queue, &block)
|
22
|
+
I18n.with_locale(msg.fetch("locale", I18n.default_locale), &block)
|
25
23
|
end
|
26
24
|
end
|
27
25
|
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require "sidekiq/api"
|
5
|
+
|
6
|
+
class Sidekiq::Monitor
|
7
|
+
class Status
|
8
|
+
VALID_SECTIONS = %w[all version overview processes queues]
|
9
|
+
COL_PAD = 2
|
10
|
+
|
11
|
+
def display(section = nil)
|
12
|
+
section ||= "all"
|
13
|
+
unless VALID_SECTIONS.include? section
|
14
|
+
puts "I don't know how to check the status of '#{section}'!"
|
15
|
+
puts "Try one of these: #{VALID_SECTIONS.join(", ")}"
|
16
|
+
return
|
17
|
+
end
|
18
|
+
send(section)
|
19
|
+
rescue => e
|
20
|
+
puts "Couldn't get status: #{e}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def all
|
24
|
+
version
|
25
|
+
puts
|
26
|
+
overview
|
27
|
+
puts
|
28
|
+
processes
|
29
|
+
puts
|
30
|
+
queues
|
31
|
+
end
|
32
|
+
|
33
|
+
def version
|
34
|
+
puts "Sidekiq #{Sidekiq::VERSION}"
|
35
|
+
puts Time.now.utc
|
36
|
+
end
|
37
|
+
|
38
|
+
def overview
|
39
|
+
puts "---- Overview ----"
|
40
|
+
puts " Processed: #{delimit stats.processed}"
|
41
|
+
puts " Failed: #{delimit stats.failed}"
|
42
|
+
puts " Busy: #{delimit stats.workers_size}"
|
43
|
+
puts " Enqueued: #{delimit stats.enqueued}"
|
44
|
+
puts " Retries: #{delimit stats.retry_size}"
|
45
|
+
puts " Scheduled: #{delimit stats.scheduled_size}"
|
46
|
+
puts " Dead: #{delimit stats.dead_size}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def processes
|
50
|
+
puts "---- Processes (#{process_set.size}) ----"
|
51
|
+
process_set.each_with_index do |process, index|
|
52
|
+
puts "#{process["identity"]} #{tags_for(process)}"
|
53
|
+
puts " Started: #{Time.at(process["started_at"])} (#{time_ago(process["started_at"])})"
|
54
|
+
puts " Threads: #{process["concurrency"]} (#{process["busy"]} busy)"
|
55
|
+
puts " Queues: #{split_multiline(process["queues"].sort, pad: 11)}"
|
56
|
+
puts "" unless (index + 1) == process_set.size
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def queues
|
61
|
+
puts "---- Queues (#{queue_data.size}) ----"
|
62
|
+
columns = {
|
63
|
+
name: [:ljust, (["name"] + queue_data.map(&:name)).map(&:length).max + COL_PAD],
|
64
|
+
size: [:rjust, (["size"] + queue_data.map(&:size)).map(&:length).max + COL_PAD],
|
65
|
+
latency: [:rjust, (["latency"] + queue_data.map(&:latency)).map(&:length).max + COL_PAD]
|
66
|
+
}
|
67
|
+
columns.each { |col, (dir, width)| print col.to_s.upcase.public_send(dir, width) }
|
68
|
+
puts
|
69
|
+
queue_data.each do |q|
|
70
|
+
columns.each do |col, (dir, width)|
|
71
|
+
print q.send(col).public_send(dir, width)
|
72
|
+
end
|
73
|
+
puts
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def delimit(number)
|
80
|
+
number.to_s.reverse.scan(/.{1,3}/).join(",").reverse
|
81
|
+
end
|
82
|
+
|
83
|
+
def split_multiline(values, opts = {})
|
84
|
+
return "none" unless values
|
85
|
+
pad = opts[:pad] || 0
|
86
|
+
max_length = opts[:max_length] || (80 - pad)
|
87
|
+
out = []
|
88
|
+
line = ""
|
89
|
+
values.each do |value|
|
90
|
+
if (line.length + value.length) > max_length
|
91
|
+
out << line
|
92
|
+
line = " " * pad
|
93
|
+
end
|
94
|
+
line << value + ", "
|
95
|
+
end
|
96
|
+
out << line[0..-3]
|
97
|
+
out.join("\n")
|
98
|
+
end
|
99
|
+
|
100
|
+
def tags_for(process)
|
101
|
+
tags = [
|
102
|
+
process["tag"],
|
103
|
+
process["labels"],
|
104
|
+
(process["quiet"] == "true" ? "quiet" : nil)
|
105
|
+
].flatten.compact
|
106
|
+
tags.any? ? "[#{tags.join("] [")}]" : nil
|
107
|
+
end
|
108
|
+
|
109
|
+
def time_ago(timestamp)
|
110
|
+
seconds = Time.now - Time.at(timestamp)
|
111
|
+
return "just now" if seconds < 60
|
112
|
+
return "a minute ago" if seconds < 120
|
113
|
+
return "#{seconds.floor / 60} minutes ago" if seconds < 3600
|
114
|
+
return "an hour ago" if seconds < 7200
|
115
|
+
"#{seconds.floor / 60 / 60} hours ago"
|
116
|
+
end
|
117
|
+
|
118
|
+
QUEUE_STRUCT = Struct.new(:name, :size, :latency)
|
119
|
+
def queue_data
|
120
|
+
@queue_data ||= Sidekiq::Queue.all.map { |q|
|
121
|
+
QUEUE_STRUCT.new(q.name, q.size.to_s, sprintf("%#.2f", q.latency))
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
def process_set
|
126
|
+
@process_set ||= Sidekiq::ProcessSet.new
|
127
|
+
end
|
128
|
+
|
129
|
+
def stats
|
130
|
+
@stats ||= Sidekiq::Stats.new
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|