sidekiq 6.5.10 → 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.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +568 -18
  3. data/README.md +66 -38
  4. data/bin/kiq +17 -0
  5. data/bin/lint-herb +13 -0
  6. data/bin/multi_queue_bench +271 -0
  7. data/bin/sidekiq +3 -8
  8. data/bin/sidekiqload +215 -118
  9. data/bin/sidekiqmon +3 -0
  10. data/bin/webload +69 -0
  11. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +124 -0
  12. data/lib/generators/sidekiq/job_generator.rb +17 -3
  13. data/lib/generators/sidekiq/templates/job.rb.erb +1 -1
  14. data/lib/sidekiq/api.rb +526 -218
  15. data/lib/sidekiq/capsule.rb +135 -0
  16. data/lib/sidekiq/cli.rb +88 -96
  17. data/lib/sidekiq/client.rb +114 -47
  18. data/lib/sidekiq/component.rb +71 -4
  19. data/lib/sidekiq/config.rb +316 -0
  20. data/lib/sidekiq/deploy.rb +64 -0
  21. data/lib/sidekiq/embedded.rb +64 -0
  22. data/lib/sidekiq/fetch.rb +12 -14
  23. data/lib/sidekiq/iterable_job.rb +56 -0
  24. data/lib/sidekiq/job/interrupt_handler.rb +24 -0
  25. data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
  26. data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
  27. data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
  28. data/lib/sidekiq/job/iterable.rb +322 -0
  29. data/lib/sidekiq/job.rb +397 -10
  30. data/lib/sidekiq/job_logger.rb +14 -11
  31. data/lib/sidekiq/job_retry.rb +78 -28
  32. data/lib/sidekiq/job_util.rb +58 -16
  33. data/lib/sidekiq/launcher.rb +86 -70
  34. data/lib/sidekiq/loader.rb +57 -0
  35. data/lib/sidekiq/logger.rb +25 -94
  36. data/lib/sidekiq/manager.rb +11 -13
  37. data/lib/sidekiq/metrics/query.rb +84 -48
  38. data/lib/sidekiq/metrics/shared.rb +29 -14
  39. data/lib/sidekiq/metrics/tracking.rb +49 -30
  40. data/lib/sidekiq/middleware/chain.rb +19 -18
  41. data/lib/sidekiq/middleware/current_attributes.rb +78 -21
  42. data/lib/sidekiq/middleware/i18n.rb +2 -0
  43. data/lib/sidekiq/middleware/modules.rb +2 -0
  44. data/lib/sidekiq/monitor.rb +15 -5
  45. data/lib/sidekiq/paginator.rb +26 -4
  46. data/lib/sidekiq/processor.rb +78 -65
  47. data/lib/sidekiq/profiler.rb +73 -0
  48. data/lib/sidekiq/rails.rb +45 -60
  49. data/lib/sidekiq/redis_client_adapter.rb +35 -72
  50. data/lib/sidekiq/redis_connection.rb +54 -114
  51. data/lib/sidekiq/ring_buffer.rb +3 -0
  52. data/lib/sidekiq/scheduled.rb +28 -30
  53. data/lib/sidekiq/systemd.rb +2 -0
  54. data/lib/sidekiq/test_api.rb +331 -0
  55. data/lib/sidekiq/testing/inline.rb +2 -30
  56. data/lib/sidekiq/testing.rb +2 -342
  57. data/lib/sidekiq/transaction_aware_client.rb +23 -9
  58. data/lib/sidekiq/tui/controls.rb +53 -0
  59. data/lib/sidekiq/tui/filtering.rb +53 -0
  60. data/lib/sidekiq/tui/tabs/base_tab.rb +230 -0
  61. data/lib/sidekiq/tui/tabs/busy.rb +104 -0
  62. data/lib/sidekiq/tui/tabs/dead.rb +14 -0
  63. data/lib/sidekiq/tui/tabs/home.rb +115 -0
  64. data/lib/sidekiq/tui/tabs/metrics.rb +114 -0
  65. data/lib/sidekiq/tui/tabs/queues.rb +83 -0
  66. data/lib/sidekiq/tui/tabs/retries.rb +14 -0
  67. data/lib/sidekiq/tui/tabs/scheduled.rb +14 -0
  68. data/lib/sidekiq/tui/tabs/set_tab.rb +91 -0
  69. data/lib/sidekiq/tui/tabs.rb +15 -0
  70. data/lib/sidekiq/tui.rb +390 -0
  71. data/lib/sidekiq/version.rb +6 -1
  72. data/lib/sidekiq/web/action.rb +149 -64
  73. data/lib/sidekiq/web/application.rb +379 -281
  74. data/lib/sidekiq/web/config.rb +117 -0
  75. data/lib/sidekiq/web/helpers.rb +190 -71
  76. data/lib/sidekiq/web/router.rb +62 -75
  77. data/lib/sidekiq/web.rb +68 -101
  78. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  79. data/lib/sidekiq.rb +85 -269
  80. data/sidekiq.gemspec +15 -11
  81. data/web/assets/images/logo.png +0 -0
  82. data/web/assets/images/status.png +0 -0
  83. data/web/assets/javascripts/application.js +84 -24
  84. data/web/assets/javascripts/base-charts.js +120 -0
  85. data/web/assets/javascripts/chartjs-adapter-date-fns.min.js +7 -0
  86. data/web/assets/javascripts/dashboard-charts.js +194 -0
  87. data/web/assets/javascripts/dashboard.js +18 -234
  88. data/web/assets/javascripts/metrics.js +138 -120
  89. data/web/assets/stylesheets/style.css +776 -0
  90. data/web/locales/ar.yml +72 -70
  91. data/web/locales/cs.yml +64 -62
  92. data/web/locales/da.yml +62 -53
  93. data/web/locales/de.yml +67 -65
  94. data/web/locales/el.yml +4 -7
  95. data/web/locales/en.yml +89 -71
  96. data/web/locales/es.yml +91 -68
  97. data/web/locales/fa.yml +67 -65
  98. data/web/locales/fr.yml +82 -67
  99. data/web/locales/gd.yml +110 -0
  100. data/web/locales/he.yml +67 -64
  101. data/web/locales/hi.yml +95 -59
  102. data/web/locales/it.yml +94 -54
  103. data/web/locales/ja.yml +69 -70
  104. data/web/locales/ko.yml +54 -52
  105. data/web/locales/lt.yml +68 -66
  106. data/web/locales/nb.yml +63 -61
  107. data/web/locales/nl.yml +54 -52
  108. data/web/locales/pl.yml +47 -45
  109. data/web/locales/{pt-br.yml → pt-BR.yml} +81 -70
  110. data/web/locales/pt.yml +53 -51
  111. data/web/locales/ru.yml +69 -66
  112. data/web/locales/sv.yml +55 -53
  113. data/web/locales/ta.yml +100 -64
  114. data/web/locales/tr.yml +102 -0
  115. data/web/locales/uk.yml +87 -61
  116. data/web/locales/ur.yml +66 -64
  117. data/web/locales/vi.yml +69 -67
  118. data/web/locales/zh-CN.yml +107 -0
  119. data/web/locales/{zh-tw.yml → zh-TW.yml} +13 -3
  120. data/web/views/_footer.html.erb +32 -0
  121. data/web/views/_job_info.html.erb +115 -0
  122. data/web/views/_metrics_period_select.html.erb +15 -0
  123. data/web/views/_nav.html.erb +45 -0
  124. data/web/views/_paging.html.erb +26 -0
  125. data/web/views/_poll_link.html.erb +4 -0
  126. data/web/views/_summary.html.erb +40 -0
  127. data/web/views/busy.html.erb +155 -0
  128. data/web/views/dashboard.html.erb +104 -0
  129. data/web/views/dead.html.erb +38 -0
  130. data/web/views/filtering.html.erb +6 -0
  131. data/web/views/layout.html.erb +26 -0
  132. data/web/views/metrics.html.erb +85 -0
  133. data/web/views/metrics_for_job.html.erb +58 -0
  134. data/web/views/morgue.html.erb +69 -0
  135. data/web/views/profiles.html.erb +43 -0
  136. data/web/views/queue.html.erb +57 -0
  137. data/web/views/queues.html.erb +46 -0
  138. data/web/views/retries.html.erb +77 -0
  139. data/web/views/retry.html.erb +39 -0
  140. data/web/views/scheduled.html.erb +64 -0
  141. data/web/views/{scheduled_job_info.erb → scheduled_job_info.html.erb} +3 -3
  142. metadata +113 -70
  143. data/lib/sidekiq/delay.rb +0 -43
  144. data/lib/sidekiq/extensions/action_mailer.rb +0 -48
  145. data/lib/sidekiq/extensions/active_record.rb +0 -43
  146. data/lib/sidekiq/extensions/class_methods.rb +0 -43
  147. data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
  148. data/lib/sidekiq/metrics/deploy.rb +0 -47
  149. data/lib/sidekiq/web/csrf_protection.rb +0 -180
  150. data/lib/sidekiq/worker.rb +0 -370
  151. data/web/assets/javascripts/graph.js +0 -16
  152. data/web/assets/stylesheets/application-dark.css +0 -143
  153. data/web/assets/stylesheets/application-rtl.css +0 -242
  154. data/web/assets/stylesheets/application.css +0 -999
  155. data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
  156. data/web/assets/stylesheets/bootstrap.css +0 -5
  157. data/web/locales/zh-cn.yml +0 -93
  158. data/web/views/_footer.erb +0 -20
  159. data/web/views/_job_info.erb +0 -89
  160. data/web/views/_nav.erb +0 -52
  161. data/web/views/_paging.erb +0 -23
  162. data/web/views/_poll_link.erb +0 -4
  163. data/web/views/_status.erb +0 -4
  164. data/web/views/_summary.erb +0 -40
  165. data/web/views/busy.erb +0 -137
  166. data/web/views/dashboard.erb +0 -84
  167. data/web/views/dead.erb +0 -34
  168. data/web/views/layout.erb +0 -42
  169. data/web/views/metrics.erb +0 -69
  170. data/web/views/metrics_for_job.erb +0 -87
  171. data/web/views/morgue.erb +0 -78
  172. data/web/views/queue.erb +0 -59
  173. data/web/views/queues.erb +0 -38
  174. data/web/views/retries.erb +0 -83
  175. data/web/views/retry.erb +0 -34
  176. data/web/views/scheduled.erb +0 -57
  177. /data/{LICENSE → LICENSE.txt} +0 -0
@@ -0,0 +1,114 @@
1
+ require_relative "base_tab"
2
+
3
+ module Sidekiq
4
+ class TUI
5
+ module Tabs
6
+ class Metrics < BaseTab
7
+ include Filtering
8
+
9
+ COLORS = %i[light_blue light_cyan light_yellow light_red light_green white gray]
10
+
11
+ def features
12
+ %i[filterable]
13
+ end
14
+
15
+ def on_filter_change
16
+ @data[:metrics_refresh] = nil
17
+ end
18
+
19
+ def regexp
20
+ filtering? ? Regexp.new(Regexp.escape(current_filter), Regexp::IGNORECASE) : nil
21
+ end
22
+
23
+ def refresh_data
24
+ refresh_data_for_stats
25
+
26
+ # only need to refresh every 60 seconds
27
+ if !@data[:metrics_refresh] || @data[:metrics_refresh] < Time.now
28
+ q = Sidekiq::Metrics::Query.new
29
+ query_result = q.top_jobs(class_filter: regexp, minutes: 60)
30
+ @data[:metrics] = query_result
31
+ @data[:metrics_refresh] = Time.now + 60
32
+ end
33
+ end
34
+
35
+ def render(tui, frame, area)
36
+ chunks = stats_content_split(tui, area)
37
+
38
+ render_stats_section(tui, frame, chunks[0])
39
+ render_metrics_chart(tui, frame, chunks[1])
40
+ end
41
+
42
+ # Run to generate metrics data:
43
+ # cd myapp && bundle install
44
+ # bundle exec rake seed_jobs
45
+ # bundle exec sidekiq
46
+ def render_metrics_chart(tui, frame, area)
47
+ y_max = 5
48
+ csize = COLORS.size
49
+ q = @data[:metrics]
50
+ job_results = q.job_results.sort_by { |(kls, jr)| jr.totals["s"] }.reverse.first(COLORS.size)
51
+ # visible_kls = job_results.first(5).map(&:first)
52
+ # chart_data = {
53
+ # series: job_results.map { |(kls, jr)| [kls, jr.dig("series", "s")] }.to_h,
54
+ # marks: query_result.marks.map { |m| [m.bucket, m.label] },
55
+ # starts_at: query_result.starts_at.iso8601,
56
+ # ends_at: query_result.ends_at.iso8601,
57
+ # visibleKls: visible_kls,
58
+ # yLabel: 'TotalExecutionTime',
59
+ # units: 'seconds',
60
+ # markLabel: '*',
61
+ # }
62
+
63
+ datasets = job_results.map.with_index do |(kls, data), idx|
64
+ # log kls, data, idx
65
+ hrdata = data.dig("series", "s")
66
+ tm = Time.now
67
+ tmi = tm.to_i
68
+ tm = Time.at(tmi - (tmi % 60)).utc
69
+ data = Array.new(60) { |idx| idx }.map do |bucket_idx|
70
+ jumpback = bucket_idx * 60
71
+ value = hrdata[(tm - jumpback).iso8601] || 0
72
+ y_max = value if value > y_max
73
+ # we have 60 data points, newest data should be
74
+ # at highest indexes so we have to rejigger the index
75
+ # here
76
+ [59 - bucket_idx, value]
77
+ end
78
+ # log data
79
+
80
+ # log(data)
81
+ tui.dataset(name: kls,
82
+ data: data,
83
+ style: tui.style(fg: COLORS[idx % csize]),
84
+ marker: :dot,
85
+ graph_type: :line)
86
+ end
87
+
88
+ xlabels = [
89
+ q.starts_at.iso8601[11..15],
90
+ q.ends_at.iso8601[11..15]
91
+ ]
92
+
93
+ # beacon_pulse = (Time.now.to_i % 2 == 0) ? "●" : " "
94
+
95
+ chart = tui.chart(
96
+ datasets: datasets,
97
+ x_axis: tui.axis(
98
+ bounds: [0.0, 60.0],
99
+ labels: xlabels,
100
+ style: tui.style(fg: :white)
101
+ ),
102
+ y_axis: chart_y_axis(tui, y_max),
103
+ block: tui.block(
104
+ title: t(name),
105
+ borders: [:all]
106
+ )
107
+ )
108
+
109
+ frame.render_widget(chart, area)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,83 @@
1
+ require_relative "base_tab"
2
+
3
+ module Sidekiq
4
+ class TUI
5
+ module Tabs
6
+ class Queues < BaseTab
7
+ def features
8
+ %i[selectable]
9
+ end
10
+
11
+ def controls
12
+ @controls ||= super + [
13
+ {code: "D", modifiers: ["shift"], display: "D", description: "Delete",
14
+ action: ->(tui, tab) { tab.delete_queue! }, refresh: true},
15
+ {code: "p", description: "Pause/Unpause Queue",
16
+ action: ->(tui, tab) { tab.toggle_pause_queue! }}
17
+ ]
18
+ end
19
+
20
+ def delete_queue!
21
+ each_selection do |qname|
22
+ Sidekiq::Queue.new(qname).clear
23
+ end
24
+ end
25
+
26
+ def toggle_pause_queue!
27
+ return unless Sidekiq.pro?
28
+
29
+ each_selection do |qname|
30
+ queue = Sidekiq::Queue.new(qname)
31
+ if queue.paused?
32
+ queue.unpause!
33
+ else
34
+ queue.pause!
35
+ end
36
+ end
37
+ end
38
+
39
+ def refresh_data
40
+ refresh_data_for_stats
41
+
42
+ queue_summaries = Sidekiq::Stats.new.queue_summaries.sort_by(&:name)
43
+
44
+ selected = Array(@data[:selected])
45
+ queues = queue_summaries.map { |queue_summary|
46
+ row_cells = [
47
+ selected.index(queue_summary.name) ? "✅" : "",
48
+ queue_summary.name,
49
+ queue_summary.size.to_s,
50
+ number_with_delimiter(queue_summary.latency, {precision: 2})
51
+ ]
52
+ row_cells << (queue_summary.paused? ? "✅" : "") if Sidekiq.pro?
53
+ row_cells
54
+ }
55
+
56
+ table_row_ids = queue_summaries.map(&:name)
57
+
58
+ @data[:queues] = queues
59
+ @data[:table] = {row_ids: table_row_ids}
60
+ end
61
+
62
+ def render(tui, frame, area)
63
+ header = ["☑️", "Queue", "Size", "Latency"].map { |x| t(x) }
64
+ header << "Paused?" if Sidekiq.pro?
65
+
66
+ chunks = stats_content_split(tui, area)
67
+
68
+ render_stats_section(tui, frame, chunks[0])
69
+ render_table(tui, frame, chunks[1]) do
70
+ {
71
+ title: t(name),
72
+ header:,
73
+ widths: header.map.with_index { |_, idx|
74
+ tui.constraint_length((idx == 1) ? 60 : 10)
75
+ },
76
+ rows: striped_rows(tui, @data[:queues])
77
+ }
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,14 @@
1
+ require_relative "base_tab"
2
+ require_relative "set_tab"
3
+
4
+ module Sidekiq
5
+ class TUI
6
+ module Tabs
7
+ class Retries < BaseTab
8
+ include SetTab
9
+
10
+ def set_class = Sidekiq::RetrySet
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require_relative "base_tab"
2
+ require_relative "set_tab"
3
+
4
+ module Sidekiq
5
+ class TUI
6
+ module Tabs
7
+ class Scheduled < BaseTab
8
+ include SetTab
9
+
10
+ def set_class = Sidekiq::ScheduledSet
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,91 @@
1
+ module Sidekiq
2
+ class TUI
3
+ module Tabs
4
+ module SetTab
5
+ include Sidekiq::Paginator
6
+ include Filtering
7
+
8
+ def features
9
+ %i[selectable pageable filterable]
10
+ end
11
+
12
+ def controls
13
+ @controls ||= super + [{code: "D", modifiers: ["shift"], display: "D", description: "Delete",
14
+ action: ->(tui, tab) { tab.alter_rows!(:delete) }, refresh: true},
15
+ {code: "R", modifiers: ["shift"], display: "R", description: "Retry",
16
+ action: ->(tui, tab) { tab.alter_rows!(:retry) }, refresh: true},
17
+ {code: "E", modifiers: ["shift"], display: "E", description: "Enqueue",
18
+ action: ->(tui, tab) { tab.alter_rows!(:add_to_queue) }, refresh: true},
19
+ {code: "K", modifiers: ["shift"], display: "K", description: "Kill",
20
+ action: ->(tui, tab) { tab.alter_rows!(:kill) }, refresh: true}]
21
+ end
22
+
23
+ def alter_rows!(action)
24
+ # log(to_s, @data[:selected])
25
+ set = set_class.new
26
+ each_selection do |id|
27
+ score, jid = id.split("|")
28
+ item = set.fetch(score, jid)&.first
29
+ item&.send(action)
30
+ end
31
+ end
32
+
33
+ def refresh_data
34
+ refresh_data_for_stats
35
+ refresh_data_for_set
36
+ end
37
+
38
+ def refresh_data_for_set
39
+ set = set_class.new
40
+ f = current_filter
41
+ pager, rows, current, total = if f && f.size > 2
42
+ rows = set.scan(f).to_a
43
+ sz = rows.size
44
+ [Sidekiq::TUI::PageOptions.new(1, sz), rows, 1, sz]
45
+ else
46
+ pager = @data.dig(:table, :pager) || Sidekiq::TUI::PageOptions.new(1, 25)
47
+ current, total, items = page(set.name, pager.page, pager.size)
48
+ rows = items.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
49
+ [pager, rows, current, total]
50
+ end
51
+
52
+ @data.merge!(
53
+ table: {pager:, rows:, current_page: current, total:,
54
+ next_page: (current * pager.size < total) ? pager.page + 1 : nil,
55
+ row_ids: rows.map { |job| [job.score, job["jid"]].join("|") }}
56
+ )
57
+ end
58
+
59
+ def render(tui, frame, area)
60
+ chunks = stats_content_split(tui, area)
61
+
62
+ render_stats_section(tui, frame, chunks[0])
63
+ render_table(tui, frame, chunks[1]) do
64
+ {
65
+ title: t(name),
66
+ header: ["☑️", "When", "Queue", "Job", "Arguments"].map { |x| t(x) },
67
+ widths: [
68
+ tui.constraint_length(5),
69
+ tui.constraint_length(24),
70
+ tui.constraint_length(20),
71
+ tui.constraint_length(30),
72
+ tui.constraint_fill(1)
73
+ ]
74
+ }.tap do |h|
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
+ ]
83
+ }
84
+ h[:rows] = striped_rows(tui, cells)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,15 @@
1
+ require_relative "tabs/home"
2
+ require_relative "tabs/busy"
3
+ require_relative "tabs/queues"
4
+ require_relative "tabs/scheduled"
5
+ require_relative "tabs/retries"
6
+ require_relative "tabs/dead"
7
+ require_relative "tabs/metrics"
8
+
9
+ module Sidekiq
10
+ class TUI
11
+ module Tabs
12
+ All = Set.new([Home, Busy, Queues, Scheduled, Retries, Dead, Metrics])
13
+ end
14
+ end
15
+ end