sidekiq 8.1.6 → 8.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e36b05d6e25f174a0ff8b8a21b4c097a108e4ccf98af88a56ae767cfea1186a
4
- data.tar.gz: c58b4f89a1466bac6c9de05cf5ea2aee0152ac42184946b4d1646c6938b351a8
3
+ metadata.gz: e02b7ba4c390eb2a743bc62a8938cedd64ee940a6e128b2c517d9c3f27649543
4
+ data.tar.gz: 86b1e8925322fb00b3d443c6aa8762d7d06f9a564276bf1b210c0415c8d571b4
5
5
  SHA512:
6
- metadata.gz: 3b8690078001bb93fc6e4fb8fe4d52091d89c317d8dcef56d24247882f4c85dbe986a05ae5451aaf70b9ff25c4acbc89b2d15e909763550bbcb13f36e73b2f7f
7
- data.tar.gz: 3cabd5a3a2861af5faf70c5f67d8eb93c578d58cb7825dc063e350f30ef37dc3e2c51060a58dcf2fca9915df1bb7fcbfd00d81996ba651c29882c8432545b03a
6
+ metadata.gz: 63fd4147cf1a9ed566f47266a0c88eb966756285f4404714f2487783a1b754604ab00d5d9b4514002a116598f6005776c6db305c8d66192ec5c8e9b84af71595
7
+ data.tar.gz: a904f9f6963083f76f5645990f068f31b5f3a1a13376d73744b693d6f2e0e2d551be420d70b9c6f97d3ae3ae6691140d21af6357e208e275228ba0061e92df14
data/Changes.md CHANGED
@@ -2,6 +2,12 @@
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
+ 8.1.7
6
+ ----------
7
+
8
+ - Forward compatibililty with Active Job 8.1 [#7019]
9
+ - Many minor fixes and test improvements [hammadxcm]
10
+
5
11
  8.1.6
6
12
  ----------
7
13
 
data/README.md CHANGED
@@ -3,6 +3,7 @@ Sidekiq
3
3
 
4
4
  [![Gem Version](https://badge.fury.io/rb/sidekiq.svg)](https://rubygems.org/gems/sidekiq)
5
5
  ![Build](https://github.com/sidekiq/sidekiq/workflows/CI/badge.svg)
6
+ [![Ruby Users Forum](https://img.shields.io/discourse/topics?server=https%3A%2F%2Fwww.rubyforum.org&style=flat&logo=discourse&label=Ruby%20Users%20Forum)](https://www.rubyforum.org/tag/sidekiq)
6
7
 
7
8
  Simple, efficient background jobs for Ruby.
8
9
 
@@ -88,6 +89,7 @@ Useful resources:
88
89
  * Product documentation is in the [wiki](https://github.com/sidekiq/sidekiq/wiki).
89
90
  * Occasional announcements are made to the [@sidekiq](https://ruby.social/@sidekiq) Mastodon account.
90
91
  * The [Sidekiq tag](https://stackoverflow.com/questions/tagged/sidekiq) on Stack Overflow has lots of useful Q & A.
92
+ * Join us in the `sidekiq` tag on the [Ruby Users Forum](https://www.rubyforum.org/tag/sidekiq).
91
93
 
92
94
  Every Thursday morning is Sidekiq Office Hour: I video chat and answer questions.
93
95
  See the [Sidekiq support page](https://sidekiq.org/support/) for details.
data/bin/sidekiqload CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ ENV["RUBY_TCP_NO_FAST_FALLBACK"] = "1"
4
+
3
5
  #
4
6
  # bin/sidekiqload is a helpful script to load test and
5
7
  # performance tune Sidekiq's core. It creates 500,000 no-op
@@ -111,7 +111,7 @@ begin
111
111
  end
112
112
 
113
113
  # @api private
114
- def stopping? = !!@@stopping
114
+ def stopping?(job = nil) = !!@@stopping
115
115
 
116
116
  # Defines a class alias for backwards compatibility with enqueued Active Job jobs.
117
117
  # @api private
data/lib/sidekiq/api.rb CHANGED
@@ -234,7 +234,7 @@ module Sidekiq
234
234
  end
235
235
 
236
236
  class History
237
- def initialize(days_previous, start_date = nil, pool: nil)
237
+ def initialize(days_previous, start_date = nil)
238
238
  # we only store five years of data in Redis
239
239
  raise ArgumentError if days_previous < 1 || days_previous > (5 * 365)
240
240
  @days_previous = days_previous
@@ -627,6 +627,7 @@ module Sidekiq
627
627
  Sidekiq.redis do |conn|
628
628
  conn.zincrby(@parent.name, at.to_f - score, Sidekiq.dump_json(@item))
629
629
  end
630
+ @score = at.to_f
630
631
  end
631
632
 
632
633
  # Enqueue this job from the scheduled or dead set so it will
@@ -1415,7 +1416,7 @@ module Sidekiq
1415
1416
  private
1416
1417
 
1417
1418
  # Bulk-fetch iteration state for multiple JIDs in a single Redis pipeline.
1418
- # Returns a Hash of { jid => IterableJobState } for JIDs that have iteration state.
1419
+ # Returns a Hash of { jid => State } for JIDs that have iteration state.
1419
1420
  def bulk_fetch(jids)
1420
1421
  raise ArgumentError unless jids
1421
1422
  jids_to_fetch = Array(jids).compact.uniq
@@ -1452,7 +1453,7 @@ module Sidekiq
1452
1453
  @cursor ||= begin
1453
1454
  Sidekiq.load_json(raw["c"])
1454
1455
  rescue JSON::ParserError
1455
- @raw["c"]
1456
+ raw["c"]
1456
1457
  end
1457
1458
  end
1458
1459
 
@@ -142,9 +142,6 @@ module Sidekiq
142
142
  raise ArgumentError, "Job 'at' must be a Numeric or an Array of Numeric timestamps" if at && (Array(at).empty? || !Array(at).all? { |entry| entry.is_a?(Numeric) })
143
143
  raise ArgumentError, "Job 'at' Array must have same size as 'args' Array" if at.is_a?(Array) && at.size != args.size
144
144
 
145
- # Use a smaller batch size by default for scheduled jobs since adding to sorted sets is more costly.
146
- batch_size = items.delete(:batch_size) || items.delete("batch_size") || (at ? 100 : 1_000)
147
-
148
145
  jid = items.delete("jid")
149
146
  raise ArgumentError, "Explicitly passing 'jid' when pushing more than one job is not supported" if jid && args.size > 1
150
147
 
@@ -159,6 +156,9 @@ module Sidekiq
159
156
  at = args.map { now + rand * spread_interval }
160
157
  end
161
158
 
159
+ # Use a smaller batch size by default for scheduled jobs since adding to sorted sets is more costly.
160
+ batch_size = items.delete(:batch_size) || items.delete("batch_size") || (at ? 100 : 1_000)
161
+
162
162
  normed = normalize_item(items)
163
163
  slice_index = 0
164
164
  result = args.each_slice(batch_size).flat_map do |slice|
@@ -72,8 +72,8 @@ module Sidekiq
72
72
 
73
73
  DEFAULT_MAX_RETRY_ATTEMPTS = 25
74
74
 
75
- def initialize(capsule)
76
- @config = @capsule = capsule
75
+ def initialize(cfg)
76
+ @config = cfg
77
77
  @max_retries = Sidekiq.default_configuration[:max_retries] || DEFAULT_MAX_RETRY_ATTEMPTS
78
78
  @backtrace_cleaner = Sidekiq.default_configuration[:backtrace_cleaner]
79
79
  end
@@ -96,7 +96,7 @@ module Sidekiq
96
96
  if msg["retry"]
97
97
  process_retry(nil, msg, queue, e)
98
98
  else
99
- @capsule.config.death_handlers.each do |handler|
99
+ @config[:death_handlers].each do |handler|
100
100
  handler.call(msg, e)
101
101
  rescue => handler_ex
102
102
  handle_exception(handler_ex, {context: "Error calling death handler", job: msg})
@@ -273,7 +273,7 @@ module Sidekiq
273
273
  end
274
274
 
275
275
  def run_death_handlers(job, exception)
276
- @capsule.config.death_handlers.each do |handler|
276
+ @config[:death_handlers].each do |handler|
277
277
  handler.call(job, exception)
278
278
  rescue => e
279
279
  handle_exception(e, {context: "Error calling death handler", job: job})
@@ -288,8 +288,8 @@ module Sidekiq
288
288
  redis do |conn|
289
289
  conn.multi do |xa|
290
290
  xa.zadd("dead", now.to_s, payload)
291
- xa.zremrangebyscore("dead", "-inf", now - @capsule.config[:dead_timeout_in_seconds])
292
- xa.zremrangebyrank("dead", 0, - @capsule.config[:dead_max_jobs])
291
+ xa.zremrangebyscore("dead", "-inf", now - @config[:dead_timeout_in_seconds])
292
+ xa.zremrangebyrank("dead", 0, - @config[:dead_max_jobs])
293
293
  end
294
294
  end
295
295
  end
@@ -235,7 +235,7 @@ module Sidekiq
235
235
  MEMORY_GRABBER = case RUBY_PLATFORM
236
236
  when /linux/
237
237
  ->(pid) {
238
- IO.readlines("/proc/#{$$}/status").each do |line|
238
+ IO.readlines("/proc/#{pid}/status").each do |line|
239
239
  next unless line.start_with?("VmRSS:")
240
240
  break line.split[1].to_i
241
241
  end
@@ -169,7 +169,12 @@ module Sidekiq
169
169
 
170
170
  def fetch_marks(time_range, granularity)
171
171
  [].tap do |result|
172
- marks = @pool.with { |c| c.hgetall("#{@time.strftime("%Y%m%d")}-marks") }
172
+ days = (time_range.begin.utc.to_date..time_range.end.utc.to_date)
173
+ marks = @pool.with { |c|
174
+ c.pipelined { |pipeline|
175
+ days.each { |day| pipeline.hgetall("#{day.strftime("%Y%m%d")}-marks") }
176
+ }
177
+ }.flat_map(&:to_a)
173
178
 
174
179
  marks.each do |timestamp, label|
175
180
  time = Time.parse(timestamp)
@@ -60,6 +60,9 @@ module Sidekiq
60
60
 
61
61
  def page_items(items, pageidx = 1, page_size = 25)
62
62
  current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
63
+ # A negative page_size makes Array#[] return nil instead of a slice, which
64
+ # would crash callers iterating the result (e.g. the Web UI Busy page).
65
+ page_size = 0 if page_size < 0
63
66
  pageidx = current_page - 1
64
67
  starting = pageidx * page_size
65
68
  items = items.to_a
@@ -30,15 +30,16 @@ module Sidekiq
30
30
  attr_reader :capsule
31
31
 
32
32
  def initialize(capsule, &block)
33
- @config = @capsule = capsule
33
+ @config = capsule.config
34
+ @capsule = capsule
34
35
  @callback = block
35
36
  @down = false
36
37
  @done = false
37
38
  @job = nil
38
39
  @thread = nil
39
40
  @reloader = Sidekiq.default_configuration[:reloader]
40
- @job_logger = (capsule.config[:job_logger] || Sidekiq::JobLogger).new(capsule.config)
41
- @retrier = Sidekiq::JobRetry.new(capsule)
41
+ @job_logger = (capsule.config[:job_logger] || Sidekiq::JobLogger).new(@config)
42
+ @retrier = Sidekiq::JobRetry.new(@capsule)
42
43
  end
43
44
 
44
45
  def terminate(wait = false)
@@ -64,7 +65,7 @@ module Sidekiq
64
65
  end
65
66
 
66
67
  def start
67
- @thread ||= safe_thread("#{config.name}/processor", &method(:run))
68
+ @thread ||= safe_thread("#{capsule.name}/processor", &method(:run))
68
69
  end
69
70
 
70
71
  private
@@ -140,31 +140,57 @@ module Sidekiq
140
140
 
141
141
  def render_stats_section(tui, frame, area)
142
142
  stats = @data[:stats]
143
-
144
143
  keys = ["Processed", "Failed", "Busy", "Enqueued", "Retries", "Scheduled", "Dead"]
145
- values = [
146
- stats[:processed],
147
- stats[:failed],
148
- stats[:busy],
149
- stats[:enqueued],
150
- stats[:retries],
151
- stats[:scheduled],
152
- stats[:dead]
153
- ]
154
-
155
- # Format keys and values with spacing
156
- keys_line = keys.map { |k| t(k).to_s.ljust(12) }.join(" ")
157
- values_line = values.map { |v| number_with_delimiter(v).ljust(12) }.join(" ")
144
+ values = %i[processed failed busy enqueued retries scheduled dead].map { |k| number_with_delimiter(stats[k]) }
145
+ render_kv_section(tui, frame, area, title: "Statistics", keys:, values:)
146
+ end
147
+
148
+ # Render a bordered paragraph with a line of (translated) keys over
149
+ # a line of preformatted values, each column ljust'ed to +width+.
150
+ def render_kv_section(tui, frame, area, title:, keys:, values:, width: 12)
151
+ keys_line = keys.map { |k| t(k).to_s.ljust(width) }.join(" ")
152
+ values_line = values.map { |v| v.to_s.ljust(width) }.join(" ")
158
153
 
159
154
  frame.render_widget(
160
155
  tui.paragraph(
161
156
  text: [keys_line, values_line],
162
- block: tui.block(title: "Statistics", borders: [:all])
157
+ block: tui.block(title:, borders: [:all])
163
158
  ),
164
159
  area
165
160
  )
166
161
  end
167
162
 
163
+ # Split a tab's area into the standard stats header and a fill
164
+ # area for the tab's main content.
165
+ def stats_content_split(tui, area)
166
+ tui.layout_split(
167
+ area,
168
+ direction: :vertical,
169
+ constraints: [
170
+ tui.constraint_length(4), # Stats
171
+ tui.constraint_fill(1) # Content
172
+ ]
173
+ )
174
+ end
175
+
176
+ def striped_rows(tui, rows)
177
+ rows.map.with_index { |cells, idx|
178
+ tui.table_row(
179
+ cells:,
180
+ style: idx.even? ? nil : tui.style(bg: :dark_gray)
181
+ )
182
+ }
183
+ end
184
+
185
+ def chart_y_axis(tui, y_max, num_labels: 5)
186
+ labels = (0...num_labels).map { |i| ((y_max * i) / (num_labels - 1)).round.to_s }
187
+ tui.axis(
188
+ bounds: [0.0, y_max.to_f],
189
+ labels: labels,
190
+ style: tui.style(fg: :white)
191
+ )
192
+ end
193
+
168
194
  # [thousands_separator, decimal_separator] per locale.
169
195
  # Locales not listed here use the English default [",", "."].
170
196
  NUMERIC_SEPARATORS = {
@@ -77,12 +77,7 @@ module Sidekiq
77
77
  tui.constraint_length(6),
78
78
  tui.constraint_length(6)
79
79
  ],
80
- rows: @data[:busy].map.with_index { |cells, idx|
81
- tui.table_row(
82
- cells:,
83
- style: idx.even? ? nil : tui.style(bg: :dark_gray)
84
- )
85
- }
80
+ rows: striped_rows(tui, @data[:busy])
86
81
  }
87
82
  end
88
83
  end
@@ -101,16 +96,7 @@ module Sidekiq
101
96
  values << format_memory(processes.total_rss)
102
97
 
103
98
  keys = %w[Processes Threads Busy Utilization RSS]
104
- keys_line = keys.map { |k| t(k).to_s.ljust(12) }.join(" ")
105
- values_line = values.map { |v| v.to_s.ljust(12) }.join(" ")
106
-
107
- frame.render_widget(
108
- tui.paragraph(
109
- text: [keys_line, values_line],
110
- block: tui.block(title: t("Status"), borders: [:all])
111
- ),
112
- area
113
- )
99
+ render_kv_section(tui, frame, area, title: t("Status"), keys:, values:)
114
100
  end
115
101
  end
116
102
  end
@@ -8,11 +8,6 @@ module Sidekiq
8
8
  include SetTab
9
9
 
10
10
  def set_class = Sidekiq::DeadSet
11
-
12
- def refresh_data
13
- refresh_data_for_stats
14
- refresh_data_for_set
15
- end
16
11
  end
17
12
  end
18
13
  end
@@ -63,31 +63,16 @@ module Sidekiq
63
63
  max_value = [@data[:chart][:deltas][:processed].max, @data[:chart][:deltas][:failed].max, 1].max
64
64
  y_max = [max_value, 5].max
65
65
 
66
- processed_data = @data[:chart][:deltas][:processed].each_with_index.map { |value, idx| [idx.to_f, value.to_f] }
67
- failed_data = @data[:chart][:deltas][:failed].each_with_index.map { |value, idx| [idx.to_f, value.to_f] }
68
-
69
- datasets = [
66
+ datasets = {processed: :green, failed: :red}.map { |key, color|
67
+ data = @data[:chart][:deltas][key].each_with_index.map { |value, idx| [idx.to_f, value.to_f] }
70
68
  tui.dataset(
71
69
  name: "",
72
- data: processed_data,
73
- style: tui.style(fg: :green),
74
- marker: :dot,
75
- graph_type: :line
76
- ),
77
- tui.dataset(
78
- name: "",
79
- data: failed_data,
80
- style: tui.style(fg: :red),
70
+ data: data,
71
+ style: tui.style(fg: color),
81
72
  marker: :dot,
82
73
  graph_type: :line
83
74
  )
84
- ]
85
-
86
- num_labels = 5
87
- y_labels = (0...num_labels).map do |i|
88
- value = ((y_max * i) / (num_labels - 1)).round
89
- value.to_s
90
- end
75
+ }
91
76
 
92
77
  beacon_pulse = (Time.now.to_i % 2 == 0) ? "●" : " "
93
78
 
@@ -98,11 +83,7 @@ module Sidekiq
98
83
  labels: [],
99
84
  style: tui.style(fg: :white)
100
85
  ),
101
- y_axis: tui.axis(
102
- bounds: [0.0, y_max.to_f],
103
- labels: y_labels,
104
- style: tui.style(fg: :white)
105
- ),
86
+ y_axis: chart_y_axis(tui, y_max),
106
87
  block: tui.block(
107
88
  title: "Dashboard #{beacon_pulse}",
108
89
  borders: [:all]
@@ -119,24 +100,14 @@ module Sidekiq
119
100
 
120
101
  keys = ["Version", "Uptime", "Connected Clients", "Memory Usage", "Peak Memory"]
121
102
  values = [
122
- redis_info[:version].to_s,
103
+ redis_info[:version],
123
104
  uptime_value,
124
- redis_info[:connected_clients].to_s,
125
- redis_info[:used_memory].to_s,
126
- redis_info[:peak_memory].to_s
105
+ redis_info[:connected_clients],
106
+ redis_info[:used_memory],
107
+ redis_info[:peak_memory]
127
108
  ]
128
109
 
129
- # Format keys and values with spacing
130
- keys_line = keys.map { |k| t(k).ljust(18) }.join(" ")
131
- values_line = values.map { |v| v.ljust(18) }.join(" ")
132
-
133
- frame.render_widget(
134
- tui.paragraph(
135
- text: [keys_line, values_line],
136
- block: tui.block(title: "Redis Information", borders: [:all])
137
- ),
138
- area
139
- )
110
+ render_kv_section(tui, frame, area, title: "Redis Information", keys:, values:, width: 18)
140
111
  end
141
112
  end
142
113
  end
@@ -33,15 +33,7 @@ module Sidekiq
33
33
  end
34
34
 
35
35
  def render(tui, frame, area)
36
- chunks = tui.layout_split(
37
- area,
38
- direction: :vertical,
39
- constraints: [
40
- tui.constraint_length(4), # Stats
41
- tui.constraint_fill(1) # Chart
42
- # TOOD Table
43
- ]
44
- )
36
+ chunks = stats_content_split(tui, area)
45
37
 
46
38
  render_stats_section(tui, frame, chunks[0])
47
39
  render_metrics_chart(tui, frame, chunks[1])
@@ -93,11 +85,6 @@ module Sidekiq
93
85
  graph_type: :line)
94
86
  end
95
87
 
96
- num_labels = 5
97
- y_labels = (0...num_labels).map do |i|
98
- value = ((y_max * i) / (num_labels - 1)).round
99
- value.to_s
100
- end
101
88
  xlabels = [
102
89
  q.starts_at.iso8601[11..15],
103
90
  q.ends_at.iso8601[11..15]
@@ -112,11 +99,7 @@ module Sidekiq
112
99
  labels: xlabels,
113
100
  style: tui.style(fg: :white)
114
101
  ),
115
- y_axis: tui.axis(
116
- bounds: [0.0, y_max.to_f],
117
- labels: y_labels,
118
- style: tui.style(fg: :white)
119
- ),
102
+ y_axis: chart_y_axis(tui, y_max),
120
103
  block: tui.block(
121
104
  title: t(name),
122
105
  borders: [:all]
@@ -63,14 +63,7 @@ module Sidekiq
63
63
  header = ["☑️", "Queue", "Size", "Latency"].map { |x| t(x) }
64
64
  header << "Paused?" if Sidekiq.pro?
65
65
 
66
- chunks = tui.layout_split(
67
- area,
68
- direction: :vertical,
69
- constraints: [
70
- tui.constraint_length(4), # Stats
71
- tui.constraint_fill(1) # Table
72
- ]
73
- )
66
+ chunks = stats_content_split(tui, area)
74
67
 
75
68
  render_stats_section(tui, frame, chunks[0])
76
69
  render_table(tui, frame, chunks[1]) do
@@ -80,12 +73,7 @@ module Sidekiq
80
73
  widths: header.map.with_index { |_, idx|
81
74
  tui.constraint_length((idx == 1) ? 60 : 10)
82
75
  },
83
- rows: @data[:queues].map.with_index { |cells, idx|
84
- tui.table_row(
85
- cells:,
86
- style: idx.even? ? nil : tui.style(bg: :dark_gray)
87
- )
88
- }
76
+ rows: striped_rows(tui, @data[:queues])
89
77
  }
90
78
  end
91
79
  end
@@ -8,11 +8,6 @@ module Sidekiq
8
8
  include SetTab
9
9
 
10
10
  def set_class = Sidekiq::RetrySet
11
-
12
- def refresh_data
13
- refresh_data_for_stats
14
- refresh_data_for_set
15
- end
16
11
  end
17
12
  end
18
13
  end
@@ -8,11 +8,6 @@ module Sidekiq
8
8
  include SetTab
9
9
 
10
10
  def set_class = Sidekiq::ScheduledSet
11
-
12
- def refresh_data
13
- refresh_data_for_stats
14
- refresh_data_for_set
15
- end
16
11
  end
17
12
  end
18
13
  end
@@ -30,6 +30,11 @@ module Sidekiq
30
30
  end
31
31
  end
32
32
 
33
+ def refresh_data
34
+ refresh_data_for_stats
35
+ refresh_data_for_set
36
+ end
37
+
33
38
  def refresh_data_for_set
34
39
  set = set_class.new
35
40
  f = current_filter
@@ -52,14 +57,7 @@ module Sidekiq
52
57
  end
53
58
 
54
59
  def render(tui, frame, area)
55
- chunks = tui.layout_split(
56
- area,
57
- direction: :vertical,
58
- constraints: [
59
- tui.constraint_length(4), # Stats
60
- tui.constraint_fill(1) # Table
61
- ]
62
- )
60
+ chunks = stats_content_split(tui, area)
63
61
 
64
62
  render_stats_section(tui, frame, chunks[0])
65
63
  render_table(tui, frame, chunks[1]) do
@@ -74,19 +72,16 @@ module Sidekiq
74
72
  tui.constraint_fill(1)
75
73
  ]
76
74
  }.tap do |h|
77
- rows = @data[:table][:rows].map.with_index { |entry, idx|
78
- tui.table_row(
79
- cells: [
80
- selected?(entry) ? "✅" : "",
81
- entry.at,
82
- entry.queue,
83
- entry.display_class,
84
- entry.display_args
85
- ],
86
- style: idx.even? ? nil : tui.style(bg: :dark_gray)
87
- )
75
+ cells = @data[:table][:rows].map { |entry|
76
+ [
77
+ selected?(entry) ? "✅" : "",
78
+ entry.at,
79
+ entry.queue,
80
+ entry.display_class,
81
+ entry.display_args
82
+ ]
88
83
  }
89
- h[:rows] = rows
84
+ h[:rows] = striped_rows(tui, cells)
90
85
  end
91
86
  end
92
87
  end
data/lib/sidekiq/tui.rb CHANGED
@@ -105,63 +105,46 @@ module Sidekiq
105
105
  @tui.constraint_length(4)
106
106
  ]
107
107
  )
108
- content = @tui.block(
109
- title: " #{Sidekiq::NAME} ",
110
- borders: [:all],
111
- title_style: @tui.style(fg: :light_red, modifiers: [:bold]),
112
- children: [
113
- # TODO convert to table
114
- @tui.paragraph(
115
- text: [
116
- @tui.text_line(spans: ["Welcome to the Sidekiq Terminal UI"], alignment: :center),
117
- @tui.text_line(spans: [
118
- @tui.text_span(content: "Global hotkeys")
119
- ]),
120
- @tui.text_line(spans: []),
121
- @tui.text_line(spans: [
122
- @tui.text_span(content: "Esc", style: @hotkey_style),
123
- @tui.text_span(content: ": Close this window")
124
- ]),
125
- @tui.text_line(spans: [
126
- @tui.text_span(content: "←/→", style: @hotkey_style),
127
- @tui.text_span(content: ": Move between tabs")
128
- ]),
129
- @tui.text_line(spans: [
130
- @tui.text_span(content: "h/l", style: @hotkey_style),
131
- @tui.text_span(content: ": Move to prev/next page of data")
132
- ]),
133
- @tui.text_line(spans: [
134
- @tui.text_span(content: "j/k", style: @hotkey_style),
135
- @tui.text_span(content: ": Move to prev/next row in current page")
136
- ]),
137
- @tui.text_line(spans: [
138
- @tui.text_span(content: "x", style: @hotkey_style),
139
- @tui.text_span(content: ": Select/deselect current row")
140
- ]),
141
- @tui.text_line(spans: [
142
- @tui.text_span(content: "A", style: @hotkey_style),
143
- @tui.text_span(content: ": Select/deselect All rows in current page")
144
- ]),
145
- @tui.text_line(spans: [
146
- @tui.text_span(content: "q", style: @hotkey_style),
147
- @tui.text_span(content: ": Quit")
148
- ])
149
- ]
150
- )
151
- ]
108
+
109
+ help_data = [
110
+ ["Esc", "Close this window"],
111
+ ["←/→", "Move between tabs"],
112
+ ["h/l", "Move to prev/next page of data"],
113
+ ["j/k", "Move to prev/next row in current page"],
114
+ ["x", "Select/deselect current row"],
115
+ ["A", "Select/deselect All rows in current page"],
116
+ ["q", "Quit"]
117
+ ]
118
+
119
+ # striped styling like other tables in the app
120
+ help_rows = help_data.map.with_index { |cells, idx|
121
+ @tui.table_row(
122
+ cells: cells,
123
+ style: idx.even? ? nil : @tui.style(bg: :dark_gray)
124
+ )
125
+ }
126
+
127
+ table = @tui.table(
128
+ block: @tui.block(
129
+ title: " #{Sidekiq::NAME} - Welcome to the Sidekiq Terminal UI ",
130
+ borders: [:all],
131
+ title_style: @tui.style(fg: :light_red, modifiers: [:bold])
132
+ ),
133
+ header: ["Key", "Action"],
134
+ rows: help_rows,
135
+ widths: [
136
+ @tui.constraint_length(15),
137
+ @tui.constraint_fill(1)
138
+ ],
139
+ column_spacing: 1
152
140
  )
153
- frame.render_widget(content, main_area)
141
+ frame.render_widget(table, main_area)
154
142
  controls = @tui.block(
155
143
  title: t("Controls"),
156
144
  borders: [:all],
157
145
  children: [
158
146
  @tui.paragraph(
159
- text: [
160
- @tui.text_line(spans: [
161
- @tui.text_span(content: "Esc", style: @hotkey_style),
162
- @tui.text_span(content: ": Close ")
163
- ])
164
- ]
147
+ text: [hotkey_line("Esc", "Close ")]
165
148
  )
166
149
  ]
167
150
  )
@@ -180,27 +163,36 @@ module Sidekiq
180
163
  def render_controls(frame, area)
181
164
  active_keys = current_tab.controls.filter { |hash| hash[:description] }
182
165
 
183
- # Split controls into two lines, 8 is arbitrary
184
- # TODO Dynamically split based on term width?
185
- first = active_keys[...8]
166
+ # Dynamically split controls based on terminal width
167
+ # Estimate space needed: each control needs ~key_length + description_length + 4 chars for ": " and spacing
168
+ available_width = area.width - 4 # Account for borders
169
+
186
170
  lines = []
187
- lines << @tui.text_line(spans: first.map { |hash|
188
- [
189
- @tui.text_span(content: hash[:display] || hash[:code], style: @hotkey_style),
190
- @tui.text_span(content: ": #{t(hash[:description])} ")
191
- ]
192
- }.flatten)
171
+ current_line = []
172
+ current_width = 0
173
+
174
+ active_keys.each do |hash|
175
+ # Estimate control width: key + ": " + description + " " (spacing)
176
+ key_text = hash[:display] || hash[:code]
177
+ control_width = key_text.length + 2 + t(hash[:description]).length + 2
178
+
179
+ if current_width + control_width > available_width && !current_line.empty?
180
+ # Start a new line
181
+ lines << controls_line(current_line)
182
+ current_line = [hash]
183
+ current_width = control_width
184
+ else
185
+ current_line << hash
186
+ current_width += control_width
187
+ end
188
+ end
193
189
 
194
- last = active_keys[8...]
195
- lines << if last && last.size > 0
196
- @tui.text_line(spans: last.map { |hash|
197
- [
198
- @tui.text_span(content: hash[:display] || hash[:code], style: @hotkey_style),
199
- @tui.text_span(content: ": #{t(hash[:description])} ")
200
- ]
201
- }.flatten)
202
- else
203
- @tui.text_line(spans: [])
190
+ # Add the last line
191
+ lines << controls_line(current_line) unless current_line.empty?
192
+
193
+ # Ensure we have at least 2 lines for layout consistency
194
+ while lines.length < 2
195
+ lines << @tui.text_line(spans: [])
204
196
  end
205
197
 
206
198
  footer = [
@@ -225,6 +217,22 @@ module Sidekiq
225
217
  frame.render_widget(controls, area)
226
218
  end
227
219
 
220
+ def hotkey_line(key, description)
221
+ @tui.text_line(spans: [
222
+ @tui.text_span(content: key, style: @hotkey_style),
223
+ @tui.text_span(content: ": #{description}")
224
+ ])
225
+ end
226
+
227
+ def controls_line(keys)
228
+ @tui.text_line(spans: keys.map { |hash|
229
+ [
230
+ @tui.text_span(content: hash[:display] || hash[:code], style: @hotkey_style),
231
+ @tui.text_span(content: ": #{t(hash[:description])} ")
232
+ ]
233
+ }.flatten)
234
+ end
235
+
228
236
  def handle_input
229
237
  # We shouldn't need more than 10 FPS for a data-oriented app.
230
238
  # This throttles down our CPU usage. Default is 60 FPS.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "8.1.6"
4
+ VERSION = "8.1.7"
5
5
  MAJOR = 8
6
6
 
7
7
  def self.gem_version
data/sidekiq.gemspec CHANGED
@@ -20,7 +20,8 @@ Gem::Specification.new do |gem|
20
20
  "documentation_uri" => "https://github.com/sidekiq/sidekiq/wiki",
21
21
  "changelog_uri" => "https://github.com/sidekiq/sidekiq/blob/main/Changes.md",
22
22
  "source_code_uri" => "https://github.com/sidekiq/sidekiq",
23
- "rubygems_mfa_required" => "true"
23
+ "rubygems_mfa_required" => "true",
24
+ "mailing_list_uri" => "https://www.rubyforum.org/tag/sidekiq"
24
25
  }
25
26
 
26
27
  gem.add_dependency "redis-client", ">= 0.29.0"
data/web/locales/en.yml CHANGED
@@ -108,3 +108,4 @@ en:
108
108
  View: View
109
109
  Token: Token
110
110
  ElapsedTime: Elapsed Time
111
+ ClickToLearnMore: Click to learn more
data/web/locales/hi.yml CHANGED
@@ -8,42 +8,57 @@ hi:
8
8
  AreYouSureDeleteJob: क्या आप इस कार्य को हटाना चाहते है?
9
9
  AreYouSureDeleteQueue: क्या आप %{queue} कतार को हटाना चाहते है?
10
10
  Arguments: अर्गुमेन्ट्स्
11
+ BackToApp: ऐप पर वापस जाएँ
11
12
  Busy: व्यस्थ
12
13
  Class: क्लास
13
14
  Connections: कनेक्श्न
15
+ CreatedAt: बनाया गया
14
16
  CurrentMessagesInQueue: <span class='title'>%{queue}</span> कतार मे वर्तमान कार्य
15
17
  Dashboard: डैशबोर्ड
16
18
  Dead: निष्प्राण
17
19
  DeadJobs: निष्प्राण कार्य
18
20
  Delete: हटाओ
19
21
  DeleteAll: सब हटाओ
22
+ Deploy: परिनियोजन
20
23
  Enqueued: कतारबद्ध
21
24
  Error: एरर
22
25
  ErrorBacktrace: एरर बैकट्रेस
23
26
  ErrorClass: एरर क्लास
24
27
  ErrorMessage: एरर संदेश
28
+ ExecutionTime: निष्पादन समय
25
29
  Extras: अतिरिक्त
26
30
  Failed: असफल
27
31
  Failures: असफलता
32
+ Failure: असफलता
28
33
  GoBack: ← पीछे
29
34
  History: वृत्तान्त
30
35
  Job: कार्य
31
36
  Jobs: कार्य
32
37
  Kill: नष्ट करे
38
+ KillAll: सभी को समाप्त करें
39
+ Language: भाषा
40
+ LastDashboardUpdateTemplateLiteral: "नवीनतम पोल: संसाधित: PROCESSED_COUNT. असफल: FAILED_COUNT."
33
41
  LastRetry: अंतिम पुन:प्रयास
42
+ Latency: विलंब
34
43
  LivePoll: लाईव सर्वेक्षण
35
44
  MemoryUsage: मेमरी उपयोग
45
+ Name: नाम
36
46
  Namespace: नामस्थान
37
47
  NextRetry: अगला पुन:प्रयास
38
48
  NoDeadJobsFound: कोई निष्प्राण कार्य नही पाए गए
39
49
  NoRetriesFound: कोई पुनर्प्रयास नही पाए गए
40
50
  NoScheduledFound: कोई परिगणित कार्य नही पाए गए
51
+ NotYetEnqueued: अभी तक कतार में नहीं जोड़ा गया
41
52
  OneMonth: १ महीना
42
53
  OneWeek: १ सप्ताह
43
54
  OriginallyFailed: पहिले से विफल
55
+ Pause: रोकें
44
56
  Paused: थमे हुए
45
57
  PeakMemoryUsage: अधिकतम मेमरी उपयोग
58
+ Plugins: प्लगइन्स
46
59
  PollingInterval: सर्वेक्षण अंतराल
60
+ PollingIntervalMilliseconds: सर्वेक्षण अंतराल (मिलीसेकंड)
61
+ Process: प्रक्रिया
47
62
  Processed: कार्रवाई कृत
48
63
  Processes: प्रोसेसेस्
49
64
  Queue: कतार
@@ -57,6 +72,7 @@ hi:
57
72
  RetryNow: पुन:प्रयास करे
58
73
  Scheduled: परिगणित
59
74
  ScheduledJobs: परिगणित कार्य
75
+ Seconds: सेकंड
60
76
  ShowAll: सब दिखाएं
61
77
  SixMonths: ६ महीने
62
78
  Size: आकार
@@ -65,13 +81,31 @@ hi:
65
81
  Stop: रोको
66
82
  StopAll: सब रोको
67
83
  StopPolling: सर्वेक्षण रोको
84
+ Success: सफल
85
+ Summary: सारांश
68
86
  Thread: थ्रेड
69
87
  Threads: थ्रेड्स्
70
88
  ThreeMonths: ३ महीने
71
89
  Time: समय
90
+ Unpause: पुनः प्रारंभ करें
72
91
  Uptime: उपरिकाल (दिवस)
92
+ Utilization: उपयोग
73
93
  Version: वर्जन
74
94
  When: कब
75
95
  Worker: वर्कर
76
96
  active: सक्रिय
77
97
  idle: निष्क्रिय
98
+ Metrics: मेट्रिक्स
99
+ NoDataFound: कोई डेटा नहीं मिला
100
+ TotalExecutionTime: कुल निष्पादन समय
101
+ AvgExecutionTime: औसत निष्पादन समय
102
+ Context: संदर्भ
103
+ NoJobMetricsFound: हाल के कार्यों के लिए कोई मेट्रिक्स नहीं मिले
104
+ Filter: फ़िल्टर
105
+ AnyJobContent: कोई भी कार्य सामग्री
106
+ Profiles: प्रोफ़ाइल
107
+ Data: डेटा
108
+ View: देखें
109
+ Token: टोकन
110
+ ElapsedTime: बीता हुआ समय
111
+ ClickToLearnMore: अधिक जानने के लिए क्लिक करें
data/web/locales/ta.yml CHANGED
@@ -5,73 +5,107 @@ ta:
5
5
  AddToQueue: வரிசையில் சேர்
6
6
  AddAllToQueue: அனைத்தையும் வரிசையில் சேர்
7
7
  AreYouSure: நீங்கள் உறுதியாக இருக்கிறீர்களா?
8
- AreYouSureDeleteJob: நீ இந்த வேலையை நீக்க வேண்டும் என்று உறுதியாக இருக்கிறீர்களா?
9
- AreYouSureDeleteQueue: நீங்கள் %{queue} வரிசையில் நீக்க வேண்டும் என்பதில் உறுதியாக இருக்கிறீர்களா?
10
- Arguments: வாதங்கள்,
11
- Busy: பணிமிகுதி
8
+ AreYouSureDeleteJob: இந்த பணியை நீக்க விரும்புகிறீர்களா?
9
+ AreYouSureDeleteQueue: "%{queue} வரிசையை நீக்க விரும்புகிறீர்களா? இது வரிசையிலுள்ள அனைத்து பணிகளையும் நீக்கிவிடும், எதிர்காலத்தில் மேலும் பணிகளை அனுப்பினால் இது மீண்டும் தோன்றும்."
10
+ Arguments: அளவுருக்கள்
11
+ BackToApp: பயன்பாட்டிற்குத் திரும்பு
12
+ Busy: செயலில்
12
13
  Class: வகுப்பு
13
14
  Connections: இணைப்புகள்
14
- CurrentMessagesInQueue: தற்போதைய வேலைகள் <span class='title'>%{queue}</span>
15
- Dashboard: டாஷ்போர்டு
16
- Dead: இறந்துபோன
17
- DeadJobs: டெட் வேலைகள்
15
+ CreatedAt: உருவாக்கப்பட்ட நேரம்
16
+ CurrentMessagesInQueue: <span class='title'>%{queue}</span> இல் உள்ள தற்போதைய பணிகள்
17
+ Dashboard: தகவற்பலகை
18
+ Dead: முடிந்தது
19
+ DeadJobs: முடிந்த பணிகள்
18
20
  Delete: நீக்கு
19
- DeleteAll: அனைத்து நீக்கு
20
- Enqueued: வரிசைப்படுத்தப்பட்டவை
21
+ DeleteAll: அனைத்தையும் நீக்கு
22
+ Deploy: வரிசைப்படுத்து
23
+ Enqueued: வரிசையிடப்பட்டது
21
24
  Error: பிழை
22
- ErrorBacktrace: பிழை பின்தேடுலை
25
+ ErrorBacktrace: பிழை பின்தடம்
23
26
  ErrorClass: பிழை வகுப்பு
24
27
  ErrorMessage: பிழை செய்தி
25
- Extras: உபரி
26
- Failed: தோல்வி
27
- Failures: தோல்விகள்
28
- GoBack: பின்புறம்
28
+ ExecutionTime: செயல்படுத்தும் நேரம்
29
+ Extras: கூடுதல்
30
+ Failed: தோல்வியடைந்தது
31
+ Failures: தோல்விகள்
32
+ Failure: தோல்வி
33
+ GoBack: ← பின் செல்
29
34
  History: வரலாறு
30
- Job: வேலை
31
- Jobs: வேலை வாய்ப்புகள்
32
- Kill: கொல்
33
- LastRetry: கடைசியாக, மீண்டும் முயற்சிக்கவும்
34
- LivePoll: நேரடி கணிப்பு
35
+ Job: பணி
36
+ Jobs: பணிகள்
37
+ Kill: நிறுத்து
38
+ KillAll: அனைத்தையும் நிறுத்து
39
+ Language: மொழி
40
+ LastDashboardUpdateTemplateLiteral: "கடைசி சோதனை: செயலாக்கப்பட்டவை: PROCESSED_COUNT. தோல்வியடைந்தவை: FAILED_COUNT."
41
+ LastRetry: கடைசி மறுமுயற்சி
42
+ Latency: தாமதம்
43
+ LivePoll: நேரடி சோதனை
35
44
  MemoryUsage: நினைவக பயன்பாடு
45
+ Name: பெயர்
36
46
  Namespace: பெயர்வெளி
37
- NextRetry: அடுத்த, மீண்டும் முயற்சிக்கவும்
38
- NoDeadJobsFound: இறந்த வேலை எதுவும் இல்லை
39
- NoRetriesFound: இல்லை மீண்டும் காணப்படவில்லை
40
- NoScheduledFound: திட்டமிட்ட வேலைகள் காணப்படவில்லை
47
+ NextRetry: அடுத்த மறுமுயற்சி
48
+ NoDeadJobsFound: முடிந்த பணிகள் எதுவும் கிடைக்கவில்லை
49
+ NoRetriesFound: மறுமுயற்சிகள் எதுவும் கிடைக்கவில்லை
50
+ NoScheduledFound: திட்டமிடப்பட்ட பணிகள் எதுவும் கிடைக்கவில்லை
51
+ NotYetEnqueued: இன்னும் வரிசையிடப்படவில்லை
41
52
  OneMonth: 1 மாதம்
42
53
  OneWeek: 1 வாரம்
43
- OriginallyFailed: முதலில் தோல்வி
44
- Paused: தற்காலிக பணிநிறுத்தம்
45
- PeakMemoryUsage: உச்ச நினைவக பயன்பாடு
46
- PollingInterval: வாக்குப்பதிவு இடைவெளி
47
- Processed: நிறையுற்றது
54
+ OriginallyFailed: முதலில் தோல்வியடைந்தது
55
+ Pause: இடைநிறுத்து
56
+ Paused: இடைநிறுத்தப்பட்டது
57
+ PeakMemoryUsage: அதிகபட்ச நினைவக பயன்பாடு
58
+ Plugins: செருகுநிரல்கள்
59
+ PollingInterval: சோதனை இடைவெளி
60
+ PollingIntervalMilliseconds: சோதனை இடைவெளி மில்லிவினாடிகள்
61
+ Process: செயல்முறை
62
+ Processed: செயலாக்கப்பட்டது
48
63
  Processes: செயல்முறைகள்
49
64
  Queue: வரிசை
50
- Queues: வரிசை
51
- Quiet: அமைதியான
52
- QuietAll: அமைதியான அனைத்து
53
- Realtime: நேரலை
54
- Retries: மீண்டும் முயற்சிக்க,
55
- RetryAll: அனைத்து, மீண்டும் முயற்சிக்கவும்
56
- RetryCount: கணிப்பீடு, மீண்டும் முயற்சிக்கவும்
57
- RetryNow: இப்போது மீண்டும் முயற்சி செய்க
58
- Scheduled: திட்டமிடப்பட்ட
59
- ScheduledJobs: திட்டமிட்ட வேலைகள்
60
- ShowAll: அனைத்து காட்டு
65
+ Queues: வரிசைகள்
66
+ Quiet: அமைதி
67
+ QuietAll: அனைத்தையும் அமைதிப்படுத்து
68
+ Realtime: நிகழ்நேரம்
69
+ Retries: மறுமுயற்சிகள்
70
+ RetryAll: அனைத்தையும் மறுமுயற்சி செய்
71
+ RetryCount: மறுமுயற்சி எண்ணிக்கை
72
+ RetryNow: இப்போது மறுமுயற்சி செய்
73
+ Scheduled: திட்டமிடப்பட்டது
74
+ ScheduledJobs: திட்டமிடப்பட்ட பணிகள்
75
+ Seconds: வினாடிகள்
76
+ ShowAll: அனைத்தையும் காட்டு
61
77
  SixMonths: 6 மாதங்கள்
62
78
  Size: அளவு
63
- Started: தொடங்குதல்
64
- Status: நிலைமை
79
+ Started: தொடங்கப்பட்டது
80
+ Status: நிலை
65
81
  Stop: நிறுத்து
66
- StopAll: நிறுத்து அனைத்து
67
- StopPolling: நிறுத்து வாக்குப்பதிவு
82
+ StopAll: அனைத்தையும் நிறுத்து
83
+ StopPolling: சோதனையை நிறுத்து
84
+ Success: வெற்றி
85
+ Summary: சுருக்கம்
68
86
  Thread: நூல்
69
87
  Threads: நூல்கள்
70
88
  ThreeMonths: 3 மாதங்கள்
71
89
  Time: நேரம்
72
- Uptime: இயக்க நேரம் (நாட்கள்)
90
+ Unpause: இடைநிறுத்தத்தை நீக்கு
91
+ Uptime: இயங்கிய நேரம் (நாட்கள்)
92
+ Utilization: பயன்பாட்டு விகிதம்
73
93
  Version: பதிப்பு
74
- When: எப்பொழுது?
94
+ When: எப்போது
75
95
  Worker: பணியாளர்
76
96
  active: செயலில்
77
- idle: முடங்கு நேரம்
97
+ idle: செயலற்று
98
+ Metrics: அளவீடுகள்
99
+ NoDataFound: தரவு எதுவும் கிடைக்கவில்லை
100
+ TotalExecutionTime: மொத்த செயல்படுத்தும் நேரம்
101
+ AvgExecutionTime: சராசரி செயல்படுத்தும் நேரம்
102
+ Context: சூழல்
103
+ NoJobMetricsFound: சமீபத்திய பணி அளவீடுகள் எதுவும் கிடைக்கவில்லை
104
+ Filter: வடிகட்டி
105
+ AnyJobContent: எந்த பணி உள்ளடக்கமும்
106
+ Profiles: சுயவிவரங்கள்
107
+ Data: தரவு
108
+ View: பார்வை
109
+ Token: டோக்கன்
110
+ ElapsedTime: கடந்த நேரம்
111
+ ClickToLearnMore: மேலும் அறிய கிளிக் செய்யவும்
@@ -11,7 +11,7 @@
11
11
  <p id="serverUtcTime"><%= server_utc_time %></p>
12
12
  </li>
13
13
  <li>
14
- <p><a rel="help" href="https://github.com/sidekiq/sidekiq/wiki">docs</a></p>
14
+ <p><a rel="help" href="https://sidekiq.org/wiki">docs</a></p>
15
15
  </li>
16
16
  <li>
17
17
  <form id="locale-form" action="<%= root_path %>change_locale" method="post">
@@ -9,7 +9,7 @@
9
9
  <tr>
10
10
  <th><%= t('Queue') %></th>
11
11
  <td>
12
- <a href="<%= root_path %>queues/<%= job.queue %>"><%= job.queue %></a>
12
+ <a href="<%= root_path %>queues/<%= CGI.escape(job.queue) %>"><%= h job.queue %></a>
13
13
  </td>
14
14
  </tr>
15
15
  <tr>
@@ -45,7 +45,7 @@
45
45
  <thead>
46
46
  <th><%= t('Name') %></th>
47
47
  <th><%= t('Started') %></th>
48
- <th class="col-sm-1"><%= t('RSS') %><a target="blank" href="https://github.com/sidekiq/sidekiq/wiki/Memory#rss"><span class="info-circle" title="Click to learn more about RSS">?</span></a></th>
48
+ <th class="col-sm-1"><%= t('RSS') %><a target="blank" href="https://sidekiq.org/wiki/Memory#rss"><span class="info-circle" title="<%= t("ClickToLearnMore") %>">?</span></a></th>
49
49
  <th class="col-sm-1"><%= t('Threads') %></th>
50
50
  <th class="col-sm-1"><%= t('Busy') %></th>
51
51
  <th>&nbsp;</th>
@@ -132,7 +132,7 @@
132
132
  <td><%= thread %></td>
133
133
  <td><%= job.jid %></td>
134
134
  <td>
135
- <a href="<%= root_path %>queues/<%= work.queue %>"><%= work.queue %></a>
135
+ <a href="<%= root_path %>queues/<%= CGI.escape(work.queue) %>"><%= h work.queue %></a>
136
136
  </td>
137
137
  <td>
138
138
  <%= job.display_class %>
@@ -7,7 +7,7 @@
7
7
  <header>
8
8
  <h1>
9
9
  <%= t('Metrics') %>
10
- <a target="blank" href="https://github.com/sidekiq/sidekiq/wiki/Metrics"><span class="info-circle" title="Click to learn more about metrics">?</span></a>
10
+ <a target="blank" href="https://sidekiq.org/wiki/Metrics"><span class="info-circle" title="<%= t("ClickToLearnMore") %>">?</span></a>
11
11
  </h1>
12
12
 
13
13
  <%= erb :_metrics_period_select, locals: { periods: @periods, period: @period, path: "#{root_path}metrics" } %>
@@ -15,7 +15,7 @@
15
15
  <h1>
16
16
  <a href="<%= root_path %>metrics?period=<%= @period %>"><%= t('Metrics') %></a> /
17
17
  <%= h @name %>
18
- <a target="blank" href="https://github.com/sidekiq/sidekiq/wiki/Metrics"><span class="info-circle" title="Click to learn more about metrics">?</span></a>
18
+ <a target="blank" href="https://sidekiq.org/wiki/Metrics"><span class="info-circle" title="<%= t("ClickToLearnMore") %>">?</span></a>
19
19
  </h1>
20
20
  <%= erb :_metrics_period_select, locals: { periods: @periods, period: @period, path: "#{root_path}metrics/#{@name}" } %>
21
21
  </header>
@@ -37,7 +37,7 @@
37
37
  <a href="<%= root_path %>morgue/<%= job_params(entry.item, entry.score) %>"><%= relative_time(entry.at) %></a>
38
38
  </td>
39
39
  <td>
40
- <a href="<%= root_path %>queues/<%= entry.queue %>"><%= entry.queue %></a>
40
+ <a href="<%= root_path %>queues/<%= CGI.escape(entry.queue) if entry.queue %>"><%= h entry.queue %></a>
41
41
  </td>
42
42
  <td>
43
43
  <%= entry.display_class %>
@@ -2,7 +2,7 @@
2
2
  <header>
3
3
  <h1>
4
4
  <%= t('Profiles') %>
5
- <a target="blank" href="https://github.com/sidekiq/sidekiq/wiki/Profiling"><span class="info-circle" title="Click to learn more about profiling">?</span></a>
5
+ <a target="blank" href="https://sidekiq.org/wiki/Profiling"><span class="info-circle" title="<%= t("ClickToLearnMore") %>">?</span></a>
6
6
  </h1>
7
7
  </header>
8
8
 
@@ -39,7 +39,7 @@
39
39
  </td>
40
40
  <td><%= entry['retry_count'] %></td>
41
41
  <td>
42
- <a href="<%= root_path %>queues/<%= entry.queue %>"><%= entry.queue %></a>
42
+ <a href="<%= root_path %>queues/<%= CGI.escape(entry.queue) %>"><%= h entry.queue %></a>
43
43
  </td>
44
44
  <td>
45
45
  <%= entry.display_class %>
@@ -36,7 +36,7 @@
36
36
  <a href="<%= root_path %>scheduled/<%= job_params(entry.item, entry.score) %>"><%= relative_time(entry.at) %></a>
37
37
  </td>
38
38
  <td>
39
- <a href="<%= root_path %>queues/<%= entry.queue %>"><%= entry.queue %></a>
39
+ <a href="<%= root_path %>queues/<%= CGI.escape(entry.queue) %>"><%= h entry.queue %></a>
40
40
  </td>
41
41
  <td>
42
42
  <%= entry.display_class %>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.6
4
+ version: 8.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Perham
@@ -247,6 +247,7 @@ metadata:
247
247
  changelog_uri: https://github.com/sidekiq/sidekiq/blob/main/Changes.md
248
248
  source_code_uri: https://github.com/sidekiq/sidekiq
249
249
  rubygems_mfa_required: 'true'
250
+ mailing_list_uri: https://www.rubyforum.org/tag/sidekiq
250
251
  rdoc_options: []
251
252
  require_paths:
252
253
  - lib
@@ -261,7 +262,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
261
262
  - !ruby/object:Gem::Version
262
263
  version: '0'
263
264
  requirements: []
264
- rubygems_version: 4.0.6
265
+ rubygems_version: 4.0.16
265
266
  specification_version: 4
266
267
  summary: Simple, efficient background processing for Ruby
267
268
  test_files: []