sidekiq 2.13.1 → 2.14.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.

data/Changes.md CHANGED
@@ -1,3 +1,14 @@
1
+ 2.14.0
2
+ -----------
3
+
4
+ - Removed slim gem dependency, Web UI now uses ERB [Locke23rus, #1120]
5
+ - Fix more race conditions in Web UI actions
6
+ - Don't reset Job enqueued\_at when retrying
7
+ - Timestamp tooltips in the Web UI should use UTC
8
+ - Fix invalid usage of handle\_exception causing issues in Airbrake
9
+ [#1134]
10
+
11
+
1
12
  2.13.1
2
13
  -----------
3
14
 
@@ -12,7 +23,7 @@
12
23
  ```ruby
13
24
  Sidekiq.configure_server do |config|
14
25
  config.server_middleware do |chain|
15
- chain.add Sidekiq::Middleare::Server::RetryJobs, :max_retries => 10
26
+ chain.add Sidekiq::Middleware::Server::RetryJobs, :max_retries => 10
16
27
  end
17
28
  end
18
29
  ```
data/Contributing.md CHANGED
@@ -3,6 +3,9 @@
3
3
  First of all, thank you for even opening this file up! I hope you find
4
4
  it worthwhile to help out with Sidekiq.
5
5
 
6
+ ## Issues
7
+
8
+ When opening an issue, please include a **backtrace** with your error.
6
9
 
7
10
  ## Code
8
11
 
data/Gemfile CHANGED
@@ -1,17 +1,2 @@
1
- source 'http://rubygems.org'
1
+ source 'https://rubygems.org'
2
2
  gemspec
3
-
4
- gem 'slim', '1.1.0' # earliest verson supported
5
-
6
- gem 'sqlite3', :platform => :mri
7
-
8
- group :test do
9
- gem 'simplecov', :require => false
10
- gem 'minitest-emoji', :require => false
11
- end
12
-
13
- group :development do
14
- gem 'pry', :platform => :mri
15
- gem 'shotgun'
16
- gem 'rack', '~> 1.4.0'
17
- end
data/Pro-Changes.md CHANGED
@@ -3,6 +3,24 @@ Sidekiq Pro Changelog
3
3
 
4
4
  Please see http://sidekiq.org/pro for more details and how to buy.
5
5
 
6
+ 1.2.3
7
+ -----------
8
+
9
+ - Pro now requires Sidekiq 2.14.0
10
+ - Fix bad exception handling in batch callbacks [#1134]
11
+ - Convert Batch UI to ERB
12
+
13
+ 1.2.2
14
+ -----------
15
+
16
+ - Problem with reliable fetch which could lead to lost jobs when Sidekiq
17
+ is shut down normally. Thanks to MikaelAmborn for the report. [#1109]
18
+
19
+ 1.2.1
20
+ -----------
21
+
22
+ - Forgot to push paging code necessary for `delete_job` performance.
23
+
6
24
  1.2.0
7
25
  -----------
8
26
 
data/lib/sidekiq/api.rb CHANGED
@@ -175,7 +175,7 @@ module Sidekiq
175
175
  end
176
176
 
177
177
  def enqueued_at
178
- Time.at(@item['enqueued_at'] || 0)
178
+ Time.at(@item['enqueued_at'] || 0).utc
179
179
  end
180
180
 
181
181
  def queue
@@ -210,7 +210,7 @@ module Sidekiq
210
210
  end
211
211
 
212
212
  def at
213
- Time.at(score)
213
+ Time.at(score).utc
214
214
  end
215
215
 
216
216
  def delete
data/lib/sidekiq/cli.rb CHANGED
@@ -9,10 +9,13 @@ require 'sidekiq'
9
9
  require 'sidekiq/util'
10
10
 
11
11
  module Sidekiq
12
- # Used to raise in workers that have not finished within the
13
- # hard timeout limit. This is needed to rollback db transactions,
12
+ # We are shutting down Sidekiq but what about workers that
13
+ # are working on some long job? This error is
14
+ # raised in workers that have not finished within the hard
15
+ # timeout limit. This is needed to rollback db transactions,
14
16
  # otherwise Ruby's Thread#kill will commit. See #377.
15
- class Shutdown < RuntimeError; end
17
+ # DO NOT RESCUE THIS ERROR.
18
+ class Shutdown < Interrupt; end
16
19
 
17
20
  class CLI
18
21
  include Util
@@ -105,7 +108,7 @@ module Sidekiq
105
108
  when 'USR2'
106
109
  if Sidekiq.options[:logfile]
107
110
  Sidekiq.logger.info "Received USR2, reopening log file"
108
- Sidekiq::Logging.initialize_logger(Sidekiq.options[:logfile])
111
+ initialize_logger
109
112
  end
110
113
  when 'TTIN'
111
114
  Thread.list.each do |thread|
@@ -133,7 +133,7 @@ module Sidekiq
133
133
  end
134
134
 
135
135
  normalized_item['jid'] ||= SecureRandom.hex(12)
136
- normalized_item['enqueued_at'] = Time.now.to_f
136
+ normalized_item['enqueued_at'] ||= Time.now.to_f
137
137
  normalized_item
138
138
  end
139
139
 
@@ -1,39 +1,39 @@
1
1
  module Sidekiq
2
2
  module ExceptionHandler
3
3
 
4
- def handle_exception(ex, msg)
5
- Sidekiq.logger.warn msg
4
+ def handle_exception(ex, ctxHash)
5
+ Sidekiq.logger.warn ctxHash
6
6
  Sidekiq.logger.warn ex
7
7
  Sidekiq.logger.warn ex.backtrace.join("\n")
8
8
  # This list of services is getting a bit ridiculous.
9
9
  # For future error services, please add your own
10
10
  # middleware like BugSnag does:
11
11
  # https://github.com/bugsnag/bugsnag-ruby/blob/master/lib/bugsnag/sidekiq.rb
12
- send_to_airbrake(msg, ex) if defined?(::Airbrake)
13
- send_to_honeybadger(msg, ex) if defined?(::Honeybadger)
14
- send_to_exceptional(msg, ex) if defined?(::Exceptional)
15
- send_to_exception_notifier(msg, ex) if defined?(::ExceptionNotifier)
12
+ send_to_airbrake(ctxHash, ex) if defined?(::Airbrake)
13
+ send_to_honeybadger(ctxHash, ex) if defined?(::Honeybadger)
14
+ send_to_exceptional(ctxHash, ex) if defined?(::Exceptional)
15
+ send_to_exception_notifier(ctxHash, ex) if defined?(::ExceptionNotifier)
16
16
  end
17
17
 
18
18
  private
19
19
 
20
- def send_to_airbrake(msg, ex)
21
- ::Airbrake.notify_or_ignore(ex, :parameters => msg)
20
+ def send_to_airbrake(hash, ex)
21
+ ::Airbrake.notify_or_ignore(ex, :parameters => hash)
22
22
  end
23
23
 
24
- def send_to_honeybadger(msg, ex)
25
- ::Honeybadger.notify_or_ignore(ex, :parameters => msg)
24
+ def send_to_honeybadger(hash, ex)
25
+ ::Honeybadger.notify_or_ignore(ex, :parameters => hash)
26
26
  end
27
27
 
28
- def send_to_exceptional(msg, ex)
28
+ def send_to_exceptional(hash, ex)
29
29
  if ::Exceptional::Config.should_send_to_api?
30
- ::Exceptional.context(msg)
30
+ ::Exceptional.context(hash)
31
31
  ::Exceptional::Remote.error(::Exceptional::ExceptionData.new(ex))
32
32
  end
33
33
  end
34
34
 
35
- def send_to_exception_notifier(msg, ex)
36
- ::ExceptionNotifier.notify_exception(ex, :data => {:message => msg})
35
+ def send_to_exception_notifier(hash, ex)
36
+ ::ExceptionNotifier.notify_exception(ex, :data => {:message => hash})
37
37
  end
38
38
  end
39
39
  end
@@ -116,7 +116,7 @@ module Sidekiq
116
116
  end
117
117
 
118
118
  rescue Exception => e
119
- handle_exception(e, "Error calling retries_exhausted")
119
+ handle_exception(e, { :context => "Error calling retries_exhausted" })
120
120
  end
121
121
 
122
122
  def retry_attempts_from(msg_retry, default)
@@ -1,3 +1,3 @@
1
1
  module Sidekiq
2
- VERSION = "2.13.1"
2
+ VERSION = "2.14.0"
3
3
  end
data/lib/sidekiq/web.rb CHANGED
@@ -1,8 +1,6 @@
1
+ require 'erb'
1
2
  require 'yaml'
2
3
  require 'sinatra/base'
3
- require 'slim'
4
-
5
- raise "The Sidekiq Web UI requires slim 1.1.0 or greater. You have slim v#{Slim::VERSION}" if Gem::Version.new(Slim::VERSION) < Gem::Version.new('1.1.0')
6
4
 
7
5
  require 'sidekiq'
8
6
  require 'sidekiq/api'
@@ -16,7 +14,6 @@ module Sidekiq
16
14
  set :public_folder, Proc.new { "#{root}/assets" }
17
15
  set :views, Proc.new { "#{root}/views" }
18
16
  set :locales, Proc.new { "#{root}/locales" }
19
- set :slim, :pretty => true
20
17
 
21
18
  helpers do
22
19
  def strings
@@ -161,15 +158,19 @@ module Sidekiq
161
158
  def redis_keys
162
159
  ["redis_stats", "uptime_in_days", "connected_clients", "used_memory_human", "used_memory_peak_human"]
163
160
  end
161
+
162
+ def h(text)
163
+ ERB::Util.h(text)
164
+ end
164
165
  end
165
166
 
166
167
  get "/workers" do
167
- slim :index
168
+ erb :index
168
169
  end
169
170
 
170
171
  get "/queues" do
171
172
  @queues = Sidekiq::Queue.all
172
- slim :queues
173
+ erb :queues
173
174
  end
174
175
 
175
176
  get "/queues/:name" do
@@ -178,7 +179,7 @@ module Sidekiq
178
179
  @name = params[:name]
179
180
  (@current_page, @total_size, @messages) = page("queue:#{@name}", params[:page], @count)
180
181
  @messages = @messages.map {|msg| Sidekiq.load_json(msg) }
181
- slim :queue
182
+ erb :queue
182
183
  end
183
184
 
184
185
  post "/reset" do
@@ -200,14 +201,14 @@ module Sidekiq
200
201
  @count = (params[:count] || 25).to_i
201
202
  (@current_page, @total_size, @retries) = page("retry", params[:page], @count)
202
203
  @retries = @retries.map {|msg, score| [Sidekiq.load_json(msg), score] }
203
- slim :retries
204
+ erb :retries
204
205
  end
205
206
 
206
207
  get "/retries/:key" do
207
208
  halt 404 unless params['key']
208
209
  @retry = Sidekiq::RetrySet.new.fetch(*parse_params(params['key'])).first
209
210
  redirect "#{root_path}retries" if @retry.nil?
210
- slim :retry
211
+ erb :retry
211
212
  end
212
213
 
213
214
  post '/retries' do
@@ -238,10 +239,12 @@ module Sidekiq
238
239
  post "/retries/:key" do
239
240
  halt 404 unless params['key']
240
241
  job = Sidekiq::RetrySet.new.fetch(*parse_params(params['key'])).first
241
- if params['retry']
242
- job.retry
243
- elsif params['delete']
244
- job.delete
242
+ if job
243
+ if params['retry']
244
+ job.retry
245
+ elsif params['delete']
246
+ job.delete
247
+ end
245
248
  end
246
249
  redirect "#{root_path}retries"
247
250
  end
@@ -250,14 +253,14 @@ module Sidekiq
250
253
  @count = (params[:count] || 25).to_i
251
254
  (@current_page, @total_size, @scheduled) = page("schedule", params[:page], @count)
252
255
  @scheduled = @scheduled.map {|msg, score| [Sidekiq.load_json(msg), score] }
253
- slim :scheduled
256
+ erb :scheduled
254
257
  end
255
258
 
256
259
  get "/scheduled/:key" do
257
260
  halt 404 unless params['key']
258
261
  @job = Sidekiq::ScheduledSet.new.fetch(*parse_params(params['key'])).first
259
262
  redirect "#{root_path}scheduled" if @job.nil?
260
- slim :scheduled_job_info
263
+ erb :scheduled_job_info
261
264
  end
262
265
 
263
266
  post '/scheduled' do
@@ -265,10 +268,12 @@ module Sidekiq
265
268
 
266
269
  params['key'].each do |key|
267
270
  job = Sidekiq::ScheduledSet.new.fetch(*parse_params(key)).first
268
- if params['delete']
269
- job.delete
270
- elsif params['add_to_queue']
271
- job.add_to_queue
271
+ if job
272
+ if params['delete']
273
+ job.delete
274
+ elsif params['add_to_queue']
275
+ job.add_to_queue
276
+ end
272
277
  end
273
278
  end
274
279
  redirect "#{root_path}scheduled"
@@ -277,10 +282,12 @@ module Sidekiq
277
282
  post "/scheduled/:key" do
278
283
  halt 404 unless params['key']
279
284
  job = Sidekiq::ScheduledSet.new.fetch(*parse_params(params['key'])).first
280
- if params['add_to_queue']
281
- job.add_to_queue
282
- elsif params['delete']
283
- job.delete
285
+ if job
286
+ if params['add_to_queue']
287
+ job.add_to_queue
288
+ elsif params['delete']
289
+ job.delete
290
+ end
284
291
  end
285
292
  redirect "#{root_path}scheduled"
286
293
  end
@@ -290,7 +297,7 @@ module Sidekiq
290
297
  stats_history = Sidekiq::Stats::History.new((params[:days] || 30).to_i)
291
298
  @processed_history = stats_history.processed
292
299
  @failed_history = stats_history.failed
293
- slim :dashboard
300
+ erb :dashboard
294
301
  end
295
302
 
296
303
  get '/dashboard/stats' do
data/sidekiq.gemspec CHANGED
@@ -14,13 +14,12 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "sidekiq"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Sidekiq::VERSION
17
- gem.add_dependency 'redis', '>= 3.0'
17
+ gem.add_dependency 'redis', '>= 3.0.4'
18
18
  gem.add_dependency 'redis-namespace'
19
19
  gem.add_dependency 'connection_pool', '>= 1.0.0'
20
20
  gem.add_dependency 'celluloid', '>= 0.14.1'
21
21
  gem.add_dependency 'json'
22
22
  gem.add_development_dependency 'sinatra'
23
- gem.add_development_dependency 'slim', '>= 1.1.0'
24
23
  gem.add_development_dependency 'minitest', '~> 5'
25
24
  gem.add_development_dependency 'rake'
26
25
  gem.add_development_dependency 'actionmailer'
@@ -0,0 +1,76 @@
1
+ <header>
2
+ <h3><%= t('Job') %></h3>
3
+ </header>
4
+
5
+ <table class="table table-bordered table-striped">
6
+ <tbody>
7
+ <tr>
8
+ <th><%= t('Queue') %></th>
9
+ <td>
10
+ <a href="<%= root_path %>queues/<%= job['queue'] %>"><%= job['queue'] %></a>
11
+ </td>
12
+ </tr>
13
+ <tr>
14
+ <th><%= t('Class') %></th>
15
+ <td>
16
+ <code><%= job['class'] %></code>
17
+ </td>
18
+ </tr>
19
+ <tr>
20
+ <th><%= t('Arguments') %></th>
21
+ <td>
22
+ <code>
23
+ <!-- We don't want to truncate any job arguments when viewing a single job's status page -->
24
+ <div class="args-extended"><%= display_args(job['args'], nil) %></div>
25
+ </code>
26
+ </td>
27
+ </tr>
28
+ <tr>
29
+ <th>JID</th>
30
+ <td>
31
+ <code><%= job.jid %></code>
32
+ </td>
33
+ </tr>
34
+ <tr>
35
+ <th><%= t('Enqueued') %></th>
36
+ <td><%= relative_time(job.enqueued_at) %></td>
37
+ </tr>
38
+ <% unless retry_extra_items(job).empty? %>
39
+ <tr>
40
+ <th><%= t('Extras') %></th>
41
+ <td>
42
+ <code>
43
+ <%= retry_extra_items(job).inspect %>
44
+ </code>
45
+ </td>
46
+ </tr>
47
+ <% end %>
48
+ <% if type == :retry %>
49
+ <% if job['retry_count'] && job['retry_count'] > 0 %>
50
+ <tr>
51
+ <th><%= t('RetryCount') %></th>
52
+ <td><%= job['retry_count'] %></td>
53
+ </tr>
54
+ <tr>
55
+ <th><%= t('LastRetry') %></th>
56
+ <td><%= relative_time(job['retried_at'].is_a?(Numeric) ? Time.at(job['retried_at']) : Time.parse(job['retried_at'])) %></td>
57
+ </tr>
58
+ <% else %>
59
+ <tr>
60
+ <th><%= t('OriginallyFailed') %></th>
61
+ <td><%= relative_time(job['failed_at'].is_a?(Numeric) ? Time.at(job['failed_at']) : Time.parse(job['failed_at'])) %></td>
62
+ </tr>
63
+ <% end %>
64
+ <tr>
65
+ <th><%= t('NextRetry') %></th>
66
+ <td><%= relative_time(job.at) %></td>
67
+ </tr>
68
+ <% end %>
69
+ <% if type == :scheduled %>
70
+ <tr>
71
+ <th><%= t('Scheduled') %></th>
72
+ <td><%= relative_time(job.at) %></td>
73
+ </tr>
74
+ <% end %>
75
+ </tbody>
76
+ </table>
@@ -0,0 +1,23 @@
1
+ <div class="navbar-inner">
2
+ <div class="container">
3
+ <a class="brand" href="<%= root_path %>"><%= Sidekiq::NAME %></a>
4
+ <ul class="nav">
5
+ <% tabs.each do |title, url| %>
6
+ <% if url == '' %>
7
+ <li class="<%= current_path == url ? 'active' : '' %>">
8
+ <a href="<%= root_path %><%= url %>"><%= t(title) %></a>
9
+ </li>
10
+ <% else %>
11
+ <li class="<%= current_path.start_with?(url) ? 'active' : '' %>">
12
+ <a href="<%= root_path %><%= url %>"><%= t(title) %></a>
13
+ </li>
14
+ <% end %>
15
+ <% end %>
16
+ <% custom_tabs.each do |title, url| %>
17
+ <li class="<%= current_path.start_with?(url) ? 'active' : '' %>">
18
+ <a href="<%= root_path %><%= url %>"><%= t(title) %></a>
19
+ </li>
20
+ <% end %>
21
+ </ul>
22
+ </div>
23
+ </div>
@@ -0,0 +1,25 @@
1
+ <% if @total_size > @count %>
2
+ <div class="pagination pagination-right">
3
+ <ul>
4
+ <li class="<%= 'disabled' if @current_page == 1 %>">
5
+ <a href="<%= url %>?page=1">«</a>
6
+ </li>
7
+ <% if @current_page > 1 %>
8
+ <li>
9
+ <a href="<%= url %>?page=<%= @current_page - 1 %>"><%= @current_page - 1 %></a>
10
+ </li>
11
+ <% end %>
12
+ <li class="disabled">
13
+ <a href="<%= url %>?page=<%= @current_page %>"><%= @current_page %></a>
14
+ </li>
15
+ <% if @total_size > @current_page * @count %>
16
+ <li>
17
+ <a href="<%= url %>?page=<%= @current_page + 1 %>"><%= @current_page + 1 %></a>
18
+ </li>
19
+ <% end %>
20
+ <li class="<%= 'disabled' if @total_size <= @current_page * @count %>">
21
+ <a href="<%= url %>?page=<%= (@total_size.to_f / @count).ceil %>">»</a>
22
+ </li>
23
+ </ul>
24
+ </div>
25
+ <% end %>
@@ -0,0 +1,4 @@
1
+ <span class="status">
2
+ <i class="status-sprite status-<%= current_status %>"></i>
3
+ <% t(current_status) %>
4
+ </span>
@@ -0,0 +1,26 @@
1
+ <ul class="unstyled summary row">
2
+ <li class="processed span2">
3
+ <span class="count"><%= number_with_delimiter(stats.processed) %></span>
4
+ <span class="desc"><%= t('Processed') %></span>
5
+ </li>
6
+ <li class="failed span2">
7
+ <span class="count"><%= number_with_delimiter(stats.failed) %></span>
8
+ <span class="desc"><%= t('Failed') %></span>
9
+ </li>
10
+ <li class="busy span2">
11
+ <span class="count"><%= number_with_delimiter(workers_size) %></span>
12
+ <span class="desc"><%= t('Busy') %></span>
13
+ </li>
14
+ <li class="scheduled span2">
15
+ <span class="count"><%= number_with_delimiter(stats.scheduled_size) %></span>
16
+ <span class="desc"><%= t('Scheduled') %></span>
17
+ </li>
18
+ <li class="retries span2">
19
+ <span class="count"><%= number_with_delimiter(stats.retry_size) %></span>
20
+ <span class="desc"><%= t('Retries') %></span>
21
+ </li>
22
+ <li class="enqueued span2">
23
+ <span class="count"><%= number_with_delimiter(stats.enqueued) %></span>
24
+ <span class="desc"><%= t('Enqueued') %></span>
25
+ </li>
26
+ </ul>
@@ -0,0 +1,29 @@
1
+ <table class="workers table table-hover table-bordered table-striped table-white">
2
+ <thead>
3
+ <th><%= t('Worker') %></th>
4
+ <th><%= t('Queue') %></th>
5
+ <th><%= t('Class') %></th>
6
+ <th><%= t('Arguments') %></th>
7
+ <th><%= t('Started') %></th>
8
+ </thead>
9
+ <% workers.each_with_index do |(worker, msg), index| %>
10
+ <tr>
11
+ <td><%= worker %></td>
12
+ <td>
13
+ <a href="<%= root_path %>queues/<%= msg['queue'] %>">= msg['queue'] %></a>
14
+ </td>
15
+
16
+ <td><%= msg['payload']['class'] %></td>
17
+ <td>
18
+ <% if msg['payload']['args'].to_s.size > 100 %>
19
+ <%= msg['payload']['args'].inspect[0..100] + "... " %>
20
+ <button data-toggle="collapse" data-target="#worker_<%= index %>" class="btn btn-mini"><%= t('ShowAll') %></button>
21
+ <div class="toggle" id="worker_<%= index %>" style="display: none;max-width: 750px;"><%= msg['payload']['args'] %></div>
22
+ <% else %>
23
+ <%= msg['payload']['args'] %>
24
+ <% end %>
25
+ </td>
26
+ <td><%= relative_time(msg['run_at'].is_a?(Numeric) ? Time.at(msg['run_at']) : Time.parse(msg['run_at'])) %></td>
27
+ </tr>
28
+ <% end %>
29
+ </table>
@@ -0,0 +1,59 @@
1
+ <script type="text/javascript" src="<%= root_path %>javascripts/dashboard.js"></script>
2
+
3
+ <h3>
4
+ <%= t('Dashboard') %>
5
+ <span class="beacon">
6
+ <span class="ring"></span>
7
+ <span class="dot"></span>
8
+ </span>
9
+ </h3>
10
+
11
+ <h5></h5>
12
+ <div id="realtime"></div>
13
+
14
+ <h5>
15
+ <span class="history-heading"><%= t('History') %></span>
16
+ <a href="<%= root_path %>?days=7" class="history-graph <%= "active" if params[:days] == "7" %>"><%= t('OneWeek') %></a>
17
+ <a href="<%= root_path %>" class="history-graph <%= "active" if params[:days].nil? || params[:days] == "30" %>" ><%= t('OneMonth') %></a>
18
+ <a href="<%= root_path %>?days=90" class="history-graph <%= "active" if params[:days] == "90" %>"><%= t('ThreeMonths') %></a>
19
+ <a href="<%= root_path %>?days=180" class="history-graph <%= "active" if params[:days] == "180" %>"><%= t('SixMonths') %></a>
20
+ </h5>
21
+ <div id="history" data-processed="<%= h Sidekiq.dump_json(@processed_history) %>" data-failed="<%= h Sidekiq.dump_json(@failed_history) %>" data-update-url="<%= root_path %>dashboard/stats"></div>
22
+
23
+ <br/>
24
+ <h5>Redis</h5>
25
+
26
+ <% if @redis_info.fetch("redis_version", nil) %>
27
+ <div class="stat">
28
+ <h3 class="redis_version"><%= @redis_info.fetch("redis_version") %></h3>
29
+ <p><%= t('Version') %></p>
30
+ </div>
31
+ <% end %>
32
+
33
+ <% if @redis_info.fetch("uptime_in_days", nil) %>
34
+ <div class="stat">
35
+ <h3 class="uptime_in_days"><%= @redis_info.fetch("uptime_in_days") %></h3>
36
+ <p><%= t('Uptime') %></p>
37
+ </div>
38
+ <% end %>
39
+
40
+ <% if @redis_info.fetch("connected_clients", nil) %>
41
+ <div class="stat">
42
+ <h3 class="connected_clients"><%= @redis_info.fetch("connected_clients") %></h3>
43
+ <p><%= t('Connections') %></p>
44
+ </div>
45
+ <% end %>
46
+
47
+ <% if @redis_info.fetch("used_memory_human", nil) %>
48
+ <div class="stat">
49
+ <h3 class="used_memory_human"><%= @redis_info.fetch("used_memory_human") %></h3>
50
+ <p><%= t('MemoryUsage') %></p>
51
+ </div>
52
+ <% end %>
53
+
54
+ <% if @redis_info.fetch("used_memory_peak_human", nil) %>
55
+ <div class="stat">
56
+ <h3 class="used_memory_peak_human"><%= @redis_info.fetch("used_memory_peak_human") %></h3>
57
+ <p><%= @redis_info.fetch("used_memory_peak_human") %></p>
58
+ </div>
59
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <div class="row header">
2
+ <div class="span7">
3
+ <h3><%= t('Workers') %></h3>
4
+ </div>
5
+ </div>
6
+
7
+ <%= erb :_workers %>
8
+ <% if workers_size > 0 %>
9
+ <div class="row">
10
+ <div class="span2 pull-right">
11
+ <form action="<%= root_path %>reset" method="post">
12
+ <button class="btn btn-primary btn-block" type="submit"><%= t('ClearWorkerList') %></button>
13
+ </form>
14
+ </div>
15
+ </div>
16
+ <% end %>