sidekiq 4.2.10 → 7.3.10

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 (159) hide show
  1. checksums.yaml +5 -5
  2. data/Changes.md +932 -7
  3. data/LICENSE.txt +9 -0
  4. data/README.md +49 -50
  5. data/bin/multi_queue_bench +271 -0
  6. data/bin/sidekiq +22 -3
  7. data/bin/sidekiqload +218 -116
  8. data/bin/sidekiqmon +11 -0
  9. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +75 -0
  10. data/lib/generators/sidekiq/job_generator.rb +59 -0
  11. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
  12. data/lib/generators/sidekiq/templates/job_spec.rb.erb +6 -0
  13. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  14. data/lib/sidekiq/api.rb +710 -322
  15. data/lib/sidekiq/capsule.rb +132 -0
  16. data/lib/sidekiq/cli.rb +268 -248
  17. data/lib/sidekiq/client.rb +153 -101
  18. data/lib/sidekiq/component.rb +90 -0
  19. data/lib/sidekiq/config.rb +311 -0
  20. data/lib/sidekiq/deploy.rb +64 -0
  21. data/lib/sidekiq/embedded.rb +63 -0
  22. data/lib/sidekiq/fetch.rb +50 -42
  23. data/lib/sidekiq/iterable_job.rb +55 -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 +294 -0
  29. data/lib/sidekiq/job.rb +385 -0
  30. data/lib/sidekiq/job_logger.rb +52 -0
  31. data/lib/sidekiq/job_retry.rb +305 -0
  32. data/lib/sidekiq/job_util.rb +109 -0
  33. data/lib/sidekiq/launcher.rb +208 -108
  34. data/lib/sidekiq/logger.rb +131 -0
  35. data/lib/sidekiq/manager.rb +43 -47
  36. data/lib/sidekiq/metrics/query.rb +158 -0
  37. data/lib/sidekiq/metrics/shared.rb +106 -0
  38. data/lib/sidekiq/metrics/tracking.rb +148 -0
  39. data/lib/sidekiq/middleware/chain.rb +113 -56
  40. data/lib/sidekiq/middleware/current_attributes.rb +128 -0
  41. data/lib/sidekiq/middleware/i18n.rb +9 -7
  42. data/lib/sidekiq/middleware/modules.rb +23 -0
  43. data/lib/sidekiq/monitor.rb +147 -0
  44. data/lib/sidekiq/paginator.rb +33 -15
  45. data/lib/sidekiq/processor.rb +188 -98
  46. data/lib/sidekiq/rails.rb +53 -92
  47. data/lib/sidekiq/redis_client_adapter.rb +114 -0
  48. data/lib/sidekiq/redis_connection.rb +86 -77
  49. data/lib/sidekiq/ring_buffer.rb +32 -0
  50. data/lib/sidekiq/scheduled.rb +140 -51
  51. data/lib/sidekiq/sd_notify.rb +149 -0
  52. data/lib/sidekiq/systemd.rb +26 -0
  53. data/lib/sidekiq/testing/inline.rb +6 -5
  54. data/lib/sidekiq/testing.rb +95 -85
  55. data/lib/sidekiq/transaction_aware_client.rb +59 -0
  56. data/lib/sidekiq/version.rb +7 -1
  57. data/lib/sidekiq/web/action.rb +40 -18
  58. data/lib/sidekiq/web/application.rb +189 -89
  59. data/lib/sidekiq/web/csrf_protection.rb +183 -0
  60. data/lib/sidekiq/web/helpers.rb +239 -101
  61. data/lib/sidekiq/web/router.rb +28 -21
  62. data/lib/sidekiq/web.rb +123 -110
  63. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  64. data/lib/sidekiq.rb +97 -185
  65. data/sidekiq.gemspec +26 -27
  66. data/web/assets/images/apple-touch-icon.png +0 -0
  67. data/web/assets/javascripts/application.js +157 -61
  68. data/web/assets/javascripts/base-charts.js +106 -0
  69. data/web/assets/javascripts/chart.min.js +13 -0
  70. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  71. data/web/assets/javascripts/dashboard-charts.js +194 -0
  72. data/web/assets/javascripts/dashboard.js +43 -280
  73. data/web/assets/javascripts/metrics.js +298 -0
  74. data/web/assets/stylesheets/application-dark.css +147 -0
  75. data/web/assets/stylesheets/application-rtl.css +163 -0
  76. data/web/assets/stylesheets/application.css +176 -196
  77. data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  78. data/web/assets/stylesheets/bootstrap.css +2 -2
  79. data/web/locales/ar.yml +87 -0
  80. data/web/locales/cs.yml +62 -62
  81. data/web/locales/da.yml +60 -53
  82. data/web/locales/de.yml +65 -53
  83. data/web/locales/el.yml +43 -24
  84. data/web/locales/en.yml +88 -64
  85. data/web/locales/es.yml +70 -53
  86. data/web/locales/fa.yml +65 -64
  87. data/web/locales/fr.yml +82 -62
  88. data/web/locales/gd.yml +98 -0
  89. data/web/locales/he.yml +80 -0
  90. data/web/locales/hi.yml +59 -59
  91. data/web/locales/it.yml +85 -54
  92. data/web/locales/ja.yml +74 -62
  93. data/web/locales/ko.yml +52 -52
  94. data/web/locales/lt.yml +83 -0
  95. data/web/locales/nb.yml +61 -61
  96. data/web/locales/nl.yml +52 -52
  97. data/web/locales/pl.yml +45 -45
  98. data/web/locales/pt-br.yml +82 -55
  99. data/web/locales/pt.yml +51 -51
  100. data/web/locales/ru.yml +68 -63
  101. data/web/locales/sv.yml +53 -53
  102. data/web/locales/ta.yml +60 -60
  103. data/web/locales/tr.yml +100 -0
  104. data/web/locales/uk.yml +85 -61
  105. data/web/locales/ur.yml +80 -0
  106. data/web/locales/vi.yml +83 -0
  107. data/web/locales/zh-cn.yml +42 -16
  108. data/web/locales/zh-tw.yml +41 -8
  109. data/web/views/_footer.erb +20 -3
  110. data/web/views/_job_info.erb +21 -4
  111. data/web/views/_metrics_period_select.erb +12 -0
  112. data/web/views/_nav.erb +5 -19
  113. data/web/views/_paging.erb +3 -1
  114. data/web/views/_poll_link.erb +3 -6
  115. data/web/views/_summary.erb +7 -7
  116. data/web/views/busy.erb +85 -31
  117. data/web/views/dashboard.erb +53 -20
  118. data/web/views/dead.erb +3 -3
  119. data/web/views/filtering.erb +6 -0
  120. data/web/views/layout.erb +17 -6
  121. data/web/views/metrics.erb +90 -0
  122. data/web/views/metrics_for_job.erb +59 -0
  123. data/web/views/morgue.erb +15 -16
  124. data/web/views/queue.erb +35 -25
  125. data/web/views/queues.erb +20 -4
  126. data/web/views/retries.erb +19 -16
  127. data/web/views/retry.erb +3 -3
  128. data/web/views/scheduled.erb +19 -17
  129. metadata +103 -194
  130. data/.github/contributing.md +0 -32
  131. data/.github/issue_template.md +0 -9
  132. data/.gitignore +0 -12
  133. data/.travis.yml +0 -18
  134. data/3.0-Upgrade.md +0 -70
  135. data/4.0-Upgrade.md +0 -53
  136. data/COMM-LICENSE +0 -95
  137. data/Ent-Changes.md +0 -173
  138. data/Gemfile +0 -29
  139. data/LICENSE +0 -9
  140. data/Pro-2.0-Upgrade.md +0 -138
  141. data/Pro-3.0-Upgrade.md +0 -44
  142. data/Pro-Changes.md +0 -628
  143. data/Rakefile +0 -12
  144. data/bin/sidekiqctl +0 -99
  145. data/code_of_conduct.md +0 -50
  146. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +0 -6
  147. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  148. data/lib/sidekiq/core_ext.rb +0 -119
  149. data/lib/sidekiq/exception_handler.rb +0 -31
  150. data/lib/sidekiq/extensions/action_mailer.rb +0 -57
  151. data/lib/sidekiq/extensions/active_record.rb +0 -40
  152. data/lib/sidekiq/extensions/class_methods.rb +0 -40
  153. data/lib/sidekiq/extensions/generic_proxy.rb +0 -25
  154. data/lib/sidekiq/logging.rb +0 -106
  155. data/lib/sidekiq/middleware/server/active_record.rb +0 -13
  156. data/lib/sidekiq/middleware/server/logging.rb +0 -31
  157. data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -205
  158. data/lib/sidekiq/util.rb +0 -63
  159. data/lib/sidekiq/worker.rb +0 -121
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/current_attributes"
4
+
5
+ module Sidekiq
6
+ ##
7
+ # Automatically save and load any current attributes in the execution context
8
+ # so context attributes "flow" from Rails actions into any associated jobs.
9
+ # This can be useful for multi-tenancy, i18n locale, timezone, any implicit
10
+ # per-request attribute. See +ActiveSupport::CurrentAttributes+.
11
+ #
12
+ # For multiple current attributes, pass an array of current attributes.
13
+ #
14
+ # @example
15
+ #
16
+ # # in your initializer
17
+ # require "sidekiq/middleware/current_attributes"
18
+ # Sidekiq::CurrentAttributes.persist("Myapp::Current")
19
+ # # or multiple current attributes
20
+ # Sidekiq::CurrentAttributes.persist(["Myapp::Current", "Myapp::OtherCurrent"])
21
+ #
22
+ module CurrentAttributes
23
+ class Save
24
+ include Sidekiq::ClientMiddleware
25
+
26
+ def initialize(cattrs)
27
+ @cattrs = cattrs
28
+ end
29
+
30
+ def call(_, job, _, _)
31
+ @cattrs.each do |(key, strklass)|
32
+ if !job.has_key?(key)
33
+ attrs = strklass.constantize.attributes
34
+ # Retries can push the job N times, we don't
35
+ # want retries to reset cattr. #5692, #5090
36
+ if attrs.any?
37
+ # Older rails has a bug that `CurrentAttributes#attributes` always returns
38
+ # the same hash instance. We need to dup it to avoid being accidentally mutated.
39
+ job[key] = if returns_same_object?
40
+ attrs.dup
41
+ else
42
+ attrs
43
+ end
44
+ end
45
+ end
46
+ end
47
+ yield
48
+ end
49
+
50
+ private
51
+
52
+ def returns_same_object?
53
+ ActiveSupport::VERSION::MAJOR < 8 ||
54
+ (ActiveSupport::VERSION::MAJOR == 8 && ActiveSupport::VERSION::MINOR == 0)
55
+ end
56
+ end
57
+
58
+ class Load
59
+ include Sidekiq::ServerMiddleware
60
+
61
+ def initialize(cattrs)
62
+ @cattrs = cattrs
63
+ end
64
+
65
+ def call(_, job, _, &block)
66
+ klass_attrs = {}
67
+
68
+ @cattrs.each do |(key, strklass)|
69
+ next unless job.has_key?(key)
70
+
71
+ klass_attrs[strklass.constantize] = job[key]
72
+ end
73
+
74
+ wrap(klass_attrs.to_a, &block)
75
+ end
76
+
77
+ private
78
+
79
+ def wrap(klass_attrs, &block)
80
+ klass, attrs = klass_attrs.shift
81
+ return block.call unless klass
82
+
83
+ retried = false
84
+
85
+ begin
86
+ klass.set(attrs) do
87
+ wrap(klass_attrs, &block)
88
+ end
89
+ rescue NoMethodError
90
+ raise if retried
91
+
92
+ # It is possible that the `CurrentAttributes` definition
93
+ # was changed before the job started processing.
94
+ attrs = attrs.select { |attr| klass.respond_to?(attr) }
95
+ retried = true
96
+ retry
97
+ end
98
+ end
99
+ end
100
+
101
+ class << self
102
+ def persist(klass_or_array, config = Sidekiq.default_configuration)
103
+ cattrs = build_cattrs_hash(klass_or_array)
104
+
105
+ config.client_middleware.add Save, cattrs
106
+ config.server_middleware.prepend Load, cattrs
107
+ end
108
+
109
+ private
110
+
111
+ def build_cattrs_hash(klass_or_array)
112
+ if klass_or_array.is_a?(Array)
113
+ {}.tap do |hash|
114
+ klass_or_array.each_with_index do |klass, index|
115
+ hash[key_at(index)] = klass.to_s
116
+ end
117
+ end
118
+ else
119
+ {key_at(0) => klass_or_array.to_s}
120
+ end
121
+ end
122
+
123
+ def key_at(index)
124
+ (index == 0) ? "cattr" : "cattr_#{index}"
125
+ end
126
+ end
127
+ end
128
+ end
@@ -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,20 @@ 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(worker_class, msg, queue, redis_pool)
13
- msg['locale'] ||= I18n.locale
13
+ include Sidekiq::ClientMiddleware
14
+
15
+ def call(_jobclass, job, _queue, _redis)
16
+ job["locale"] ||= I18n.locale
14
17
  yield
15
18
  end
16
19
  end
17
20
 
18
21
  # Pull the msg locale out and set the current thread to use it.
19
22
  class Server
20
- def call(worker, msg, queue)
21
- I18n.locale = msg['locale'] || I18n.default_locale
22
- yield
23
- ensure
24
- I18n.locale = I18n.default_locale
23
+ include Sidekiq::ServerMiddleware
24
+
25
+ def call(_jobclass, job, _queue, &block)
26
+ I18n.with_locale(job.fetch("locale", I18n.default_locale), &block)
25
27
  end
26
28
  end
27
29
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidekiq
4
+ # Server-side middleware must import this Module in order
5
+ # to get access to server resources during `call`.
6
+ module ServerMiddleware
7
+ attr_accessor :config
8
+ def redis_pool
9
+ config.redis_pool
10
+ end
11
+
12
+ def logger
13
+ config.logger
14
+ end
15
+
16
+ def redis(&block)
17
+ config.redis(&block)
18
+ end
19
+ end
20
+
21
+ # no difference for now
22
+ ClientMiddleware = ServerMiddleware
23
+ end
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "fileutils"
5
+ require "sidekiq/api"
6
+
7
+ class Sidekiq::Monitor
8
+ class Status
9
+ VALID_SECTIONS = %w[all version overview processes queues]
10
+ COL_PAD = 2
11
+
12
+ def display(section = nil)
13
+ section ||= "all"
14
+ unless VALID_SECTIONS.include? section
15
+ puts "I don't know how to check the status of '#{section}'!"
16
+ puts "Try one of these: #{VALID_SECTIONS.join(", ")}"
17
+ return
18
+ end
19
+ send(section)
20
+ end
21
+
22
+ def all
23
+ version
24
+ puts
25
+ overview
26
+ puts
27
+ processes
28
+ puts
29
+ queues
30
+ end
31
+
32
+ def version
33
+ puts "Sidekiq #{Sidekiq::VERSION}"
34
+ puts Time.now.utc
35
+ end
36
+
37
+ def overview
38
+ puts "---- Overview ----"
39
+ puts " Processed: #{delimit stats.processed}"
40
+ puts " Failed: #{delimit stats.failed}"
41
+ puts " Busy: #{delimit stats.workers_size}"
42
+ puts " Enqueued: #{delimit stats.enqueued}"
43
+ puts " Retries: #{delimit stats.retry_size}"
44
+ puts " Scheduled: #{delimit stats.scheduled_size}"
45
+ puts " Dead: #{delimit stats.dead_size}"
46
+ end
47
+
48
+ def processes
49
+ puts "---- Processes (#{process_set.size}) ----"
50
+ process_set.each_with_index do |process, index|
51
+ # Keep compatibility with legacy versions since we don't want to break sidekiqmon during rolling upgrades or downgrades.
52
+ #
53
+ # Before:
54
+ # ["default", "critical"]
55
+ #
56
+ # After:
57
+ # {"default" => 1, "critical" => 10}
58
+ queues =
59
+ if process["weights"]
60
+ process["weights"].sort_by { |queue| queue[0] }.map { |capsule| capsule.map { |name, weight| (weight > 0) ? "#{name}: #{weight}" : name }.join(", ") }
61
+ else
62
+ process["queues"].sort
63
+ end
64
+
65
+ puts "#{process["identity"]} #{tags_for(process)}"
66
+ puts " Started: #{Time.at(process["started_at"])} (#{time_ago(process["started_at"])})"
67
+ puts " Threads: #{process["concurrency"]} (#{process["busy"]} busy)"
68
+ puts " Queues: #{split_multiline(queues, pad: 11)}"
69
+ puts " Version: #{process["version"] || "Unknown"}" if process["version"] != Sidekiq::VERSION
70
+ puts "" unless (index + 1) == process_set.size
71
+ end
72
+ end
73
+
74
+ def queues
75
+ puts "---- Queues (#{queue_data.size}) ----"
76
+ columns = {
77
+ name: [:ljust, (["name"] + queue_data.map(&:name)).map(&:length).max + COL_PAD],
78
+ size: [:rjust, (["size"] + queue_data.map(&:size)).map(&:length).max + COL_PAD],
79
+ latency: [:rjust, (["latency"] + queue_data.map(&:latency)).map(&:length).max + COL_PAD]
80
+ }
81
+ columns.each { |col, (dir, width)| print col.to_s.upcase.public_send(dir, width) }
82
+ puts
83
+ queue_data.each do |q|
84
+ columns.each do |col, (dir, width)|
85
+ print q.send(col).public_send(dir, width)
86
+ end
87
+ puts
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def delimit(number)
94
+ number.to_s.reverse.scan(/.{1,3}/).join(",").reverse
95
+ end
96
+
97
+ def split_multiline(values, opts = {})
98
+ return "none" unless values
99
+ pad = opts[:pad] || 0
100
+ max_length = opts[:max_length] || (80 - pad)
101
+ out = []
102
+ line = +""
103
+ values.each do |value|
104
+ if (line.length + value.length) > max_length
105
+ out << line
106
+ line = " " * pad
107
+ end
108
+ line << value + ", "
109
+ end
110
+ out << line[0..-3]
111
+ out.join("\n")
112
+ end
113
+
114
+ def tags_for(process)
115
+ tags = [
116
+ process["tag"],
117
+ process["labels"],
118
+ ((process["quiet"] == "true") ? "quiet" : nil)
119
+ ].flatten.compact
120
+ tags.any? ? "[#{tags.join("] [")}]" : nil
121
+ end
122
+
123
+ def time_ago(timestamp)
124
+ seconds = Time.now - Time.at(timestamp)
125
+ return "just now" if seconds < 60
126
+ return "a minute ago" if seconds < 120
127
+ return "#{seconds.floor / 60} minutes ago" if seconds < 3600
128
+ return "an hour ago" if seconds < 7200
129
+ "#{seconds.floor / 60 / 60} hours ago"
130
+ end
131
+
132
+ QUEUE_STRUCT = Struct.new(:name, :size, :latency)
133
+ def queue_data
134
+ @queue_data ||= Sidekiq::Queue.all.map { |q|
135
+ QUEUE_STRUCT.new(q.name, q.size.to_s, sprintf("%#.2f", q.latency))
136
+ }
137
+ end
138
+
139
+ def process_set
140
+ @process_set ||= Sidekiq::ProcessSet.new
141
+ end
142
+
143
+ def stats
144
+ @stats ||= Sidekiq::Stats.new
145
+ end
146
+ end
147
+ end
@@ -1,9 +1,15 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Sidekiq
3
4
  module Paginator
5
+ TYPE_CACHE = {
6
+ "dead" => "zset",
7
+ "retry" => "zset",
8
+ "schedule" => "zset"
9
+ }
4
10
 
5
- def page(key, pageidx=1, page_size=25, opts=nil)
6
- current_page = pageidx.to_i < 1 ? 1 : pageidx.to_i
11
+ def page(key, pageidx = 1, page_size = 25, opts = nil)
12
+ current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
7
13
  pageidx = current_page - 1
8
14
  total_size = 0
9
15
  items = []
@@ -12,26 +18,31 @@ module Sidekiq
12
18
 
13
19
  Sidekiq.redis do |conn|
14
20
  type = conn.type(key)
21
+ rev = opts && opts[:reverse]
15
22
 
16
23
  case type
17
- when 'zset'
18
- rev = opts && opts[:reverse]
19
- total_size, items = conn.multi do
20
- conn.zcard(key)
24
+ when "zset"
25
+ total_size, items = conn.multi { |transaction|
26
+ transaction.zcard(key)
21
27
  if rev
22
- conn.zrevrange(key, starting, ending, :with_scores => true)
28
+ transaction.zrange(key, starting, ending, "REV", "withscores")
23
29
  else
24
- conn.zrange(key, starting, ending, :with_scores => true)
30
+ transaction.zrange(key, starting, ending, "withscores")
25
31
  end
26
- end
32
+ }
27
33
  [current_page, total_size, items]
28
- when 'list'
29
- total_size, items = conn.multi do
30
- conn.llen(key)
31
- conn.lrange(key, starting, ending)
32
- end
34
+ when "list"
35
+ total_size, items = conn.multi { |transaction|
36
+ transaction.llen(key)
37
+ if rev
38
+ transaction.lrange(key, -ending - 1, -starting - 1)
39
+ else
40
+ transaction.lrange(key, starting, ending)
41
+ end
42
+ }
43
+ items.reverse! if rev
33
44
  [current_page, total_size, items]
34
- when 'none'
45
+ when "none"
35
46
  [1, 0, []]
36
47
  else
37
48
  raise "can't page a #{type}"
@@ -39,5 +50,12 @@ module Sidekiq
39
50
  end
40
51
  end
41
52
 
53
+ def page_items(items, pageidx = 1, page_size = 25)
54
+ current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
55
+ pageidx = current_page - 1
56
+ starting = pageidx * page_size
57
+ items = items.to_a
58
+ [current_page, items.size, items[starting, page_size]]
59
+ end
42
60
  end
43
61
  end