sidekiq 6.2.2 → 8.1.5

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 (181) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +726 -11
  3. data/LICENSE.txt +9 -0
  4. data/README.md +70 -39
  5. data/bin/kiq +17 -0
  6. data/bin/lint-herb +13 -0
  7. data/bin/multi_queue_bench +271 -0
  8. data/bin/sidekiq +4 -9
  9. data/bin/sidekiqload +214 -115
  10. data/bin/sidekiqmon +4 -1
  11. data/bin/webload +69 -0
  12. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +124 -0
  13. data/lib/generators/sidekiq/job_generator.rb +71 -0
  14. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +3 -3
  15. data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
  16. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  17. data/lib/sidekiq/api.rb +729 -264
  18. data/lib/sidekiq/capsule.rb +135 -0
  19. data/lib/sidekiq/cli.rb +124 -100
  20. data/lib/sidekiq/client.rb +153 -106
  21. data/lib/sidekiq/component.rb +132 -0
  22. data/lib/sidekiq/config.rb +320 -0
  23. data/lib/sidekiq/deploy.rb +64 -0
  24. data/lib/sidekiq/embedded.rb +64 -0
  25. data/lib/sidekiq/fetch.rb +27 -26
  26. data/lib/sidekiq/iterable_job.rb +56 -0
  27. data/lib/sidekiq/job/interrupt_handler.rb +24 -0
  28. data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
  29. data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
  30. data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
  31. data/lib/sidekiq/job/iterable.rb +322 -0
  32. data/lib/sidekiq/job.rb +397 -5
  33. data/lib/sidekiq/job_logger.rb +23 -32
  34. data/lib/sidekiq/job_retry.rb +141 -68
  35. data/lib/sidekiq/job_util.rb +113 -0
  36. data/lib/sidekiq/launcher.rb +122 -98
  37. data/lib/sidekiq/loader.rb +57 -0
  38. data/lib/sidekiq/logger.rb +27 -106
  39. data/lib/sidekiq/manager.rb +41 -43
  40. data/lib/sidekiq/metrics/query.rb +184 -0
  41. data/lib/sidekiq/metrics/shared.rb +109 -0
  42. data/lib/sidekiq/metrics/tracking.rb +153 -0
  43. data/lib/sidekiq/middleware/chain.rb +96 -51
  44. data/lib/sidekiq/middleware/current_attributes.rb +120 -0
  45. data/lib/sidekiq/middleware/i18n.rb +8 -4
  46. data/lib/sidekiq/middleware/modules.rb +23 -0
  47. data/lib/sidekiq/monitor.rb +16 -6
  48. data/lib/sidekiq/paginator.rb +37 -10
  49. data/lib/sidekiq/processor.rb +105 -87
  50. data/lib/sidekiq/profiler.rb +73 -0
  51. data/lib/sidekiq/rails.rb +49 -36
  52. data/lib/sidekiq/redis_client_adapter.rb +117 -0
  53. data/lib/sidekiq/redis_connection.rb +55 -86
  54. data/lib/sidekiq/ring_buffer.rb +32 -0
  55. data/lib/sidekiq/scheduled.rb +106 -50
  56. data/lib/sidekiq/systemd.rb +2 -0
  57. data/lib/sidekiq/test_api.rb +331 -0
  58. data/lib/sidekiq/testing/inline.rb +2 -30
  59. data/lib/sidekiq/testing.rb +2 -342
  60. data/lib/sidekiq/transaction_aware_client.rb +59 -0
  61. data/lib/sidekiq/tui/controls.rb +53 -0
  62. data/lib/sidekiq/tui/filtering.rb +53 -0
  63. data/lib/sidekiq/tui/tabs/base_tab.rb +204 -0
  64. data/lib/sidekiq/tui/tabs/busy.rb +118 -0
  65. data/lib/sidekiq/tui/tabs/dead.rb +19 -0
  66. data/lib/sidekiq/tui/tabs/home.rb +144 -0
  67. data/lib/sidekiq/tui/tabs/metrics.rb +131 -0
  68. data/lib/sidekiq/tui/tabs/queues.rb +95 -0
  69. data/lib/sidekiq/tui/tabs/retries.rb +19 -0
  70. data/lib/sidekiq/tui/tabs/scheduled.rb +19 -0
  71. data/lib/sidekiq/tui/tabs/set_tab.rb +96 -0
  72. data/lib/sidekiq/tui/tabs.rb +15 -0
  73. data/lib/sidekiq/tui.rb +382 -0
  74. data/lib/sidekiq/version.rb +6 -1
  75. data/lib/sidekiq/web/action.rb +149 -64
  76. data/lib/sidekiq/web/application.rb +376 -268
  77. data/lib/sidekiq/web/config.rb +117 -0
  78. data/lib/sidekiq/web/helpers.rb +213 -87
  79. data/lib/sidekiq/web/router.rb +61 -74
  80. data/lib/sidekiq/web.rb +71 -100
  81. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  82. data/lib/sidekiq.rb +95 -196
  83. data/sidekiq.gemspec +14 -11
  84. data/web/assets/images/logo.png +0 -0
  85. data/web/assets/images/status.png +0 -0
  86. data/web/assets/javascripts/application.js +171 -57
  87. data/web/assets/javascripts/base-charts.js +120 -0
  88. data/web/assets/javascripts/chart.min.js +13 -0
  89. data/web/assets/javascripts/chartjs-adapter-date-fns.min.js +7 -0
  90. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  91. data/web/assets/javascripts/dashboard-charts.js +194 -0
  92. data/web/assets/javascripts/dashboard.js +41 -274
  93. data/web/assets/javascripts/metrics.js +280 -0
  94. data/web/assets/stylesheets/style.css +776 -0
  95. data/web/locales/ar.yml +72 -70
  96. data/web/locales/cs.yml +64 -62
  97. data/web/locales/da.yml +62 -53
  98. data/web/locales/de.yml +67 -65
  99. data/web/locales/el.yml +45 -24
  100. data/web/locales/en.yml +93 -69
  101. data/web/locales/es.yml +91 -68
  102. data/web/locales/fa.yml +67 -65
  103. data/web/locales/fr.yml +82 -67
  104. data/web/locales/gd.yml +110 -0
  105. data/web/locales/he.yml +67 -64
  106. data/web/locales/hi.yml +61 -59
  107. data/web/locales/it.yml +94 -54
  108. data/web/locales/ja.yml +74 -68
  109. data/web/locales/ko.yml +54 -52
  110. data/web/locales/lt.yml +68 -66
  111. data/web/locales/nb.yml +63 -61
  112. data/web/locales/nl.yml +54 -52
  113. data/web/locales/pl.yml +47 -45
  114. data/web/locales/{pt-br.yml → pt-BR.yml} +85 -56
  115. data/web/locales/pt.yml +53 -51
  116. data/web/locales/ru.yml +69 -66
  117. data/web/locales/sv.yml +55 -53
  118. data/web/locales/ta.yml +62 -60
  119. data/web/locales/tr.yml +102 -0
  120. data/web/locales/uk.yml +87 -61
  121. data/web/locales/ur.yml +66 -64
  122. data/web/locales/vi.yml +69 -67
  123. data/web/locales/zh-CN.yml +107 -0
  124. data/web/locales/{zh-tw.yml → zh-TW.yml} +44 -9
  125. data/web/views/_footer.html.erb +32 -0
  126. data/web/views/_job_info.html.erb +115 -0
  127. data/web/views/_metrics_period_select.html.erb +15 -0
  128. data/web/views/_nav.html.erb +45 -0
  129. data/web/views/_paging.html.erb +26 -0
  130. data/web/views/_poll_link.html.erb +4 -0
  131. data/web/views/_summary.html.erb +40 -0
  132. data/web/views/busy.html.erb +151 -0
  133. data/web/views/dashboard.html.erb +104 -0
  134. data/web/views/dead.html.erb +38 -0
  135. data/web/views/filtering.html.erb +6 -0
  136. data/web/views/layout.html.erb +26 -0
  137. data/web/views/metrics.html.erb +85 -0
  138. data/web/views/metrics_for_job.html.erb +58 -0
  139. data/web/views/morgue.html.erb +69 -0
  140. data/web/views/profiles.html.erb +43 -0
  141. data/web/views/queue.html.erb +57 -0
  142. data/web/views/queues.html.erb +46 -0
  143. data/web/views/retries.html.erb +77 -0
  144. data/web/views/retry.html.erb +39 -0
  145. data/web/views/scheduled.html.erb +64 -0
  146. data/web/views/{scheduled_job_info.erb → scheduled_job_info.html.erb} +3 -3
  147. metadata +130 -61
  148. data/LICENSE +0 -9
  149. data/lib/generators/sidekiq/worker_generator.rb +0 -57
  150. data/lib/sidekiq/delay.rb +0 -41
  151. data/lib/sidekiq/exception_handler.rb +0 -27
  152. data/lib/sidekiq/extensions/action_mailer.rb +0 -48
  153. data/lib/sidekiq/extensions/active_record.rb +0 -43
  154. data/lib/sidekiq/extensions/class_methods.rb +0 -43
  155. data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
  156. data/lib/sidekiq/util.rb +0 -95
  157. data/lib/sidekiq/web/csrf_protection.rb +0 -180
  158. data/lib/sidekiq/worker.rb +0 -244
  159. data/web/assets/stylesheets/application-dark.css +0 -147
  160. data/web/assets/stylesheets/application-rtl.css +0 -246
  161. data/web/assets/stylesheets/application.css +0 -1053
  162. data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
  163. data/web/assets/stylesheets/bootstrap.css +0 -5
  164. data/web/locales/zh-cn.yml +0 -68
  165. data/web/views/_footer.erb +0 -20
  166. data/web/views/_job_info.erb +0 -89
  167. data/web/views/_nav.erb +0 -52
  168. data/web/views/_paging.erb +0 -23
  169. data/web/views/_poll_link.erb +0 -7
  170. data/web/views/_status.erb +0 -4
  171. data/web/views/_summary.erb +0 -40
  172. data/web/views/busy.erb +0 -132
  173. data/web/views/dashboard.erb +0 -83
  174. data/web/views/dead.erb +0 -34
  175. data/web/views/layout.erb +0 -42
  176. data/web/views/morgue.erb +0 -78
  177. data/web/views/queue.erb +0 -55
  178. data/web/views/queues.erb +0 -38
  179. data/web/views/retries.erb +0 -83
  180. data/web/views/retry.erb +0 -34
  181. data/web/views/scheduled.erb +0 -57
@@ -1,82 +1,93 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "sidekiq/middleware/modules"
4
+
3
5
  module Sidekiq
4
6
  # Middleware is code configured to run before/after
5
- # a message is processed. It is patterned after Rack
7
+ # a job is processed. It is patterned after Rack
6
8
  # middleware. Middleware exists for the client side
7
9
  # (pushing jobs onto the queue) as well as the server
8
10
  # side (when jobs are actually processed).
9
11
  #
12
+ # Callers will register middleware Classes and Sidekiq will
13
+ # create new instances of the middleware for every job. This
14
+ # is important so that instance state is not shared accidentally
15
+ # between job executions.
16
+ #
10
17
  # To add middleware for the client:
11
18
  #
12
- # Sidekiq.configure_client do |config|
13
- # config.client_middleware do |chain|
14
- # chain.add MyClientHook
19
+ # Sidekiq.configure_client do |config|
20
+ # config.client_middleware do |chain|
21
+ # chain.add MyClientHook
22
+ # end
15
23
  # end
16
- # end
17
24
  #
18
25
  # To modify middleware for the server, just call
19
26
  # with another block:
20
27
  #
21
- # Sidekiq.configure_server do |config|
22
- # config.server_middleware do |chain|
23
- # chain.add MyServerHook
24
- # chain.remove ActiveRecord
28
+ # Sidekiq.configure_server do |config|
29
+ # config.server_middleware do |chain|
30
+ # chain.add MyServerHook
31
+ # chain.remove ActiveRecord
32
+ # end
25
33
  # end
26
- # end
27
34
  #
28
35
  # To insert immediately preceding another entry:
29
36
  #
30
- # Sidekiq.configure_client do |config|
31
- # config.client_middleware do |chain|
32
- # chain.insert_before ActiveRecord, MyClientHook
37
+ # Sidekiq.configure_client do |config|
38
+ # config.client_middleware do |chain|
39
+ # chain.insert_before ActiveRecord, MyClientHook
40
+ # end
33
41
  # end
34
- # end
35
42
  #
36
43
  # To insert immediately after another entry:
37
44
  #
38
- # Sidekiq.configure_client do |config|
39
- # config.client_middleware do |chain|
40
- # chain.insert_after ActiveRecord, MyClientHook
45
+ # Sidekiq.configure_client do |config|
46
+ # config.client_middleware do |chain|
47
+ # chain.insert_after ActiveRecord, MyClientHook
48
+ # end
41
49
  # end
42
- # end
43
50
  #
44
51
  # This is an example of a minimal server middleware:
45
52
  #
46
- # class MyServerHook
47
- # def call(worker_instance, msg, queue)
48
- # puts "Before work"
49
- # yield
50
- # puts "After work"
53
+ # class MyServerHook
54
+ # include Sidekiq::ServerMiddleware
55
+ #
56
+ # def call(job_instance, msg, queue)
57
+ # logger.info "Before job"
58
+ # redis {|conn| conn.get("foo") } # do something in Redis
59
+ # yield
60
+ # logger.info "After job"
61
+ # end
51
62
  # end
52
- # end
53
63
  #
54
64
  # This is an example of a minimal client middleware, note
55
65
  # the method must return the result or the job will not push
56
66
  # to Redis:
57
67
  #
58
- # class MyClientHook
59
- # def call(worker_class, msg, queue, redis_pool)
60
- # puts "Before push"
61
- # result = yield
62
- # puts "After push"
63
- # result
68
+ # class MyClientHook
69
+ # include Sidekiq::ClientMiddleware
70
+ #
71
+ # def call(job_class, msg, queue, redis_pool)
72
+ # logger.info "Before push"
73
+ # result = yield
74
+ # logger.info "After push"
75
+ # result
76
+ # end
64
77
  # end
65
- # end
66
78
  #
67
79
  module Middleware
68
80
  class Chain
69
81
  include Enumerable
70
82
 
71
- def initialize_copy(copy)
72
- copy.instance_variable_set(:@entries, entries.dup)
73
- end
74
-
83
+ # Iterate through each middleware in the chain
75
84
  def each(&block)
76
85
  entries.each(&block)
77
86
  end
78
87
 
79
- def initialize
88
+ # @api private
89
+ def initialize(config = nil) # :nodoc:
90
+ @config = config
80
91
  @entries = nil
81
92
  yield self if block_given?
82
93
  end
@@ -85,38 +96,62 @@ module Sidekiq
85
96
  @entries ||= []
86
97
  end
87
98
 
99
+ def copy_for(capsule)
100
+ chain = Sidekiq::Middleware::Chain.new(capsule)
101
+ chain.instance_variable_set(:@entries, entries.dup)
102
+ chain
103
+ end
104
+
105
+ # Remove all middleware matching the given Class
106
+ # @param klass [Class]
88
107
  def remove(klass)
89
108
  entries.delete_if { |entry| entry.klass == klass }
90
109
  end
91
110
 
111
+ # Add the given middleware to the end of the chain.
112
+ # Sidekiq will call `klass.new(*args)` to create a clean
113
+ # copy of your middleware for every job executed.
114
+ #
115
+ # chain.add(Statsd::Metrics, { collector: "localhost:8125" })
116
+ #
117
+ # @param klass [Class] Your middleware class
118
+ # @param *args [Array<Object>] Set of arguments to pass to every instance of your middleware
92
119
  def add(klass, *args)
93
120
  remove(klass)
94
- entries << Entry.new(klass, *args)
121
+ entries << Entry.new(@config, klass, *args)
95
122
  end
96
123
 
124
+ # Identical to {#add} except the middleware is added to the front of the chain.
97
125
  def prepend(klass, *args)
98
126
  remove(klass)
99
- entries.insert(0, Entry.new(klass, *args))
127
+ entries.insert(0, Entry.new(@config, klass, *args))
100
128
  end
101
129
 
130
+ # Inserts +newklass+ before +oldklass+ in the chain.
131
+ # Useful if one middleware must run before another middleware.
102
132
  def insert_before(oldklass, newklass, *args)
103
133
  i = entries.index { |entry| entry.klass == newklass }
104
- new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
134
+ new_entry = i.nil? ? Entry.new(@config, newklass, *args) : entries.delete_at(i)
105
135
  i = entries.index { |entry| entry.klass == oldklass } || 0
106
136
  entries.insert(i, new_entry)
107
137
  end
108
138
 
139
+ # Inserts +newklass+ after +oldklass+ in the chain.
140
+ # Useful if one middleware must run after another middleware.
109
141
  def insert_after(oldklass, newklass, *args)
110
142
  i = entries.index { |entry| entry.klass == newklass }
111
- new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
143
+ new_entry = i.nil? ? Entry.new(@config, newklass, *args) : entries.delete_at(i)
112
144
  i = entries.index { |entry| entry.klass == oldklass } || entries.count - 1
113
145
  entries.insert(i + 1, new_entry)
114
146
  end
115
147
 
148
+ # @return [Boolean] if the given class is already in the chain
116
149
  def exists?(klass)
117
150
  any? { |entry| entry.klass == klass }
118
151
  end
152
+ alias_method :include?, :exists?
119
153
 
154
+ # @return [Boolean] if the chain contains no middleware
120
155
  def empty?
121
156
  @entries.nil? || @entries.empty?
122
157
  end
@@ -129,33 +164,43 @@ module Sidekiq
129
164
  entries.clear
130
165
  end
131
166
 
132
- def invoke(*args)
167
+ # Used by Sidekiq to execute the middleware at runtime
168
+ # @api private
169
+ def invoke(*args, &block)
133
170
  return yield if empty?
134
171
 
135
172
  chain = retrieve
136
- traverse_chain = proc do
137
- if chain.empty?
138
- yield
139
- else
140
- chain.shift.call(*args, &traverse_chain)
173
+ traverse(chain, 0, args, &block)
174
+ end
175
+
176
+ private
177
+
178
+ def traverse(chain, index, args, &block)
179
+ if index >= chain.size
180
+ yield
181
+ else
182
+ chain[index].call(*args) do
183
+ traverse(chain, index + 1, args, &block)
141
184
  end
142
185
  end
143
- traverse_chain.call
144
186
  end
145
187
  end
146
188
 
147
- private
148
-
189
+ # Represents each link in the middleware chain
190
+ # @api private
149
191
  class Entry
150
192
  attr_reader :klass
151
193
 
152
- def initialize(klass, *args)
194
+ def initialize(config, klass, *args)
195
+ @config = config
153
196
  @klass = klass
154
197
  @args = args
155
198
  end
156
199
 
157
200
  def make_new
158
- @klass.new(*@args)
201
+ x = @klass.new(*@args)
202
+ x.config = @config if @config && x.respond_to?(:config=)
203
+ x
159
204
  end
160
205
  end
161
206
  end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_job/arguments"
4
+ require "active_support/current_attributes"
5
+
6
+ module Sidekiq
7
+ ##
8
+ # Automatically save and load any current attributes in the execution context
9
+ # so context attributes "flow" from Rails actions into any associated jobs.
10
+ # This can be useful for multi-tenancy, i18n locale, timezone, any implicit
11
+ # per-request attribute. See +ActiveSupport::CurrentAttributes+.
12
+ #
13
+ # For multiple current attributes, pass an array of current attributes.
14
+ #
15
+ # @example
16
+ #
17
+ # # in your initializer
18
+ # require "sidekiq/middleware/current_attributes"
19
+ # Sidekiq::CurrentAttributes.persist("Myapp::Current")
20
+ # # or multiple current attributes
21
+ # Sidekiq::CurrentAttributes.persist(["Myapp::Current", "Myapp::OtherCurrent"])
22
+ #
23
+ module CurrentAttributes
24
+ Serializer = ::ActiveJob::Arguments
25
+
26
+ class Save
27
+ include Sidekiq::ClientMiddleware
28
+
29
+ def initialize(cattrs)
30
+ @cattrs = cattrs
31
+ end
32
+
33
+ def call(_, job, _, _)
34
+ @cattrs.each do |(key, strklass)|
35
+ if !job.has_key?(key)
36
+ attrs = strklass.constantize.attributes
37
+ # Retries can push the job N times, we don't
38
+ # want retries to reset cattr. #5692, #5090
39
+ job[key] = Serializer.serialize(attrs) if attrs.any?
40
+ end
41
+ end
42
+ yield
43
+ end
44
+ end
45
+
46
+ class Load
47
+ include Sidekiq::ServerMiddleware
48
+
49
+ def initialize(cattrs)
50
+ @cattrs = cattrs
51
+ end
52
+
53
+ def call(_, job, *, &block)
54
+ klass_attrs = {}
55
+
56
+ @cattrs.each do |(key, strklass)|
57
+ next unless job.has_key?(key)
58
+
59
+ klass_attrs[strklass.constantize] = Serializer.deserialize(job[key]).to_h
60
+ end
61
+
62
+ wrap(klass_attrs.to_a, &block)
63
+ end
64
+
65
+ private
66
+
67
+ def wrap(klass_attrs, &block)
68
+ klass, attrs = klass_attrs.shift
69
+ return block.call unless klass
70
+
71
+ retried = false
72
+
73
+ begin
74
+ set_succeeded = false
75
+ klass.set(attrs) do
76
+ set_succeeded = true
77
+ wrap(klass_attrs, &block)
78
+ end
79
+ rescue NoMethodError
80
+ # Don't retry if the no method error didn't come from current attributes
81
+ raise if retried || set_succeeded
82
+
83
+ # It is possible that the `CurrentAttributes` definition
84
+ # was changed before the job started processing.
85
+ attrs = attrs.select { |attr| klass.respond_to?(attr) }
86
+ retried = true
87
+ retry
88
+ end
89
+ end
90
+ end
91
+
92
+ class << self
93
+ def persist(klass_or_array, config = Sidekiq.default_configuration)
94
+ cattrs = build_cattrs_hash(klass_or_array)
95
+
96
+ config.client_middleware.prepend Load, cattrs
97
+ config.client_middleware.add Save, cattrs
98
+ config.server_middleware.prepend Load, cattrs
99
+ end
100
+
101
+ private
102
+
103
+ def build_cattrs_hash(klass_or_array)
104
+ if klass_or_array.is_a?(Array)
105
+ {}.tap do |hash|
106
+ klass_or_array.each_with_index do |klass, index|
107
+ hash[key_at(index)] = klass.to_s
108
+ end
109
+ end
110
+ else
111
+ {key_at(0) => klass_or_array.to_s}
112
+ end
113
+ end
114
+
115
+ def key_at(index)
116
+ (index == 0) ? "cattr" : "cattr_#{index}"
117
+ end
118
+ end
119
+ end
120
+ end
@@ -10,16 +10,20 @@ module Sidekiq::Middleware::I18n
10
10
  # Get the current locale and store it in the message
11
11
  # to be sent to Sidekiq.
12
12
  class Client
13
- def call(_worker, msg, _queue, _redis)
14
- msg["locale"] ||= I18n.locale
13
+ include Sidekiq::ClientMiddleware
14
+
15
+ def call(_jobclass, job, _queue, _redis)
16
+ job["locale"] ||= I18n.locale
15
17
  yield
16
18
  end
17
19
  end
18
20
 
19
21
  # Pull the msg locale out and set the current thread to use it.
20
22
  class Server
21
- def call(_worker, msg, _queue, &block)
22
- I18n.with_locale(msg.fetch("locale", I18n.default_locale), &block)
23
+ include Sidekiq::ServerMiddleware
24
+
25
+ def call(_jobclass, job, _queue, &block)
26
+ I18n.with_locale(job.fetch("locale", I18n.default_locale), &block)
23
27
  end
24
28
  end
25
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
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "fileutils"
4
5
  require "sidekiq/api"
@@ -16,8 +17,6 @@ class Sidekiq::Monitor
16
17
  return
17
18
  end
18
19
  send(section)
19
- rescue => e
20
- puts "Couldn't get status: #{e}"
21
20
  end
22
21
 
23
22
  def all
@@ -49,10 +48,21 @@ class Sidekiq::Monitor
49
48
  def processes
50
49
  puts "---- Processes (#{process_set.size}) ----"
51
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
+ queues =
53
+ if process["capsules"] # 8.0.6+
54
+ process["capsules"].values.map { |x| x["weights"].keys.join(", ") }
55
+ elsif process["weights"]
56
+ process["weights"].sort_by { |queue| queue[0] }.map { |capsule| capsule.map { |name, weight| (weight > 0) ? "#{name}: #{weight}" : name }.join(", ") }
57
+ else
58
+ process["queues"].sort
59
+ end
60
+
52
61
  puts "#{process["identity"]} #{tags_for(process)}"
53
62
  puts " Started: #{Time.at(process["started_at"])} (#{time_ago(process["started_at"])})"
54
63
  puts " Threads: #{process["concurrency"]} (#{process["busy"]} busy)"
55
- puts " Queues: #{split_multiline(process["queues"].sort, pad: 11)}"
64
+ puts " Queues: #{split_multiline(queues, pad: 11)}"
65
+ puts " Version: #{process["version"] || "Unknown"}" if process["version"] != Sidekiq::VERSION
56
66
  puts "" unless (index + 1) == process_set.size
57
67
  end
58
68
  end
@@ -85,13 +95,13 @@ class Sidekiq::Monitor
85
95
  pad = opts[:pad] || 0
86
96
  max_length = opts[:max_length] || (80 - pad)
87
97
  out = []
88
- line = ""
98
+ line = +""
89
99
  values.each do |value|
90
100
  if (line.length + value.length) > max_length
91
101
  out << line
92
102
  line = " " * pad
93
103
  end
94
- line << value + ", "
104
+ line << value + "; "
95
105
  end
96
106
  out << line[0..-3]
97
107
  out.join("\n")
@@ -101,7 +111,7 @@ class Sidekiq::Monitor
101
111
  tags = [
102
112
  process["tag"],
103
113
  process["labels"],
104
- (process["quiet"] == "true" ? "quiet" : nil)
114
+ ((process["quiet"] == "true") ? "quiet" : nil)
105
115
  ].flatten.compact
106
116
  tags.any? ? "[#{tags.join("] [")}]" : nil
107
117
  end
@@ -2,8 +2,14 @@
2
2
 
3
3
  module Sidekiq
4
4
  module Paginator
5
+ TYPE_CACHE = {
6
+ "dead" => "zset",
7
+ "retry" => "zset",
8
+ "schedule" => "zset"
9
+ }
10
+
5
11
  def page(key, pageidx = 1, page_size = 25, opts = nil)
6
- current_page = pageidx.to_i < 1 ? 1 : pageidx.to_i
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 = []
@@ -11,27 +17,35 @@ module Sidekiq
11
17
  ending = starting + page_size - 1
12
18
 
13
19
  Sidekiq.redis do |conn|
14
- type = conn.type(key)
20
+ # horrible, think you can make this cleaner?
21
+ type = TYPE_CACHE[key]
22
+ if type
23
+ elsif key.start_with?("queue:")
24
+ type = TYPE_CACHE[key] = "list"
25
+ else
26
+ type = conn.type(key)
27
+ TYPE_CACHE[key] = type unless type == "none"
28
+ end
15
29
  rev = opts && opts[:reverse]
16
30
 
17
31
  case type
18
32
  when "zset"
19
- total_size, items = conn.multi {
20
- conn.zcard(key)
33
+ total_size, items = conn.multi { |transaction|
34
+ transaction.zcard(key)
21
35
  if rev
22
- conn.zrevrange(key, starting, ending, with_scores: true)
36
+ transaction.zrange(key, starting, ending, "REV", "withscores")
23
37
  else
24
- conn.zrange(key, starting, ending, with_scores: true)
38
+ transaction.zrange(key, starting, ending, "withscores")
25
39
  end
26
40
  }
27
41
  [current_page, total_size, items]
28
42
  when "list"
29
- total_size, items = conn.multi {
30
- conn.llen(key)
43
+ total_size, items = conn.multi { |transaction|
44
+ transaction.llen(key)
31
45
  if rev
32
- conn.lrange(key, -ending - 1, -starting - 1)
46
+ transaction.lrange(key, -ending - 1, -starting - 1)
33
47
  else
34
- conn.lrange(key, starting, ending)
48
+ transaction.lrange(key, starting, ending)
35
49
  end
36
50
  }
37
51
  items.reverse! if rev
@@ -43,5 +57,18 @@ module Sidekiq
43
57
  end
44
58
  end
45
59
  end
60
+
61
+ def page_items(items, pageidx = 1, page_size = 25)
62
+ current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
63
+ pageidx = current_page - 1
64
+ starting = pageidx * page_size
65
+ items = items.to_a
66
+ total_size = items.size
67
+ if starting > total_size
68
+ starting = 0
69
+ current_page = 1
70
+ end
71
+ [current_page, total_size, items[starting, page_size]]
72
+ end
46
73
  end
47
74
  end