sidekiq 4.2.10 → 7.3.2

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 (158) hide show
  1. checksums.yaml +5 -5
  2. data/Changes.md +859 -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 +212 -119
  8. data/bin/sidekiqmon +11 -0
  9. data/lib/generators/sidekiq/job_generator.rb +59 -0
  10. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
  11. data/lib/generators/sidekiq/templates/job_spec.rb.erb +6 -0
  12. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  13. data/lib/sidekiq/api.rb +680 -315
  14. data/lib/sidekiq/capsule.rb +132 -0
  15. data/lib/sidekiq/cli.rb +268 -248
  16. data/lib/sidekiq/client.rb +136 -101
  17. data/lib/sidekiq/component.rb +68 -0
  18. data/lib/sidekiq/config.rb +293 -0
  19. data/lib/sidekiq/deploy.rb +64 -0
  20. data/lib/sidekiq/embedded.rb +63 -0
  21. data/lib/sidekiq/fetch.rb +49 -42
  22. data/lib/sidekiq/iterable_job.rb +55 -0
  23. data/lib/sidekiq/job/interrupt_handler.rb +24 -0
  24. data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
  25. data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
  26. data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
  27. data/lib/sidekiq/job/iterable.rb +231 -0
  28. data/lib/sidekiq/job.rb +385 -0
  29. data/lib/sidekiq/job_logger.rb +62 -0
  30. data/lib/sidekiq/job_retry.rb +305 -0
  31. data/lib/sidekiq/job_util.rb +109 -0
  32. data/lib/sidekiq/launcher.rb +208 -108
  33. data/lib/sidekiq/logger.rb +131 -0
  34. data/lib/sidekiq/manager.rb +43 -47
  35. data/lib/sidekiq/metrics/query.rb +158 -0
  36. data/lib/sidekiq/metrics/shared.rb +97 -0
  37. data/lib/sidekiq/metrics/tracking.rb +148 -0
  38. data/lib/sidekiq/middleware/chain.rb +113 -56
  39. data/lib/sidekiq/middleware/current_attributes.rb +113 -0
  40. data/lib/sidekiq/middleware/i18n.rb +7 -7
  41. data/lib/sidekiq/middleware/modules.rb +23 -0
  42. data/lib/sidekiq/monitor.rb +147 -0
  43. data/lib/sidekiq/paginator.rb +28 -16
  44. data/lib/sidekiq/processor.rb +188 -98
  45. data/lib/sidekiq/rails.rb +46 -97
  46. data/lib/sidekiq/redis_client_adapter.rb +114 -0
  47. data/lib/sidekiq/redis_connection.rb +71 -73
  48. data/lib/sidekiq/ring_buffer.rb +31 -0
  49. data/lib/sidekiq/scheduled.rb +140 -51
  50. data/lib/sidekiq/sd_notify.rb +149 -0
  51. data/lib/sidekiq/systemd.rb +26 -0
  52. data/lib/sidekiq/testing/inline.rb +6 -5
  53. data/lib/sidekiq/testing.rb +95 -85
  54. data/lib/sidekiq/transaction_aware_client.rb +51 -0
  55. data/lib/sidekiq/version.rb +3 -1
  56. data/lib/sidekiq/web/action.rb +22 -16
  57. data/lib/sidekiq/web/application.rb +230 -86
  58. data/lib/sidekiq/web/csrf_protection.rb +183 -0
  59. data/lib/sidekiq/web/helpers.rb +241 -104
  60. data/lib/sidekiq/web/router.rb +23 -19
  61. data/lib/sidekiq/web.rb +118 -110
  62. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  63. data/lib/sidekiq.rb +96 -185
  64. data/sidekiq.gemspec +26 -27
  65. data/web/assets/images/apple-touch-icon.png +0 -0
  66. data/web/assets/javascripts/application.js +157 -61
  67. data/web/assets/javascripts/base-charts.js +106 -0
  68. data/web/assets/javascripts/chart.min.js +13 -0
  69. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  70. data/web/assets/javascripts/dashboard-charts.js +192 -0
  71. data/web/assets/javascripts/dashboard.js +37 -280
  72. data/web/assets/javascripts/metrics.js +298 -0
  73. data/web/assets/stylesheets/application-dark.css +147 -0
  74. data/web/assets/stylesheets/application-rtl.css +163 -0
  75. data/web/assets/stylesheets/application.css +173 -198
  76. data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  77. data/web/assets/stylesheets/bootstrap.css +2 -2
  78. data/web/locales/ar.yml +87 -0
  79. data/web/locales/cs.yml +62 -62
  80. data/web/locales/da.yml +60 -53
  81. data/web/locales/de.yml +65 -53
  82. data/web/locales/el.yml +43 -24
  83. data/web/locales/en.yml +86 -64
  84. data/web/locales/es.yml +70 -53
  85. data/web/locales/fa.yml +65 -64
  86. data/web/locales/fr.yml +83 -62
  87. data/web/locales/gd.yml +99 -0
  88. data/web/locales/he.yml +80 -0
  89. data/web/locales/hi.yml +59 -59
  90. data/web/locales/it.yml +53 -53
  91. data/web/locales/ja.yml +75 -62
  92. data/web/locales/ko.yml +52 -52
  93. data/web/locales/lt.yml +83 -0
  94. data/web/locales/nb.yml +61 -61
  95. data/web/locales/nl.yml +52 -52
  96. data/web/locales/pl.yml +45 -45
  97. data/web/locales/pt-br.yml +83 -55
  98. data/web/locales/pt.yml +51 -51
  99. data/web/locales/ru.yml +68 -63
  100. data/web/locales/sv.yml +53 -53
  101. data/web/locales/ta.yml +60 -60
  102. data/web/locales/tr.yml +101 -0
  103. data/web/locales/uk.yml +62 -61
  104. data/web/locales/ur.yml +80 -0
  105. data/web/locales/vi.yml +83 -0
  106. data/web/locales/zh-cn.yml +43 -16
  107. data/web/locales/zh-tw.yml +42 -8
  108. data/web/views/_footer.erb +21 -3
  109. data/web/views/_job_info.erb +21 -4
  110. data/web/views/_metrics_period_select.erb +12 -0
  111. data/web/views/_nav.erb +5 -19
  112. data/web/views/_paging.erb +3 -1
  113. data/web/views/_poll_link.erb +3 -6
  114. data/web/views/_summary.erb +7 -7
  115. data/web/views/busy.erb +85 -31
  116. data/web/views/dashboard.erb +50 -20
  117. data/web/views/dead.erb +3 -3
  118. data/web/views/filtering.erb +7 -0
  119. data/web/views/layout.erb +17 -6
  120. data/web/views/metrics.erb +91 -0
  121. data/web/views/metrics_for_job.erb +59 -0
  122. data/web/views/morgue.erb +14 -15
  123. data/web/views/queue.erb +34 -24
  124. data/web/views/queues.erb +20 -4
  125. data/web/views/retries.erb +19 -16
  126. data/web/views/retry.erb +3 -3
  127. data/web/views/scheduled.erb +19 -17
  128. metadata +91 -198
  129. data/.github/contributing.md +0 -32
  130. data/.github/issue_template.md +0 -9
  131. data/.gitignore +0 -12
  132. data/.travis.yml +0 -18
  133. data/3.0-Upgrade.md +0 -70
  134. data/4.0-Upgrade.md +0 -53
  135. data/COMM-LICENSE +0 -95
  136. data/Ent-Changes.md +0 -173
  137. data/Gemfile +0 -29
  138. data/LICENSE +0 -9
  139. data/Pro-2.0-Upgrade.md +0 -138
  140. data/Pro-3.0-Upgrade.md +0 -44
  141. data/Pro-Changes.md +0 -628
  142. data/Rakefile +0 -12
  143. data/bin/sidekiqctl +0 -99
  144. data/code_of_conduct.md +0 -50
  145. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +0 -6
  146. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  147. data/lib/sidekiq/core_ext.rb +0 -119
  148. data/lib/sidekiq/exception_handler.rb +0 -31
  149. data/lib/sidekiq/extensions/action_mailer.rb +0 -57
  150. data/lib/sidekiq/extensions/active_record.rb +0 -40
  151. data/lib/sidekiq/extensions/class_methods.rb +0 -40
  152. data/lib/sidekiq/extensions/generic_proxy.rb +0 -25
  153. data/lib/sidekiq/logging.rb +0 -106
  154. data/lib/sidekiq/middleware/server/active_record.rb +0 -13
  155. data/lib/sidekiq/middleware/server/logging.rb +0 -31
  156. data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -205
  157. data/lib/sidekiq/util.rb +0 -63
  158. data/lib/sidekiq/worker.rb +0 -121
@@ -0,0 +1,113 @@
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
+ job[key] = attrs if attrs.any?
37
+ end
38
+ end
39
+ yield
40
+ end
41
+ end
42
+
43
+ class Load
44
+ include Sidekiq::ServerMiddleware
45
+
46
+ def initialize(cattrs)
47
+ @cattrs = cattrs
48
+ end
49
+
50
+ def call(_, job, _, &block)
51
+ klass_attrs = {}
52
+
53
+ @cattrs.each do |(key, strklass)|
54
+ next unless job.has_key?(key)
55
+
56
+ klass_attrs[strklass.constantize] = job[key]
57
+ end
58
+
59
+ wrap(klass_attrs.to_a, &block)
60
+ end
61
+
62
+ private
63
+
64
+ def wrap(klass_attrs, &block)
65
+ klass, attrs = klass_attrs.shift
66
+ return block.call unless klass
67
+
68
+ retried = false
69
+
70
+ begin
71
+ klass.set(attrs) do
72
+ wrap(klass_attrs, &block)
73
+ end
74
+ rescue NoMethodError
75
+ raise if retried
76
+
77
+ # It is possible that the `CurrentAttributes` definition
78
+ # was changed before the job started processing.
79
+ attrs = attrs.select { |attr| klass.respond_to?(attr) }
80
+ retried = true
81
+ retry
82
+ end
83
+ end
84
+ end
85
+
86
+ class << self
87
+ def persist(klass_or_array, config = Sidekiq.default_configuration)
88
+ cattrs = build_cattrs_hash(klass_or_array)
89
+
90
+ config.client_middleware.add Save, cattrs
91
+ config.server_middleware.add Load, cattrs
92
+ end
93
+
94
+ private
95
+
96
+ def build_cattrs_hash(klass_or_array)
97
+ if klass_or_array.is_a?(Array)
98
+ {}.tap do |hash|
99
+ klass_or_array.each_with_index do |klass, index|
100
+ hash[key_at(index)] = klass.to_s
101
+ end
102
+ end
103
+ else
104
+ {key_at(0) => klass_or_array.to_s}
105
+ end
106
+ end
107
+
108
+ def key_at(index)
109
+ (index == 0) ? "cattr" : "cattr_#{index}"
110
+ end
111
+ end
112
+ end
113
+ 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,18 @@ 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
+ def call(_jobclass, job, _queue, _redis)
15
+ job["locale"] ||= I18n.locale
14
16
  yield
15
17
  end
16
18
  end
17
19
 
18
20
  # Pull the msg locale out and set the current thread to use it.
19
21
  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
22
+ include Sidekiq::ServerMiddleware
23
+ def call(_jobclass, job, _queue, &block)
24
+ I18n.with_locale(job.fetch("locale", I18n.default_locale), &block)
25
25
  end
26
26
  end
27
27
  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,9 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Sidekiq
3
4
  module Paginator
4
-
5
- def page(key, pageidx=1, page_size=25, opts=nil)
6
- current_page = pageidx.to_i < 1 ? 1 : pageidx.to_i
5
+ def page(key, pageidx = 1, page_size = 25, opts = nil)
6
+ current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
7
7
  pageidx = current_page - 1
8
8
  total_size = 0
9
9
  items = []
@@ -12,26 +12,31 @@ module Sidekiq
12
12
 
13
13
  Sidekiq.redis do |conn|
14
14
  type = conn.type(key)
15
+ rev = opts && opts[:reverse]
15
16
 
16
17
  case type
17
- when 'zset'
18
- rev = opts && opts[:reverse]
19
- total_size, items = conn.multi do
20
- conn.zcard(key)
18
+ when "zset"
19
+ total_size, items = conn.multi { |transaction|
20
+ transaction.zcard(key)
21
21
  if rev
22
- conn.zrevrange(key, starting, ending, :with_scores => true)
22
+ transaction.zrange(key, starting, ending, "REV", "withscores")
23
23
  else
24
- conn.zrange(key, starting, ending, :with_scores => true)
24
+ transaction.zrange(key, starting, ending, "withscores")
25
25
  end
26
- end
26
+ }
27
27
  [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
28
+ when "list"
29
+ total_size, items = conn.multi { |transaction|
30
+ transaction.llen(key)
31
+ if rev
32
+ transaction.lrange(key, -ending - 1, -starting - 1)
33
+ else
34
+ transaction.lrange(key, starting, ending)
35
+ end
36
+ }
37
+ items.reverse! if rev
33
38
  [current_page, total_size, items]
34
- when 'none'
39
+ when "none"
35
40
  [1, 0, []]
36
41
  else
37
42
  raise "can't page a #{type}"
@@ -39,5 +44,12 @@ module Sidekiq
39
44
  end
40
45
  end
41
46
 
47
+ def page_items(items, pageidx = 1, page_size = 25)
48
+ current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
49
+ pageidx = current_page - 1
50
+ starting = pageidx * page_size
51
+ items = items.to_a
52
+ [current_page, items.size, items[starting, page_size]]
53
+ end
42
54
  end
43
55
  end