sidekiq 8.1.3 → 8.1.4

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: 205ebd1ee2e6fdbe27b860b06a03147e96d77a3abc32481eb3bbf51ce01a64a6
4
- data.tar.gz: 355195d5aadd03117e7eee7bdf144d193a4c9a41d4534b5f05ea6fad847cb7df
3
+ metadata.gz: 44df206459c8280090bcf339baa35fbd6ff2ed2dee6ade615e71caed68e34c77
4
+ data.tar.gz: c3e3238d59e0150d5ab67edf02c4a73eae7b7dfd3a19b72db7354ea4ef8b677c
5
5
  SHA512:
6
- metadata.gz: a592aa88c757173e70ee511388f15642e2aad1347df15252e972cea12e42ab91a0e8df702c3ff9e0069595bb68dae32a6d53318df390563a9ae7cc0df31852dd
7
- data.tar.gz: d10b27d602e3c53fe79a85d21a59d0492831c32c8a67e2a344869c42e22e402ad591f82e3835e14c879a81fe8020430450fecd6a324d008bf33c4592dee23ea9
6
+ metadata.gz: b6d89797767ce01eb6dc86ca8dc88edb96e66c3b407ba66309228ed81b993dfeaf31bf0dd9c00f1a8a26320e983006629330d28101d9a2c735df4897062bccad
7
+ data.tar.gz: a027b597b9b3446555fdf37d6ec0ec35edb208815c73500febbd3f914dcda4da633aa3c05ce678fa90ed25244bcd0b189ad2adab0b733a131f7f14b4a1463ce1
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.4
6
+ ----------
7
+
8
+ - The TTIN signal is undeprecated as the INFO signal is not supported on Linux
9
+ - Show iteration job state on Busy page [#6978]
10
+
5
11
  8.1.3
6
12
  ----------
7
13
 
data/lib/sidekiq/api.rb CHANGED
@@ -478,6 +478,10 @@ module Sidekiq
478
478
  self["jid"]
479
479
  end
480
480
 
481
+ def iterable_state
482
+ @iterable_state ||= Sidekiq::IterableJobQuery.new(jid)[jid]
483
+ end
484
+
481
485
  def bid
482
486
  self["bid"]
483
487
  end
@@ -1397,6 +1401,66 @@ module Sidekiq
1397
1401
  Sidekiq.redis { |c| c.hget(key, "data") }
1398
1402
  end
1399
1403
  end
1404
+
1405
+ # Persisted iteration state from Redis for jobs using Sidekiq::IterableJob.
1406
+ class IterableJobQuery
1407
+ def initialize(jids)
1408
+ @cache = bulk_fetch(jids)
1409
+ end
1410
+
1411
+ def [](jid)
1412
+ @cache[jid]
1413
+ end
1414
+
1415
+ private
1416
+
1417
+ # 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
+ def bulk_fetch(jids)
1420
+ raise ArgumentError unless jids
1421
+ jids_to_fetch = Array(jids).compact.uniq
1422
+ return {} if jids_to_fetch.empty?
1423
+
1424
+ results = Sidekiq.redis do |conn|
1425
+ conn.pipelined do |pipe|
1426
+ jids_to_fetch.each { |jid| pipe.hgetall("it-#{jid}") }
1427
+ end
1428
+ end
1429
+
1430
+ # TODO Requires Ruby 4
1431
+ # states = ::Hash.new(capacity: jids_to_fetch.size)
1432
+ states = {}
1433
+ jids_to_fetch.each_with_index do |jid, i|
1434
+ raw = results[i]
1435
+ next if raw.nil? || raw.empty?
1436
+
1437
+ states[jid] = State.new(jid, raw)
1438
+ end
1439
+ states
1440
+ end
1441
+
1442
+ State = Struct.new(:jid, :raw) do
1443
+ def executions
1444
+ raw["ex"].to_i
1445
+ end
1446
+
1447
+ def runtime
1448
+ raw["rt"].to_f
1449
+ end
1450
+
1451
+ def cursor
1452
+ @cursor ||= begin
1453
+ Sidekiq.load_json(raw["c"])
1454
+ rescue JSON::ParserError
1455
+ @raw["c"]
1456
+ end
1457
+ end
1458
+
1459
+ def cancelled
1460
+ raw["cancelled"]&.to_i
1461
+ end
1462
+ end
1463
+ end
1400
1464
  end
1401
1465
 
1402
1466
  Sidekiq.loader.run_load_hooks(:api)
data/lib/sidekiq/cli.rb CHANGED
@@ -200,9 +200,7 @@ module Sidekiq # :nodoc:
200
200
  cli.logger.info "Received TSTP, no longer accepting new work"
201
201
  cli.launcher.quiet
202
202
  },
203
- # deprecated, use INFO
204
203
  "TTIN" => ->(cli) {
205
- cli.logger.error { "DEPRECATED: Please use the INFO signal for backtraces, support for TTIN will be removed in Sidekiq 9.0." }
206
204
  Thread.list.each do |thread|
207
205
  cli.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
208
206
  if thread.backtrace
@@ -213,6 +211,7 @@ module Sidekiq # :nodoc:
213
211
  end
214
212
  },
215
213
  "INFO" => ->(cli) {
214
+ cli.logger.error { "DEPRECATED: the INFO signal does not work on Linux, use TTIN instead." }
216
215
  Thread.list.each do |thread|
217
216
  cli.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
218
217
  if thread.backtrace
@@ -117,6 +117,11 @@ module Sidekiq
117
117
  # larger than 1000 but YMMV based on network quality, size of job args, etc.
118
118
  # A large number of jobs can cause a bit of Redis command processing latency.
119
119
  #
120
+ # Accepts an `:at` option to schedule the jobs for future execution. It
121
+ # accepts either a single Numeric timestamp (or seconds-from-now) applied
122
+ # to every job, or an Array of Numeric values with the same size as `args`
123
+ # to schedule each job at its corresponding time.
124
+ #
120
125
  # Accepts an additional `:spread_interval` option (in seconds) to randomly spread
121
126
  # the jobs schedule times over the specified interval.
122
127
  #
data/lib/sidekiq/job.rb CHANGED
@@ -313,6 +313,11 @@ module Sidekiq
313
313
  #
314
314
  # +items+ must be an Array of Arrays.
315
315
  #
316
+ # The +:at+ option schedules the jobs for future execution. It accepts
317
+ # either a single Numeric timestamp (or seconds-from-now) applied to every
318
+ # job, or an Array of Numeric values with the same size as +items+ to
319
+ # schedule each job at its corresponding time.
320
+ #
316
321
  # For finer-grained control, use `Sidekiq::Client.push_bulk` directly.
317
322
  #
318
323
  # Example (3 Redis round trips):
@@ -325,6 +330,14 @@ module Sidekiq
325
330
  #
326
331
  # SomeJob.perform_bulk([[1], [2], [3]])
327
332
  #
333
+ # Scheduling every job 60 seconds from now (single Numeric +:at+):
334
+ #
335
+ # SomeJob.perform_bulk([[1], [2], [3]], at: 60)
336
+ #
337
+ # Scheduling each job at its own time (Array +:at+):
338
+ #
339
+ # SomeJob.perform_bulk([[1], [2]], at: [Time.now.to_f + 30, Time.now.to_f + 60])
340
+ #
328
341
  def perform_bulk(*args, **kwargs)
329
342
  Setter.new(self, {}).perform_bulk(*args, **kwargs)
330
343
  end
@@ -154,7 +154,7 @@ module Sidekiq
154
154
 
155
155
  # Format keys and values with spacing
156
156
  keys_line = keys.map { |k| t(k).to_s.ljust(12) }.join(" ")
157
- values_line = values.map { |v| v.to_s.ljust(12) }.join(" ")
157
+ values_line = values.map { |v| number_with_delimiter(v).ljust(12) }.join(" ")
158
158
 
159
159
  frame.render_widget(
160
160
  tui.paragraph(
@@ -165,10 +165,27 @@ module Sidekiq
165
165
  )
166
166
  end
167
167
 
168
- # TODO Implement I18n delimiter
168
+ # [thousands_separator, decimal_separator] per locale.
169
+ # Locales not listed here use the English default [",", "."].
170
+ NUMERIC_SEPARATORS = {
171
+ # period thousands, comma decimal
172
+ "da" => [".", ","], "de" => [".", ","], "el" => [".", ","],
173
+ "es" => [".", ","], "it" => [".", ","], "nl" => [".", ","],
174
+ "pt" => [".", ","], "pt-BR" => [".", ","], "tr" => [".", ","],
175
+ "vi" => [".", ","],
176
+ # space thousands, comma decimal
177
+ "cs" => [" ", ","], "fr" => [" ", ","], "lt" => [" ", ","],
178
+ "nb" => [" ", ","], "pl" => [" ", ","], "ru" => [" ", ","],
179
+ "sv" => [" ", ","], "uk" => [" ", ","]
180
+ }.freeze
181
+
169
182
  def number_with_delimiter(number, options = {})
170
183
  precision = options[:precision] || 0
171
- number.round(precision)
184
+ rounded = number.round(precision)
185
+ thousands, decimal = NUMERIC_SEPARATORS.fetch(@parent.lang, [",", "."])
186
+ integer_part, decimal_part = rounded.to_s.split(".")
187
+ integer_with_sep = integer_part.gsub(/(\d)(?=(\d{3})+(?!\d))/, "\\1#{thousands}")
188
+ (precision > 0) ? "#{integer_with_sep}#{decimal}#{(decimal_part || "").ljust(precision, "0")}" : integer_with_sep
172
189
  end
173
190
 
174
191
  def format_memory(rss_kb)
data/lib/sidekiq/tui.rb CHANGED
@@ -21,6 +21,8 @@ module Sidekiq
21
21
  REFRESH_INTERVAL_SECONDS = 2
22
22
  LOCALE_DIRECTORIES = [File.expand_path("#{File.dirname(__FILE__)}/../../web/locales")]
23
23
 
24
+ attr_reader :lang
25
+
24
26
  # language is meant to be a locale code, e.g.
25
27
  # LANG=en_US.utf-8
26
28
  def initialize(cfg, language: ENV["LANG"] || "en")
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "8.1.3"
4
+ VERSION = "8.1.4"
5
5
  MAJOR = 8
6
6
 
7
7
  def self.gem_version
@@ -92,6 +92,8 @@ module Sidekiq
92
92
  @count = (url_params("count") || 100).to_i
93
93
  (@current_page, @total_size, @workset) = page_items(workset, url_params("page"), @count)
94
94
 
95
+ @iterable_states = Sidekiq::IterableJobQuery.new(workset.map { |_, _, work| work.job.jid })
96
+
95
97
  erb(:busy)
96
98
  end
97
99
 
@@ -226,6 +228,8 @@ module Sidekiq
226
228
  @retries = @retries.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
227
229
  end
228
230
 
231
+ @iterable_states = Sidekiq::IterableJobQuery.new(@retries.map(&:jid))
232
+
229
233
  erb(:retries)
230
234
  end
231
235
 
@@ -78,6 +78,7 @@ module Sidekiq
78
78
  end
79
79
  end
80
80
 
81
+ # TODO Remove
81
82
  def parse_yaml_old(path)
82
83
  require "yaml"
83
84
  YAML.safe_load_file(path)
data/web/locales/gd.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # elements like %{queue} are variables and should not be translated
2
2
  gd:
3
- LanguageName: Gaeilge
3
+ LanguageName: Gàidhlig
4
4
  Actions: Gnìomhan
5
5
  AddToQueue: Cuir ris a’ chiutha
6
6
  AddAllToQueue: Cuir a h-uile ris a’ chiutha
@@ -36,6 +36,8 @@ gd:
36
36
  Jobs: Obraichean
37
37
  Kill: Marbh
38
38
  KillAll: Marbh na h-uile
39
+ Language: Cànan
40
+ LastDashboardUpdateTemplateLiteral: "An t-ath-nuadhachadh mu dheireadh: Air pròiseasadh: PROCESSED_COUNT. Air fàilligeadh: FAILED_COUNT."
39
41
  LastRetry: An oidhirp mu dheireadh
40
42
  Latency: Foillidheachd
41
43
  LivePoll: Ath-nuadhachadh beò
@@ -55,6 +57,7 @@ gd:
55
57
  PeakMemoryUsage: Bàrr cleachdadh a’ chuimhne
56
58
  Plugins: Plugain
57
59
  PollingInterval: Eadaramh an ath-nuadhachaidh
60
+ PollingIntervalMilliseconds: Milidiogan eadaramh an ath-nuadhachaidh
58
61
  Process: Pròiseas
59
62
  Processed: Air pròiseasadh
60
63
  Processes: Pròiseasan
@@ -98,3 +101,10 @@ gd:
98
101
  AvgExecutionTime: Ùine cuibheasach nan gnìomhan
99
102
  Context: Co-theacsa
100
103
  NoJobMetricsFound: Cha deach meatraigeachd o chionn goirid air obair a lorg
104
+ Filter: Criathraich
105
+ AnyJobContent: Susbaint obrach sam bith
106
+ Profiles: Pròifilean
107
+ Data: Dàta
108
+ View: Seall
109
+ Token: Tòcan
110
+ ElapsedTime: An ùine a chaidh seachad
@@ -34,6 +34,14 @@
34
34
  <code><%= job.jid %></code>
35
35
  </td>
36
36
  </tr>
37
+ <% if (state = job.iterable_state) %>
38
+ <tr>
39
+ <th>Iteration</th>
40
+ <td>
41
+ <code>cursor=<%= h(state.cursor.inspect) %>; exec=<%= state.executions %>; rt=<%= number_with_delimiter(state.runtime, precision: 3) %>s</code>
42
+ </td>
43
+ </tr>
44
+ <% end %>
37
45
  <% if job.bid %>
38
46
  <tr>
39
47
  <th>BID</th>
@@ -138,7 +138,12 @@
138
138
  <td>
139
139
  <code><div class="args"><%= display_args(job.display_args) %></div></code>
140
140
  </td>
141
- <td><%= relative_time(work.run_at) %></td>
141
+ <td>
142
+ <%= relative_time(work.run_at) %>
143
+ <% if (state = @iterable_states[job.jid]) %>
144
+ <div><small>cursor=<%= h(truncate(state.cursor.inspect, 100)) %>; exec=<%= state.executions %>; rt=<%= number_with_delimiter(state.runtime, precision: 3) %>s</small></div>
145
+ <% end %>
146
+ </td>
142
147
  </tr>
143
148
  <% end %>
144
149
  </table>
@@ -52,6 +52,9 @@
52
52
  </td>
53
53
  <td>
54
54
  <div><a href="<%= root_path %>retries/<%= job_params(entry.item, entry.score) %>"><%= h truncate("#{entry['error_class']}: #{entry['error_message']}", 200) %></a></div>
55
+ <% if (state = @iterable_states[entry.jid]) %>
56
+ <div><small>cursor=<%= h(truncate(state.cursor.inspect, 100)) %>; exec=<%= state.executions %>; rt=<%= number_with_delimiter(state.runtime, precision: 3) %>s</small></div>
57
+ <% end %>
55
58
  </td>
56
59
  </tr>
57
60
  <% end %>
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.3
4
+ version: 8.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Perham