sidekiq 6.3.1 → 7.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +285 -11
  3. data/LICENSE.txt +9 -0
  4. data/README.md +47 -34
  5. data/bin/sidekiq +4 -9
  6. data/bin/sidekiqload +207 -117
  7. data/bin/sidekiqmon +4 -1
  8. data/lib/generators/sidekiq/job_generator.rb +57 -0
  9. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
  10. data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
  11. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  12. data/lib/sidekiq/api.rb +333 -190
  13. data/lib/sidekiq/capsule.rb +127 -0
  14. data/lib/sidekiq/cli.rb +86 -80
  15. data/lib/sidekiq/client.rb +104 -95
  16. data/lib/sidekiq/{util.rb → component.rb} +14 -41
  17. data/lib/sidekiq/config.rb +282 -0
  18. data/lib/sidekiq/deploy.rb +62 -0
  19. data/lib/sidekiq/embedded.rb +61 -0
  20. data/lib/sidekiq/fetch.rb +23 -24
  21. data/lib/sidekiq/job.rb +371 -10
  22. data/lib/sidekiq/job_logger.rb +16 -28
  23. data/lib/sidekiq/job_retry.rb +99 -58
  24. data/lib/sidekiq/job_util.rb +107 -0
  25. data/lib/sidekiq/launcher.rb +103 -95
  26. data/lib/sidekiq/logger.rb +9 -44
  27. data/lib/sidekiq/manager.rb +40 -41
  28. data/lib/sidekiq/metrics/query.rb +153 -0
  29. data/lib/sidekiq/metrics/shared.rb +95 -0
  30. data/lib/sidekiq/metrics/tracking.rb +136 -0
  31. data/lib/sidekiq/middleware/chain.rb +96 -51
  32. data/lib/sidekiq/middleware/current_attributes.rb +59 -16
  33. data/lib/sidekiq/middleware/i18n.rb +6 -4
  34. data/lib/sidekiq/middleware/modules.rb +21 -0
  35. data/lib/sidekiq/monitor.rb +17 -4
  36. data/lib/sidekiq/paginator.rb +17 -9
  37. data/lib/sidekiq/processor.rb +81 -80
  38. data/lib/sidekiq/rails.rb +21 -14
  39. data/lib/sidekiq/redis_client_adapter.rb +95 -0
  40. data/lib/sidekiq/redis_connection.rb +14 -82
  41. data/lib/sidekiq/ring_buffer.rb +29 -0
  42. data/lib/sidekiq/scheduled.rb +76 -38
  43. data/lib/sidekiq/testing/inline.rb +4 -4
  44. data/lib/sidekiq/testing.rb +42 -69
  45. data/lib/sidekiq/transaction_aware_client.rb +44 -0
  46. data/lib/sidekiq/version.rb +2 -1
  47. data/lib/sidekiq/web/action.rb +3 -3
  48. data/lib/sidekiq/web/application.rb +95 -11
  49. data/lib/sidekiq/web/csrf_protection.rb +4 -4
  50. data/lib/sidekiq/web/helpers.rb +58 -30
  51. data/lib/sidekiq/web.rb +22 -17
  52. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  53. data/lib/sidekiq.rb +85 -202
  54. data/sidekiq.gemspec +12 -10
  55. data/web/assets/javascripts/application.js +77 -26
  56. data/web/assets/javascripts/base-charts.js +106 -0
  57. data/web/assets/javascripts/chart.min.js +13 -0
  58. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  59. data/web/assets/javascripts/dashboard-charts.js +168 -0
  60. data/web/assets/javascripts/dashboard.js +3 -240
  61. data/web/assets/javascripts/metrics.js +264 -0
  62. data/web/assets/stylesheets/application-dark.css +17 -17
  63. data/web/assets/stylesheets/application-rtl.css +2 -91
  64. data/web/assets/stylesheets/application.css +69 -302
  65. data/web/locales/ar.yml +70 -70
  66. data/web/locales/cs.yml +62 -62
  67. data/web/locales/da.yml +60 -53
  68. data/web/locales/de.yml +65 -65
  69. data/web/locales/el.yml +43 -24
  70. data/web/locales/en.yml +84 -69
  71. data/web/locales/es.yml +68 -68
  72. data/web/locales/fa.yml +65 -65
  73. data/web/locales/fr.yml +81 -67
  74. data/web/locales/gd.yml +99 -0
  75. data/web/locales/he.yml +65 -64
  76. data/web/locales/hi.yml +59 -59
  77. data/web/locales/it.yml +53 -53
  78. data/web/locales/ja.yml +73 -68
  79. data/web/locales/ko.yml +52 -52
  80. data/web/locales/lt.yml +66 -66
  81. data/web/locales/nb.yml +61 -61
  82. data/web/locales/nl.yml +52 -52
  83. data/web/locales/pl.yml +45 -45
  84. data/web/locales/pt-br.yml +83 -55
  85. data/web/locales/pt.yml +51 -51
  86. data/web/locales/ru.yml +67 -66
  87. data/web/locales/sv.yml +53 -53
  88. data/web/locales/ta.yml +60 -60
  89. data/web/locales/uk.yml +62 -61
  90. data/web/locales/ur.yml +64 -64
  91. data/web/locales/vi.yml +67 -67
  92. data/web/locales/zh-cn.yml +43 -16
  93. data/web/locales/zh-tw.yml +42 -8
  94. data/web/views/_footer.erb +5 -2
  95. data/web/views/_job_info.erb +18 -2
  96. data/web/views/_metrics_period_select.erb +12 -0
  97. data/web/views/_nav.erb +1 -1
  98. data/web/views/_paging.erb +2 -0
  99. data/web/views/_poll_link.erb +1 -1
  100. data/web/views/_summary.erb +1 -1
  101. data/web/views/busy.erb +44 -28
  102. data/web/views/dashboard.erb +36 -4
  103. data/web/views/filtering.erb +7 -0
  104. data/web/views/metrics.erb +82 -0
  105. data/web/views/metrics_for_job.erb +68 -0
  106. data/web/views/morgue.erb +5 -9
  107. data/web/views/queue.erb +15 -15
  108. data/web/views/queues.erb +3 -1
  109. data/web/views/retries.erb +5 -9
  110. data/web/views/scheduled.erb +12 -13
  111. metadata +62 -31
  112. data/LICENSE +0 -9
  113. data/lib/generators/sidekiq/worker_generator.rb +0 -57
  114. data/lib/sidekiq/delay.rb +0 -41
  115. data/lib/sidekiq/exception_handler.rb +0 -27
  116. data/lib/sidekiq/extensions/action_mailer.rb +0 -48
  117. data/lib/sidekiq/extensions/active_record.rb +0 -43
  118. data/lib/sidekiq/extensions/class_methods.rb +0 -43
  119. data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
  120. data/lib/sidekiq/worker.rb +0 -311
data/lib/sidekiq/web.rb CHANGED
@@ -30,9 +30,22 @@ module Sidekiq
30
30
  "Queues" => "queues",
31
31
  "Retries" => "retries",
32
32
  "Scheduled" => "scheduled",
33
- "Dead" => "morgue"
33
+ "Dead" => "morgue",
34
+ "Metrics" => "metrics"
34
35
  }
35
36
 
37
+ if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3")
38
+ CONTENT_LANGUAGE = "Content-Language"
39
+ CONTENT_SECURITY_POLICY = "Content-Security-Policy"
40
+ LOCATION = "Location"
41
+ X_CASCADE = "X-Cascade"
42
+ else
43
+ CONTENT_LANGUAGE = "content-language"
44
+ CONTENT_SECURITY_POLICY = "content-security-policy"
45
+ LOCATION = "location"
46
+ X_CASCADE = "x-cascade"
47
+ end
48
+
36
49
  class << self
37
50
  def settings
38
51
  self
@@ -47,6 +60,10 @@ module Sidekiq
47
60
  end
48
61
  alias_method :tabs, :custom_tabs
49
62
 
63
+ def custom_job_info_rows
64
+ @custom_job_info_rows ||= []
65
+ end
66
+
50
67
  def locales
51
68
  @locales ||= LOCALES
52
69
  end
@@ -75,14 +92,6 @@ module Sidekiq
75
92
  send(:"#{attribute}=", value)
76
93
  end
77
94
 
78
- def sessions=(val)
79
- puts "WARNING: Sidekiq::Web.sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
80
- end
81
-
82
- def session_secret=(val)
83
- puts "WARNING: Sidekiq::Web.session_secret= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
84
- end
85
-
86
95
  attr_accessor :app_url, :redis_pool
87
96
  attr_writer :locales, :views
88
97
  end
@@ -129,10 +138,6 @@ module Sidekiq
129
138
  send(:"#{attribute}=", value)
130
139
  end
131
140
 
132
- def sessions=(val)
133
- puts "Sidekiq::Web#sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller[2..2].first}"
134
- end
135
-
136
141
  def self.register(extension)
137
142
  extension.registered(WebApplication)
138
143
  end
@@ -144,13 +149,13 @@ module Sidekiq
144
149
  m = middlewares
145
150
 
146
151
  rules = []
147
- rules = [[:all, {"Cache-Control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
152
+ rules = [[:all, {Rack::CACHE_CONTROL => "private, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
148
153
 
149
154
  ::Rack::Builder.new do
150
155
  use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
151
- root: ASSETS,
152
- cascade: true,
153
- header_rules: rules
156
+ root: ASSETS,
157
+ cascade: true,
158
+ header_rules: rules
154
159
  m.each { |middleware, block| use(*middleware, &block) }
155
160
  use Sidekiq::Web::CsrfProtection unless $TESTING
156
161
  run WebApplication.new(klass)
@@ -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,14 +1,39 @@
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 2.7.0." if RUBY_PLATFORM != "java" && Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.7.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"
7
32
  require "sidekiq/client"
8
- require "sidekiq/worker"
33
+ require "sidekiq/transaction_aware_client"
9
34
  require "sidekiq/job"
10
- require "sidekiq/redis_connection"
11
- require "sidekiq/delay"
35
+ require "sidekiq/worker_compatibility_alias"
36
+ require "sidekiq/redis_client_adapter"
12
37
 
13
38
  require "json"
14
39
 
@@ -16,248 +41,106 @@ module Sidekiq
16
41
  NAME = "Sidekiq"
17
42
  LICENSE = "See LICENSE and the LGPL-3.0 for licensing details."
18
43
 
19
- DEFAULTS = {
20
- queues: [],
21
- labels: [],
22
- concurrency: 10,
23
- require: ".",
24
- strict: true,
25
- environment: nil,
26
- timeout: 25,
27
- poll_interval_average: nil,
28
- average_scheduled_poll_interval: 5,
29
- error_handlers: [],
30
- death_handlers: [],
31
- lifecycle_events: {
32
- startup: [],
33
- quiet: [],
34
- shutdown: [],
35
- heartbeat: []
36
- },
37
- dead_max_jobs: 10_000,
38
- dead_timeout_in_seconds: 180 * 24 * 60 * 60, # 6 months
39
- reloader: proc { |&block| block.call }
40
- }
41
-
42
- DEFAULT_WORKER_OPTIONS = {
43
- "retry" => true,
44
- "queue" => "default"
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
44
  def self.❨╯°□°❩╯︵┻━┻
56
- puts "Calm down, yo."
57
- end
58
-
59
- def self.options
60
- @options ||= DEFAULTS.dup
61
- end
62
-
63
- def self.options=(opts)
64
- @options = opts
65
- end
66
-
67
- ##
68
- # Configuration for Sidekiq server, use like:
69
- #
70
- # Sidekiq.configure_server do |config|
71
- # config.redis = { :namespace => 'myapp', :size => 25, :url => 'redis://myhost:8877/0' }
72
- # config.server_middleware do |chain|
73
- # chain.add MyServerHook
74
- # end
75
- # end
76
- def self.configure_server
77
- yield self if server?
78
- end
79
-
80
- ##
81
- # Configuration for Sidekiq client, use like:
82
- #
83
- # Sidekiq.configure_client do |config|
84
- # config.redis = { :namespace => 'myapp', :size => 1, :url => 'redis://myhost:8877/0' }
85
- # end
86
- def self.configure_client
87
- yield self unless server?
45
+ puts "Take a deep breath and count to ten..."
88
46
  end
89
47
 
90
48
  def self.server?
91
49
  defined?(Sidekiq::CLI)
92
50
  end
93
51
 
94
- def self.redis
95
- raise ArgumentError, "requires a block" unless block_given?
96
- redis_pool.with do |conn|
97
- retryable = true
98
- begin
99
- yield conn
100
- rescue Redis::BaseError => ex
101
- # 2550 Failover can cause the server to become a replica, need
102
- # to disconnect and reopen the socket to get back to the primary.
103
- # 4495 Use the same logic if we have a "Not enough replicas" error from the primary
104
- # 4985 Use the same logic when a blocking command is force-unblocked
105
- if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
106
- conn.disconnect!
107
- retryable = false
108
- retry
109
- end
110
- raise
111
- end
112
- end
113
- end
114
-
115
- def self.redis_info
116
- redis do |conn|
117
- # admin commands can't go through redis-namespace starting
118
- # in redis-namespace 2.0
119
- if conn.respond_to?(:namespace)
120
- conn.redis.info
121
- else
122
- conn.info
123
- end
124
- rescue Redis::CommandError => ex
125
- # 2850 return fake version when INFO command has (probably) been renamed
126
- raise unless /unknown command/.match?(ex.message)
127
- FAKE_INFO
128
- end
129
- end
130
-
131
- def self.redis_pool
132
- @redis ||= Sidekiq::RedisConnection.create
133
- end
134
-
135
- def self.redis=(hash)
136
- @redis = if hash.is_a?(ConnectionPool)
137
- hash
138
- else
139
- Sidekiq::RedisConnection.create(hash)
140
- end
52
+ def self.load_json(string)
53
+ JSON.parse(string)
141
54
  end
142
55
 
143
- def self.client_middleware
144
- @client_chain ||= Middleware::Chain.new
145
- yield @client_chain if block_given?
146
- @client_chain
56
+ def self.dump_json(object)
57
+ JSON.generate(object)
147
58
  end
148
59
 
149
- def self.server_middleware
150
- @server_chain ||= default_server_middleware
151
- yield @server_chain if block_given?
152
- @server_chain
60
+ def self.pro?
61
+ defined?(Sidekiq::Pro)
153
62
  end
154
63
 
155
- def self.default_server_middleware
156
- Middleware::Chain.new
64
+ def self.ent?
65
+ defined?(Sidekiq::Enterprise)
157
66
  end
158
67
 
159
- def self.default_worker_options=(hash)
160
- # stringify
161
- @default_worker_options = default_worker_options.merge(hash.transform_keys(&:to_s))
68
+ def self.redis_pool
69
+ (Thread.current[:sidekiq_capsule] || default_configuration).redis_pool
162
70
  end
163
71
 
164
- def self.default_worker_options
165
- defined?(@default_worker_options) ? @default_worker_options : DEFAULT_WORKER_OPTIONS
72
+ def self.redis(&block)
73
+ (Thread.current[:sidekiq_capsule] || default_configuration).redis(&block)
166
74
  end
167
75
 
168
- ##
169
- # Death handlers are called when all retries for a job have been exhausted and
170
- # the job dies. It's the notification to your application
171
- # that this job will not succeed without manual intervention.
172
- #
173
- # Sidekiq.configure_server do |config|
174
- # config.death_handlers << ->(job, ex) do
175
- # end
176
- # end
177
- def self.death_handlers
178
- options[:death_handlers]
76
+ def self.strict_args!(mode = :raise)
77
+ Sidekiq::Config::DEFAULTS[:on_complex_arguments] = mode
179
78
  end
180
79
 
181
- def self.load_json(string)
182
- JSON.parse(string)
80
+ def self.default_job_options=(hash)
81
+ @default_job_options = default_job_options.merge(hash.transform_keys(&:to_s))
183
82
  end
184
83
 
185
- def self.dump_json(object)
186
- JSON.generate(object)
84
+ def self.default_job_options
85
+ @default_job_options ||= {"retry" => true, "queue" => "default"}
187
86
  end
188
87
 
189
- def self.log_formatter
190
- @log_formatter ||= if ENV["DYNO"]
191
- Sidekiq::Logger::Formatters::WithoutTimestamp.new
192
- else
193
- Sidekiq::Logger::Formatters::Pretty.new
194
- end
195
- end
196
-
197
- def self.log_formatter=(log_formatter)
198
- @log_formatter = log_formatter
199
- logger.formatter = log_formatter
88
+ def self.default_configuration
89
+ @config ||= Sidekiq::Config.new
200
90
  end
201
91
 
202
92
  def self.logger
203
- @logger ||= Sidekiq::Logger.new($stdout, level: Logger::INFO)
93
+ default_configuration.logger
204
94
  end
205
95
 
206
- def self.logger=(logger)
207
- if logger.nil?
208
- self.logger.level = Logger::FATAL
209
- return self.logger
210
- end
211
-
212
- logger.extend(Sidekiq::LoggingUtils)
213
-
214
- @logger = logger
215
- end
216
-
217
- def self.pro?
218
- defined?(Sidekiq::Pro)
96
+ def self.configure_server(&block)
97
+ (@config_blocks ||= []) << block
98
+ yield default_configuration if server?
219
99
  end
220
100
 
221
- # How frequently Redis should be checked by a random Sidekiq process for
222
- # scheduled and retriable jobs. Each individual process will take turns by
223
- # waiting some multiple of this value.
224
- #
225
- # See sidekiq/scheduled.rb for an in-depth explanation of this value
226
- def self.average_scheduled_poll_interval=(interval)
227
- options[:average_scheduled_poll_interval] = interval
101
+ def self.freeze!
102
+ @frozen = true
103
+ @config_blocks = nil
228
104
  end
229
105
 
230
- # Register a proc to handle any error which occurs within the Sidekiq process.
106
+ # Creates a Sidekiq::Config instance that is more tuned for embedding
107
+ # within an arbitrary Ruby process. Notably it reduces concurrency by
108
+ # default so there is less contention for CPU time with other threads.
231
109
  #
232
- # Sidekiq.configure_server do |config|
233
- # config.error_handlers << proc {|ex,ctx_hash| MyErrorService.notify(ex, ctx_hash) }
110
+ # inst = Sidekiq.configure_embed do |config|
111
+ # config.queues = %w[critical default low]
234
112
  # end
113
+ # inst.run
114
+ # sleep 10
115
+ # inst.terminate
116
+ #
117
+ # NB: it is really easy to overload a Ruby process with threads due to the GIL.
118
+ # I do not recommend setting concurrency higher than 2-3.
235
119
  #
236
- # The default error handler logs errors to Sidekiq.logger.
237
- def self.error_handlers
238
- options[:error_handlers]
120
+ # NB: Sidekiq only supports one instance in memory. You will get undefined behavior
121
+ # if you try to embed Sidekiq twice in the same process.
122
+ def self.configure_embed(&block)
123
+ raise "Sidekiq global configuration is frozen, you must create all embedded instances BEFORE calling `run`" if @frozen
124
+
125
+ require "sidekiq/embedded"
126
+ cfg = default_configuration
127
+ cfg.concurrency = 2
128
+ @config_blocks&.each { |block| block.call(cfg) }
129
+ yield cfg
130
+
131
+ Sidekiq::Embedded.new(cfg)
239
132
  end
240
133
 
241
- # Register a block to run at a point in the Sidekiq lifecycle.
242
- # :startup, :quiet or :shutdown are valid events.
243
- #
244
- # Sidekiq.configure_server do |config|
245
- # config.on(:shutdown) do
246
- # puts "Goodbye cruel world!"
247
- # end
248
- # end
249
- def self.on(event, &block)
250
- raise ArgumentError, "Symbols only please: #{event}" unless event.is_a?(Symbol)
251
- raise ArgumentError, "Invalid event name: #{event}" unless options[:lifecycle_events].key?(event)
252
- options[:lifecycle_events][event] << block
134
+ def self.configure_client
135
+ yield default_configuration unless server?
253
136
  end
254
137
 
255
- # We are shutting down Sidekiq but what about workers that
138
+ # We are shutting down Sidekiq but what about threads that
256
139
  # are working on some long job? This error is
257
- # raised in workers that have not finished within the hard
140
+ # raised in jobs that have not finished within the hard
258
141
  # timeout limit. This is needed to rollback db transactions,
259
142
  # otherwise Ruby's Thread#kill will commit. See #377.
260
- # DO NOT RESCUE THIS ERROR IN YOUR WORKERS
143
+ # DO NOT RESCUE THIS ERROR IN YOUR JOBS
261
144
  class Shutdown < Interrupt; end
262
145
  end
263
146
 
data/sidekiq.gemspec CHANGED
@@ -2,27 +2,29 @@ require_relative "lib/sidekiq/version"
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.authors = ["Mike Perham"]
5
- gem.email = ["mperham@gmail.com"]
5
+ gem.email = ["info@contribsys.com"]
6
6
  gem.summary = "Simple, efficient background processing for Ruby"
7
7
  gem.description = "Simple, efficient background processing for Ruby."
8
8
  gem.homepage = "https://sidekiq.org"
9
9
  gem.license = "LGPL-3.0"
10
10
 
11
11
  gem.executables = ["sidekiq", "sidekiqmon"]
12
- gem.files = ["sidekiq.gemspec", "README.md", "Changes.md", "LICENSE"] + `git ls-files | grep -E '^(bin|lib|web)'`.split("\n")
12
+ gem.files = %w[sidekiq.gemspec README.md Changes.md LICENSE.txt] + `git ls-files | grep -E '^(bin|lib|web)'`.split("\n")
13
13
  gem.name = "sidekiq"
14
14
  gem.version = Sidekiq::VERSION
15
- gem.required_ruby_version = ">= 2.5.0"
15
+ gem.required_ruby_version = ">= 2.7.0"
16
16
 
17
17
  gem.metadata = {
18
18
  "homepage_uri" => "https://sidekiq.org",
19
- "bug_tracker_uri" => "https://github.com/mperham/sidekiq/issues",
20
- "documentation_uri" => "https://github.com/mperham/sidekiq/wiki",
21
- "changelog_uri" => "https://github.com/mperham/sidekiq/blob/main/Changes.md",
22
- "source_code_uri" => "https://github.com/mperham/sidekiq"
19
+ "bug_tracker_uri" => "https://github.com/sidekiq/sidekiq/issues",
20
+ "documentation_uri" => "https://github.com/sidekiq/sidekiq/wiki",
21
+ "changelog_uri" => "https://github.com/sidekiq/sidekiq/blob/main/Changes.md",
22
+ "source_code_uri" => "https://github.com/sidekiq/sidekiq",
23
+ "rubygems_mfa_required" => "true"
23
24
  }
24
25
 
25
- gem.add_dependency "redis", ">= 4.2.0"
26
- gem.add_dependency "connection_pool", ">= 2.2.2"
27
- gem.add_dependency "rack", "~> 2.0"
26
+ gem.add_dependency "redis-client", ">= 0.14.0"
27
+ gem.add_dependency "connection_pool", ">= 2.3.0"
28
+ gem.add_dependency "rack", ">= 2.2.4"
29
+ gem.add_dependency "concurrent-ruby", "< 2"
28
30
  end
@@ -9,7 +9,9 @@ var ready = (callback) => {
9
9
  else document.addEventListener("DOMContentLoaded", callback);
10
10
  }
11
11
 
12
- ready(() => {
12
+ ready(addListeners)
13
+
14
+ function addListeners() {
13
15
  document.querySelectorAll(".check_all").forEach(node => {
14
16
  node.addEventListener("click", event => {
15
17
  node.closest('table').querySelectorAll('input[type=checkbox]').forEach(inp => { inp.checked = !!node.checked; });
@@ -26,42 +28,66 @@ ready(() => {
26
28
  })
27
29
 
28
30
  document.querySelectorAll("[data-toggle]").forEach(node => {
29
- node.addEventListener("click", event => {
30
- var targName = node.getAttribute("data-toggle");
31
- var full = document.getElementById(targName + "_full");
32
- if (full.style.display == "block") {
33
- full.style.display = 'none';
34
- } else {
35
- full.style.display = 'block';
36
- }
37
- })
31
+ node.addEventListener("click", addDataToggleListeners)
38
32
  })
39
33
 
34
+ addShiftClickListeners()
40
35
  updateFuzzyTimes();
36
+ setLivePollFromUrl();
41
37
 
42
38
  var buttons = document.querySelectorAll(".live-poll");
43
39
  if (buttons.length > 0) {
44
40
  buttons.forEach(node => {
45
- node.addEventListener("click", event => {
46
- if (localStorage.sidekiqLivePoll == "enabled") {
47
- localStorage.sidekiqLivePoll = "disabled";
48
- clearTimeout(livePollTimer);
49
- livePollTimer = null;
50
- } else {
51
- localStorage.sidekiqLivePoll = "enabled";
52
- livePollCallback();
53
- }
54
-
55
- updateLivePollButton();
56
- })
41
+ node.addEventListener("click", addPollingListeners)
57
42
  });
58
43
 
59
44
  updateLivePollButton();
60
- if (localStorage.sidekiqLivePoll == "enabled") {
45
+ if (localStorage.sidekiqLivePoll == "enabled" && !livePollTimer) {
61
46
  scheduleLivePoll();
62
47
  }
63
48
  }
64
- })
49
+ }
50
+
51
+ function addPollingListeners(_event) {
52
+ if (localStorage.sidekiqLivePoll == "enabled") {
53
+ localStorage.sidekiqLivePoll = "disabled";
54
+ clearTimeout(livePollTimer);
55
+ livePollTimer = null;
56
+ } else {
57
+ localStorage.sidekiqLivePoll = "enabled";
58
+ livePollCallback();
59
+ }
60
+
61
+ updateLivePollButton();
62
+ }
63
+
64
+ function addDataToggleListeners(event) {
65
+ var source = event.target || event.srcElement;
66
+ var targName = source.getAttribute("data-toggle");
67
+ var full = document.getElementById(targName);
68
+ if (full.style.display == "block") {
69
+ full.style.display = 'none';
70
+ } else {
71
+ full.style.display = 'block';
72
+ }
73
+ }
74
+
75
+ function addShiftClickListeners() {
76
+ let checkboxes = Array.from(document.querySelectorAll(".shift_clickable"));
77
+ let lastChecked = null;
78
+ checkboxes.forEach(checkbox => {
79
+ checkbox.addEventListener("click", (e) => {
80
+ if (e.shiftKey && lastChecked) {
81
+ let myIndex = checkboxes.indexOf(checkbox);
82
+ let lastIndex = checkboxes.indexOf(lastChecked);
83
+ let [min, max] = [myIndex, lastIndex].sort();
84
+ let newState = checkbox.checked;
85
+ checkboxes.slice(min, max).forEach(c => c.checked = newState);
86
+ }
87
+ lastChecked = checkbox;
88
+ });
89
+ });
90
+ }
65
91
 
66
92
  function updateFuzzyTimes() {
67
93
  var locale = document.body.getAttribute("data-locale");
@@ -76,6 +102,14 @@ function updateFuzzyTimes() {
76
102
  t.cancel();
77
103
  }
78
104
 
105
+ function setLivePollFromUrl() {
106
+ var url_params = new URL(window.location.href).searchParams
107
+
108
+ if (url_params.get("poll") == "true") {
109
+ localStorage.sidekiqLivePoll = "enabled";
110
+ }
111
+ }
112
+
79
113
  function updateLivePollButton() {
80
114
  if (localStorage.sidekiqLivePoll == "enabled") {
81
115
  document.querySelectorAll('.live-poll-stop').forEach(box => { box.style.display = "inline-block" })
@@ -89,11 +123,24 @@ function updateLivePollButton() {
89
123
  function livePollCallback() {
90
124
  clearTimeout(livePollTimer);
91
125
 
92
- fetch(window.location.href).then(resp => resp.text()).then(replacePage).finally(scheduleLivePoll)
126
+ fetch(window.location.href)
127
+ .then(checkResponse)
128
+ .then(resp => resp.text())
129
+ .then(replacePage)
130
+ .catch(showError)
131
+ .finally(scheduleLivePoll)
132
+ }
133
+
134
+ function checkResponse(resp) {
135
+ if (!resp.ok) {
136
+ throw response.error();
137
+ }
138
+ return resp
93
139
  }
94
140
 
95
141
  function scheduleLivePoll() {
96
142
  let ti = parseInt(localStorage.sidekiqTimeInterval) || 5000;
143
+ if (ti < 2000) { ti = 2000 }
97
144
  livePollTimer = setTimeout(livePollCallback, ti);
98
145
  }
99
146
 
@@ -107,5 +154,9 @@ function replacePage(text) {
107
154
  var header_status = doc.querySelector('.status')
108
155
  document.querySelector('.status').replaceWith(header_status)
109
156
 
110
- updateFuzzyTimes();
157
+ addListeners();
158
+ }
159
+
160
+ function showError(error) {
161
+ console.error(error)
111
162
  }