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
data/lib/sidekiq/web.rb CHANGED
@@ -1,27 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "erb"
4
-
5
- require "sidekiq"
6
- require "sidekiq/api"
7
- require "sidekiq/paginator"
8
- require "sidekiq/web/helpers"
9
-
10
- require "sidekiq/web/router"
11
- require "sidekiq/web/action"
12
- require "sidekiq/web/application"
13
- require "sidekiq/web/csrf_protection"
14
-
15
- require "rack/content_length"
4
+ require "securerandom"
16
5
  require "rack/builder"
17
6
  require "rack/static"
7
+ require "sidekiq"
8
+ require "sidekiq/api"
9
+ require "sidekiq/web/config"
18
10
 
19
11
  module Sidekiq
20
12
  class Web
21
13
  ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../web")
22
14
  VIEWS = "#{ROOT}/views"
23
15
  LOCALES = ["#{ROOT}/locales"]
24
- LAYOUT = "#{VIEWS}/layout.erb"
16
+ LAYOUT = "#{VIEWS}/layout.html.erb"
25
17
  ASSETS = "#{ROOT}/assets"
26
18
 
27
19
  DEFAULT_TABS = {
@@ -30,144 +22,119 @@ module Sidekiq
30
22
  "Queues" => "queues",
31
23
  "Retries" => "retries",
32
24
  "Scheduled" => "scheduled",
33
- "Dead" => "morgue"
25
+ "Dead" => "morgue",
26
+ "Metrics" => "metrics",
27
+ "Profiles" => "profiles"
34
28
  }
35
29
 
36
- if ENV["SIDEKIQ_METRICS_BETA"] == "1"
37
- DEFAULT_TABS["Metrics"] = "metrics"
38
- end
30
+ @@config = Sidekiq::Web::Config.new
39
31
 
40
32
  class << self
41
- def settings
42
- self
33
+ def configure
34
+ if block_given?
35
+ yield @@config
36
+ else
37
+ @@config
38
+ end
43
39
  end
44
40
 
45
- def default_tabs
46
- DEFAULT_TABS
41
+ def app_url=(url)
42
+ @@config.app_url = url
47
43
  end
48
44
 
49
- def custom_tabs
50
- @custom_tabs ||= {}
45
+ def assets_path=(path)
46
+ @@config.assets_path = path
51
47
  end
52
- alias_method :tabs, :custom_tabs
53
48
 
54
- def locales
55
- @locales ||= LOCALES
56
- end
49
+ def assets_path = @@config.assets_path
57
50
 
58
- def views
59
- @views ||= VIEWS
60
- end
51
+ def tabs = @@config.tabs
61
52
 
62
- def enable(*opts)
63
- opts.each { |key| set(key, true) }
64
- end
53
+ def locales = @@config.locales
65
54
 
66
- def disable(*opts)
67
- opts.each { |key| set(key, false) }
68
- end
69
-
70
- def middlewares
71
- @middlewares ||= []
72
- end
73
-
74
- def use(*args, &block)
75
- middlewares << [args, block]
76
- end
55
+ def views = @@config.views
77
56
 
78
- def set(attribute, value)
79
- send(:"#{attribute}=", value)
80
- end
57
+ def custom_job_info_rows = @@config.custom_job_info_rows
81
58
 
82
- def sessions=(val)
83
- puts "WARNING: Sidekiq::Web.sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
59
+ def redis_pool
60
+ @pool || Sidekiq.default_configuration.redis_pool
84
61
  end
85
62
 
86
- def session_secret=(val)
87
- puts "WARNING: Sidekiq::Web.session_secret= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
63
+ def redis_pool=(pool)
64
+ @pool = pool
88
65
  end
89
66
 
90
- attr_accessor :app_url, :redis_pool
91
- attr_writer :locales, :views
92
- end
67
+ def middlewares = @@config.middlewares
93
68
 
94
- def self.inherited(child)
95
- child.app_url = app_url
96
- child.redis_pool = redis_pool
97
- end
69
+ def use(*args, &block) = @@config.middlewares << [args, block]
98
70
 
99
- def settings
100
- self.class.settings
71
+ def register(*args, **kw, &block)
72
+ Sidekiq.logger.warn { "`Sidekiq::Web.register` is deprecated, use `Sidekiq::Web.configure {|cfg| cfg.register(...) }`" }
73
+ @@config.register(*args, **kw, &block)
74
+ end
101
75
  end
102
76
 
103
- def middlewares
104
- @middlewares ||= self.class.middlewares
77
+ # Allow user to say
78
+ # run Sidekiq::Web
79
+ # rather than:
80
+ # run Sidekiq::Web.new
81
+ def self.call(env)
82
+ @inst ||= new
83
+ @inst.call(env)
105
84
  end
106
85
 
107
- def use(*args, &block)
108
- middlewares << [args, block]
86
+ # testing, internal use only
87
+ def self.reset!
88
+ @@config.reset!
89
+ @inst = nil
109
90
  end
110
91
 
111
92
  def call(env)
112
- app.call(env)
113
- end
114
-
115
- def self.call(env)
116
- @app ||= new
117
- @app.call(env)
93
+ env[:web_config] = Sidekiq::Web.configure
94
+ env[:csp_nonce] = SecureRandom.hex(8)
95
+ env[:redis_pool] = self.class.redis_pool
96
+ safe_request?(env) ? app.call(env) : deny(env)
118
97
  end
119
98
 
120
99
  def app
121
- @app ||= build
122
- end
123
-
124
- def enable(*opts)
125
- opts.each { |key| set(key, true) }
100
+ @app ||= build(@@config)
126
101
  end
127
102
 
128
- def disable(*opts)
129
- opts.each { |key| set(key, false) }
103
+ def safe_methods?(env)
104
+ %w[GET HEAD OPTIONS TRACE].include? env["REQUEST_METHOD"]
130
105
  end
131
106
 
132
- def set(attribute, value)
133
- send(:"#{attribute}=", value)
107
+ def safe_request?(env)
108
+ return true if safe_methods?(env)
109
+ env["HTTP_SEC_FETCH_SITE"] == "same-origin"
134
110
  end
135
111
 
136
- def sessions=(val)
137
- puts "Sidekiq::Web#sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller[2..2].first}"
138
- end
139
-
140
- def self.register(extension)
141
- extension.registered(WebApplication)
112
+ def deny(env)
113
+ Sidekiq.logger.warn "attack prevented by #{self.class}"
114
+ [403, {Rack::CONTENT_TYPE => "text/plain"}, ["Forbidden"]]
142
115
  end
143
116
 
144
117
  private
145
118
 
146
- def build
147
- klass = self.class
148
- m = middlewares
119
+ def build(cfg)
120
+ cfg.freeze
121
+ m = cfg.middlewares
149
122
 
150
123
  rules = []
151
- rules = [[:all, {"cache-control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
124
+ rules = [[:all, {"cache-control" => "private, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
152
125
 
153
126
  ::Rack::Builder.new do
154
127
  use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
155
- root: ASSETS,
128
+ root: cfg.assets_path,
156
129
  cascade: true,
157
130
  header_rules: rules
158
131
  m.each { |middleware, block| use(*middleware, &block) }
159
- use Sidekiq::Web::CsrfProtection unless $TESTING
160
- run WebApplication.new(klass)
132
+ run Sidekiq::Web::Application.new(self.class)
161
133
  end
162
134
  end
163
135
  end
164
-
165
- Sidekiq::WebApplication.helpers WebHelpers
166
- Sidekiq::WebApplication.helpers Sidekiq::Paginator
167
-
168
- Sidekiq::WebAction.class_eval <<-RUBY, __FILE__, __LINE__ + 1
169
- def _render
170
- #{ERB.new(File.read(Web::LAYOUT)).src}
171
- end
172
- RUBY
173
136
  end
137
+
138
+ require "sidekiq/web/router"
139
+ require "sidekiq/web/action"
140
+ require "sidekiq/web/application"
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidekiq
4
+ # Sidekiq::Job is a new alias for Sidekiq::Worker as of Sidekiq 6.3.0.
5
+ # Use `include Sidekiq::Job` rather than `include Sidekiq::Worker`.
6
+ #
7
+ # The term "worker" is too generic and overly confusing, used in several
8
+ # different contexts meaning different things. Many people call a Sidekiq
9
+ # process a "worker". Some people call the thread that executes jobs a
10
+ # "worker". This change brings Sidekiq closer to ActiveJob where your job
11
+ # classes extend ApplicationJob.
12
+ Worker = Job
13
+ end
data/lib/sidekiq.rb CHANGED
@@ -1,15 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sidekiq/version"
4
- fail "Sidekiq #{Sidekiq::VERSION} does not support Ruby versions below 2.5.0." if RUBY_PLATFORM != "java" && Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.5.0")
4
+ fail "Sidekiq #{Sidekiq::VERSION} does not support Ruby versions below 3.2.0." if RUBY_PLATFORM != "java" && Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2.0")
5
5
 
6
+ begin
7
+ require "sidekiq-ent/version"
8
+ fail <<~EOM if Gem::Version.new(Sidekiq::Enterprise::VERSION).segments[0] != Sidekiq::MAJOR
9
+
10
+ Sidekiq Enterprise #{Sidekiq::Enterprise::VERSION} does not work with Sidekiq #{Sidekiq::VERSION}.
11
+ Starting with Sidekiq 7, major versions are synchronized so Sidekiq Enterprise 7 works with Sidekiq 7.
12
+ Use `bundle up sidekiq-ent` to upgrade.
13
+
14
+ EOM
15
+ rescue LoadError
16
+ end
17
+
18
+ begin
19
+ require "sidekiq/pro/version"
20
+ fail <<~EOM if Gem::Version.new(Sidekiq::Pro::VERSION).segments[0] != Sidekiq::MAJOR
21
+
22
+ Sidekiq Pro #{Sidekiq::Pro::VERSION} does not work with Sidekiq #{Sidekiq::VERSION}.
23
+ Starting with Sidekiq 7, major versions are synchronized so Sidekiq Pro 7 works with Sidekiq 7.
24
+ Use `bundle up sidekiq-pro` to upgrade.
25
+
26
+ EOM
27
+ rescue LoadError
28
+ end
29
+
30
+ require "sidekiq/config"
6
31
  require "sidekiq/logger"
32
+ require "sidekiq/loader"
7
33
  require "sidekiq/client"
8
34
  require "sidekiq/transaction_aware_client"
9
- require "sidekiq/worker"
10
35
  require "sidekiq/job"
11
- require "sidekiq/redis_connection"
12
- require "sidekiq/delay"
36
+ require "sidekiq/iterable_job"
37
+ require "sidekiq/worker_compatibility_alias"
38
+ require "sidekiq/redis_client_adapter"
13
39
 
14
40
  require "json"
15
41
 
@@ -17,320 +43,110 @@ module Sidekiq
17
43
  NAME = "Sidekiq"
18
44
  LICENSE = "See LICENSE and the LGPL-3.0 for licensing details."
19
45
 
20
- DEFAULTS = {
21
- queues: [],
22
- labels: [],
23
- concurrency: 10,
24
- require: ".",
25
- strict: true,
26
- environment: nil,
27
- timeout: 25,
28
- poll_interval_average: nil,
29
- average_scheduled_poll_interval: 5,
30
- on_complex_arguments: :warn,
31
- error_handlers: [],
32
- death_handlers: [],
33
- lifecycle_events: {
34
- startup: [],
35
- quiet: [],
36
- shutdown: [],
37
- # triggers when we fire the first heartbeat on startup OR repairing a network partition
38
- heartbeat: [],
39
- # triggers on EVERY heartbeat call, every 10 seconds
40
- beat: []
41
- },
42
- dead_max_jobs: 10_000,
43
- dead_timeout_in_seconds: 180 * 24 * 60 * 60, # 6 months
44
- reloader: proc { |&block| block.call }
45
- }
46
-
47
- FAKE_INFO = {
48
- "redis_version" => "9.9.9",
49
- "uptime_in_days" => "9999",
50
- "connected_clients" => "9999",
51
- "used_memory_human" => "9P",
52
- "used_memory_peak_human" => "9P"
53
- }
54
-
55
46
  def self.❨╯°□°❩╯︵┻━┻
56
- puts "Calm down, yo."
57
- end
58
-
59
- # config.concurrency = 5
60
- def self.concurrency=(val)
61
- self[:concurrency] = Integer(val)
62
- end
63
-
64
- # config.queues = %w( high default low ) # strict
65
- # config.queues = %w( high,3 default,2 low,1 ) # weighted
66
- # config.queues = %w( feature1,1 feature2,1 feature3,1 ) # random
67
- #
68
- # With weighted priority, queue will be checked first (weight / total) of the time.
69
- # high will be checked first (3/6) or 50% of the time.
70
- # I'd recommend setting weights between 1-10. Weights in the hundreds or thousands
71
- # are ridiculous and unnecessarily expensive. You can get random queue ordering
72
- # by explicitly setting all weights to 1.
73
- def self.queues=(val)
74
- self[:queues] = Array(val).each_with_object([]) do |qstr, memo|
75
- name, weight = qstr.split(",")
76
- self[:strict] = false if weight.to_i > 0
77
- [weight.to_i, 1].max.times do
78
- memo << name
79
- end
80
- end
81
- end
82
-
83
- ### Private APIs
84
- def self.default_error_handler(ex, ctx)
85
- logger.warn(dump_json(ctx)) unless ctx.empty?
86
- logger.warn("#{ex.class.name}: #{ex.message}")
87
- logger.warn(ex.backtrace.join("\n")) unless ex.backtrace.nil?
88
- end
89
-
90
- # DEFAULT_ERROR_HANDLER is a constant that allows the default error handler to
91
- # be referenced. It must be defined here, after the default_error_handler
92
- # method is defined.
93
- DEFAULT_ERROR_HANDLER = method(:default_error_handler)
94
-
95
- @config = DEFAULTS.dup
96
- def self.options
97
- logger.warn "`config.options[:key] = value` is deprecated, use `config[:key] = value`: #{caller(1..2)}"
98
- @config
99
- end
100
-
101
- def self.options=(opts)
102
- logger.warn "config.options = hash` is deprecated, use `config.merge!(hash)`: #{caller(1..2)}"
103
- @config = opts
47
+ puts "Take a deep breath and count to ten..."
104
48
  end
105
49
 
106
- def self.[](key)
107
- @config[key]
108
- end
109
-
110
- def self.[]=(key, val)
111
- @config[key] = val
112
- end
50
+ def self.testing!(mode = :fake, &block)
51
+ raise "Unknown testing mode: #{mode}" unless %i[fake disable inline].include?(mode)
113
52
 
114
- def self.merge!(hash)
115
- @config.merge!(hash)
116
- end
117
-
118
- def self.fetch(*args, &block)
119
- @config.fetch(*args, &block)
120
- end
121
-
122
- def self.handle_exception(ex, ctx = {})
123
- self[:error_handlers].each do |handler|
124
- handler.call(ex, ctx)
125
- rescue => ex
126
- logger.error "!!! ERROR HANDLER THREW AN ERROR !!!"
127
- logger.error ex
128
- logger.error ex.backtrace.join("\n") unless ex.backtrace.nil?
129
- end
130
- end
131
- ###
132
-
133
- ##
134
- # Configuration for Sidekiq server, use like:
135
- #
136
- # Sidekiq.configure_server do |config|
137
- # config.server_middleware do |chain|
138
- # chain.add MyServerHook
139
- # end
140
- # end
141
- def self.configure_server
142
- yield self if server?
143
- end
144
-
145
- ##
146
- # Configuration for Sidekiq client, use like:
147
- #
148
- # Sidekiq.configure_client do |config|
149
- # config.redis = { size: 1, url: 'redis://myhost:8877/0' }
150
- # end
151
- def self.configure_client
152
- yield self unless server?
53
+ require "sidekiq/test_api"
54
+ Sidekiq::Testing.__set_test_mode(mode, &block)
153
55
  end
154
56
 
155
57
  def self.server?
156
58
  defined?(Sidekiq::CLI)
157
59
  end
158
60
 
159
- def self.redis
160
- raise ArgumentError, "requires a block" unless block_given?
161
- redis_pool.with do |conn|
162
- retryable = true
163
- begin
164
- yield conn
165
- rescue RedisConnection.adapter::BaseError => ex
166
- # 2550 Failover can cause the server to become a replica, need
167
- # to disconnect and reopen the socket to get back to the primary.
168
- # 4495 Use the same logic if we have a "Not enough replicas" error from the primary
169
- # 4985 Use the same logic when a blocking command is force-unblocked
170
- # The same retry logic is also used in client.rb
171
- if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
172
- conn.disconnect!
173
- retryable = false
174
- retry
175
- end
176
- raise
177
- end
178
- end
179
- end
180
-
181
- def self.redis_info
182
- redis do |conn|
183
- # admin commands can't go through redis-namespace starting
184
- # in redis-namespace 2.0
185
- if conn.respond_to?(:namespace)
186
- conn.redis.info
187
- else
188
- conn.info
189
- end
190
- rescue RedisConnection.adapter::CommandError => ex
191
- # 2850 return fake version when INFO command has (probably) been renamed
192
- raise unless /unknown command/.match?(ex.message)
193
- FAKE_INFO
194
- end
61
+ def self.load_json(string)
62
+ JSON.parse(string)
195
63
  end
196
64
 
197
- def self.redis_pool
198
- @redis ||= RedisConnection.create
65
+ def self.dump_json(object)
66
+ JSON.generate(object)
199
67
  end
200
68
 
201
- def self.redis=(hash)
202
- @redis = if hash.is_a?(ConnectionPool)
203
- hash
204
- else
205
- RedisConnection.create(hash)
206
- end
69
+ def self.pro?
70
+ defined?(Sidekiq::Pro)
207
71
  end
208
72
 
209
- def self.client_middleware
210
- @client_chain ||= Middleware::Chain.new(self)
211
- yield @client_chain if block_given?
212
- @client_chain
73
+ def self.ent?
74
+ defined?(Sidekiq::Enterprise)
213
75
  end
214
76
 
215
- def self.server_middleware
216
- @server_chain ||= default_server_middleware
217
- yield @server_chain if block_given?
218
- @server_chain
77
+ def self.redis_pool
78
+ (Thread.current[:sidekiq_capsule] || default_configuration).redis_pool
219
79
  end
220
80
 
221
- def self.default_server_middleware
222
- Middleware::Chain.new(self)
81
+ def self.redis(&block)
82
+ (Thread.current[:sidekiq_capsule] || default_configuration).redis(&block)
223
83
  end
224
84
 
225
- def self.default_worker_options=(hash) # deprecated
226
- @default_job_options = default_job_options.merge(hash.transform_keys(&:to_s))
85
+ def self.strict_args!(mode = :raise)
86
+ Sidekiq::Config::DEFAULTS[:on_complex_arguments] = mode
227
87
  end
228
88
 
229
89
  def self.default_job_options=(hash)
230
90
  @default_job_options = default_job_options.merge(hash.transform_keys(&:to_s))
231
91
  end
232
92
 
233
- def self.default_worker_options # deprecated
234
- @default_job_options ||= {"retry" => true, "queue" => "default"}
235
- end
236
-
237
93
  def self.default_job_options
238
94
  @default_job_options ||= {"retry" => true, "queue" => "default"}
239
95
  end
240
96
 
241
- ##
242
- # Death handlers are called when all retries for a job have been exhausted and
243
- # the job dies. It's the notification to your application
244
- # that this job will not succeed without manual intervention.
245
- #
246
- # Sidekiq.configure_server do |config|
247
- # config.death_handlers << ->(job, ex) do
248
- # end
249
- # end
250
- def self.death_handlers
251
- self[:death_handlers]
252
- end
253
-
254
- def self.load_json(string)
255
- JSON.parse(string)
256
- end
257
-
258
- def self.dump_json(object)
259
- JSON.generate(object)
260
- end
261
-
262
- def self.log_formatter
263
- @log_formatter ||= if ENV["DYNO"]
264
- Sidekiq::Logger::Formatters::WithoutTimestamp.new
265
- else
266
- Sidekiq::Logger::Formatters::Pretty.new
267
- end
268
- end
269
-
270
- def self.log_formatter=(log_formatter)
271
- @log_formatter = log_formatter
272
- logger.formatter = log_formatter
97
+ def self.default_configuration
98
+ @config ||= Sidekiq::Config.new
273
99
  end
274
100
 
275
101
  def self.logger
276
- @logger ||= Sidekiq::Logger.new($stdout, level: :info)
102
+ default_configuration.logger
277
103
  end
278
104
 
279
- def self.logger=(logger)
280
- if logger.nil?
281
- self.logger.level = Logger::FATAL
282
- return self.logger
283
- end
284
-
285
- logger.extend(Sidekiq::LoggingUtils)
286
-
287
- @logger = logger
105
+ def self.loader
106
+ @loader ||= Loader.new
288
107
  end
289
108
 
290
- def self.pro?
291
- defined?(Sidekiq::Pro)
109
+ def self.configure_server(&block)
110
+ (@config_blocks ||= []) << block
111
+ yield default_configuration if server?
292
112
  end
293
113
 
294
- def self.ent?
295
- defined?(Sidekiq::Enterprise)
114
+ def self.freeze!
115
+ @frozen = true
116
+ @config_blocks = nil
117
+ default_configuration.freeze!
296
118
  end
297
119
 
298
- # How frequently Redis should be checked by a random Sidekiq process for
299
- # scheduled and retriable jobs. Each individual process will take turns by
300
- # waiting some multiple of this value.
120
+ # Creates a Sidekiq::Config instance that is more tuned for embedding
121
+ # within an arbitrary Ruby process. Notably it reduces concurrency by
122
+ # default so there is less contention for CPU time with other threads.
301
123
  #
302
- # See sidekiq/scheduled.rb for an in-depth explanation of this value
303
- def self.average_scheduled_poll_interval=(interval)
304
- self[:average_scheduled_poll_interval] = interval
305
- end
306
-
307
- # Register a proc to handle any error which occurs within the Sidekiq process.
308
- #
309
- # Sidekiq.configure_server do |config|
310
- # config.error_handlers << proc {|ex,ctx_hash| MyErrorService.notify(ex, ctx_hash) }
124
+ # instance = Sidekiq.configure_embed do |config|
125
+ # config.queues = %w[critical default low]
311
126
  # end
127
+ # instance.run
128
+ # sleep 10
129
+ # instance.stop
312
130
  #
313
- # The default error handler logs errors to Sidekiq.logger.
314
- def self.error_handlers
315
- self[:error_handlers]
316
- end
317
-
318
- # Register a block to run at a point in the Sidekiq lifecycle.
319
- # :startup, :quiet or :shutdown are valid events.
131
+ # NB: it is really easy to overload a Ruby process with threads due to the GIL.
132
+ # I do not recommend setting concurrency higher than 2-3.
320
133
  #
321
- # Sidekiq.configure_server do |config|
322
- # config.on(:shutdown) do
323
- # puts "Goodbye cruel world!"
324
- # end
325
- # end
326
- def self.on(event, &block)
327
- raise ArgumentError, "Symbols only please: #{event}" unless event.is_a?(Symbol)
328
- raise ArgumentError, "Invalid event name: #{event}" unless self[:lifecycle_events].key?(event)
329
- self[:lifecycle_events][event] << block
134
+ # NB: Sidekiq only supports one instance in memory. You will get undefined behavior
135
+ # if you try to embed Sidekiq twice in the same process.
136
+ def self.configure_embed(&block)
137
+ raise "Sidekiq global configuration is frozen, you must create all embedded instances BEFORE calling `run`" if @frozen
138
+
139
+ require "sidekiq/embedded"
140
+ cfg = default_configuration
141
+ cfg.concurrency = 2
142
+ @config_blocks&.each { |block| block.call(cfg) }
143
+ yield cfg
144
+
145
+ Sidekiq::Embedded.new(cfg)
330
146
  end
331
147
 
332
- def self.strict_args!(mode = :raise)
333
- self[:on_complex_arguments] = mode
148
+ def self.configure_client
149
+ yield default_configuration unless server?
334
150
  end
335
151
 
336
152
  # We are shutting down Sidekiq but what about threads that