sidekiq 6.5.12 → 7.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +5 -24
  3. data/README.md +13 -12
  4. data/bin/sidekiq +3 -8
  5. data/bin/sidekiqload +14 -23
  6. data/lib/sidekiq/api.rb +51 -127
  7. data/lib/sidekiq/capsule.rb +110 -0
  8. data/lib/sidekiq/cli.rb +44 -59
  9. data/lib/sidekiq/client.rb +30 -17
  10. data/lib/sidekiq/component.rb +1 -0
  11. data/lib/sidekiq/config.rb +270 -0
  12. data/lib/sidekiq/deploy.rb +62 -0
  13. data/lib/sidekiq/embedded.rb +61 -0
  14. data/lib/sidekiq/fetch.rb +10 -11
  15. data/lib/sidekiq/job.rb +375 -10
  16. data/lib/sidekiq/job_logger.rb +1 -1
  17. data/lib/sidekiq/job_retry.rb +8 -8
  18. data/lib/sidekiq/job_util.rb +4 -4
  19. data/lib/sidekiq/launcher.rb +36 -46
  20. data/lib/sidekiq/logger.rb +1 -26
  21. data/lib/sidekiq/manager.rb +9 -11
  22. data/lib/sidekiq/metrics/query.rb +3 -3
  23. data/lib/sidekiq/metrics/shared.rb +4 -3
  24. data/lib/sidekiq/metrics/tracking.rb +18 -18
  25. data/lib/sidekiq/middleware/chain.rb +7 -9
  26. data/lib/sidekiq/middleware/current_attributes.rb +3 -8
  27. data/lib/sidekiq/monitor.rb +1 -1
  28. data/lib/sidekiq/paginator.rb +1 -9
  29. data/lib/sidekiq/pool.rb +7 -0
  30. data/lib/sidekiq/processor.rb +17 -26
  31. data/lib/sidekiq/rails.rb +11 -10
  32. data/lib/sidekiq/redis_client_adapter.rb +9 -45
  33. data/lib/sidekiq/redis_connection.rb +11 -111
  34. data/lib/sidekiq/scheduled.rb +19 -20
  35. data/lib/sidekiq/testing.rb +4 -32
  36. data/lib/sidekiq/transaction_aware_client.rb +4 -5
  37. data/lib/sidekiq/version.rb +1 -1
  38. data/lib/sidekiq/web/application.rb +1 -4
  39. data/lib/sidekiq/web/csrf_protection.rb +1 -1
  40. data/lib/sidekiq/web/helpers.rb +21 -22
  41. data/lib/sidekiq/web.rb +2 -17
  42. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  43. data/lib/sidekiq.rb +52 -274
  44. data/sidekiq.gemspec +29 -5
  45. data/web/assets/javascripts/application.js +0 -1
  46. data/web/assets/javascripts/base-charts.js +106 -0
  47. data/web/assets/javascripts/dashboard-charts.js +166 -0
  48. data/web/assets/javascripts/dashboard.js +3 -223
  49. data/web/assets/javascripts/metrics.js +90 -116
  50. data/web/assets/stylesheets/application-rtl.css +2 -91
  51. data/web/assets/stylesheets/application.css +21 -296
  52. data/web/locales/ar.yml +70 -70
  53. data/web/locales/cs.yml +62 -62
  54. data/web/locales/da.yml +52 -52
  55. data/web/locales/de.yml +65 -65
  56. data/web/locales/el.yml +2 -7
  57. data/web/locales/en.yml +76 -70
  58. data/web/locales/es.yml +68 -68
  59. data/web/locales/fa.yml +65 -65
  60. data/web/locales/fr.yml +67 -67
  61. data/web/locales/he.yml +65 -64
  62. data/web/locales/hi.yml +59 -59
  63. data/web/locales/it.yml +53 -53
  64. data/web/locales/ja.yml +64 -68
  65. data/web/locales/ko.yml +52 -52
  66. data/web/locales/lt.yml +66 -66
  67. data/web/locales/nb.yml +61 -61
  68. data/web/locales/nl.yml +52 -52
  69. data/web/locales/pl.yml +45 -45
  70. data/web/locales/pt-br.yml +59 -69
  71. data/web/locales/pt.yml +51 -51
  72. data/web/locales/ru.yml +67 -66
  73. data/web/locales/sv.yml +53 -53
  74. data/web/locales/ta.yml +60 -60
  75. data/web/locales/uk.yml +62 -61
  76. data/web/locales/ur.yml +64 -64
  77. data/web/locales/vi.yml +67 -67
  78. data/web/locales/zh-cn.yml +1 -0
  79. data/web/locales/zh-tw.yml +10 -1
  80. data/web/views/_footer.erb +5 -2
  81. data/web/views/busy.erb +1 -6
  82. data/web/views/dashboard.erb +36 -5
  83. data/web/views/metrics.erb +30 -19
  84. data/web/views/metrics_for_job.erb +16 -34
  85. metadata +60 -37
  86. data/lib/sidekiq/delay.rb +0 -43
  87. data/lib/sidekiq/extensions/action_mailer.rb +0 -48
  88. data/lib/sidekiq/extensions/active_record.rb +0 -43
  89. data/lib/sidekiq/extensions/class_methods.rb +0 -43
  90. data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
  91. data/lib/sidekiq/metrics/deploy.rb +0 -47
  92. data/lib/sidekiq/worker.rb +0 -370
  93. data/web/assets/javascripts/graph.js +0 -16
  94. /data/{LICENSE → LICENSE.txt} +0 -0
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sidekiq/manager"
4
- require "sidekiq/fetch"
4
+ require "sidekiq/capsule"
5
5
  require "sidekiq/scheduled"
6
6
  require "sidekiq/ring_buffer"
7
7
 
8
8
  module Sidekiq
9
- # The Launcher starts the Manager and Poller threads and provides the process heartbeat.
9
+ # The Launcher starts the Capsule Managers, the Poller thread and provides the process heartbeat.
10
10
  class Launcher
11
11
  include Sidekiq::Component
12
12
 
@@ -16,48 +16,53 @@ module Sidekiq
16
16
  proc { "sidekiq" },
17
17
  proc { Sidekiq::VERSION },
18
18
  proc { |me, data| data["tag"] },
19
- proc { |me, data| "[#{Processor::WORK_STATE.size} of #{data["concurrency"]} busy]" },
19
+ proc { |me, data| "[#{Processor::WORK_STATE.size} of #{me.config.total_concurrency} busy]" },
20
20
  proc { |me, data| "stopping" if me.stopping? }
21
21
  ]
22
22
 
23
- attr_accessor :manager, :poller, :fetcher
23
+ attr_accessor :managers, :poller
24
24
 
25
- def initialize(options)
26
- @config = options
27
- options[:fetch] ||= BasicFetch.new(options)
28
- @manager = Sidekiq::Manager.new(options)
29
- @poller = Sidekiq::Scheduled::Poller.new(options)
25
+ def initialize(config, embedded: false)
26
+ @config = config
27
+ @embedded = embedded
28
+ @managers = config.capsules.values.map do |cap|
29
+ Sidekiq::Manager.new(cap)
30
+ end
31
+ @poller = Sidekiq::Scheduled::Poller.new(@config)
30
32
  @done = false
31
33
  end
32
34
 
33
35
  def run
36
+ Sidekiq.freeze!
34
37
  @thread = safe_thread("heartbeat", &method(:start_heartbeat))
35
38
  @poller.start
36
- @manager.start
39
+ @managers.each(&:start)
37
40
  end
38
41
 
39
42
  # Stops this instance from processing any more jobs,
40
43
  #
41
44
  def quiet
45
+ return if @done
46
+
42
47
  @done = true
43
- @manager.quiet
48
+ @managers.each(&:quiet)
44
49
  @poller.terminate
50
+ fire_event(:quiet, reverse: true)
45
51
  end
46
52
 
47
53
  # Shuts down this Sidekiq instance. Waits up to the deadline for all jobs to complete.
48
54
  def stop
49
55
  deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + @config[:timeout]
50
56
 
51
- @done = true
52
- @manager.quiet
53
- @poller.terminate
54
-
55
- @manager.stop(deadline)
57
+ quiet
58
+ stoppers = @managers.map do |mgr|
59
+ Thread.new do
60
+ mgr.stop(deadline)
61
+ end
62
+ end
56
63
 
57
- # Requeue everything in case there was a thread which fetched a job while the process was stopped.
58
- # This call is a no-op in Sidekiq but necessary for Sidekiq Pro.
59
- strategy = @config[:fetch]
60
- strategy.bulk_requeue([], @config)
64
+ fire_event(:shutdown, reverse: true)
65
+ stoppers.each(&:join)
61
66
 
62
67
  clear_heartbeat
63
68
  end
@@ -68,7 +73,7 @@ module Sidekiq
68
73
 
69
74
  private unless $TESTING
70
75
 
71
- BEAT_PAUSE = 5
76
+ BEAT_PAUSE = 10
72
77
 
73
78
  def start_heartbeat
74
79
  loop do
@@ -95,7 +100,7 @@ module Sidekiq
95
100
  end
96
101
 
97
102
  def heartbeat
98
- $0 = PROCTITLES.map { |proc| proc.call(self, to_data) }.compact.join(" ")
103
+ $0 = PROCTITLES.map { |proc| proc.call(self, to_data) }.compact.join(" ") unless @embedded
99
104
 
100
105
 
101
106
  end
@@ -107,7 +112,7 @@ module Sidekiq
107
112
 
108
113
  nowdate = Time.now.utc.strftime("%Y-%m-%d")
109
114
  begin
110
- Sidekiq.redis do |conn|
115
+ redis do |conn|
111
116
  conn.pipelined do |pipeline|
112
117
  pipeline.incrby("stat:processed", procd)
113
118
  pipeline.incrby("stat:processed:#{nowdate}", procd)
@@ -119,9 +124,7 @@ module Sidekiq
119
124
  end
120
125
  end
121
126
  rescue => ex
122
- # we're exiting the process, things might be shut down so don't
123
- # try to handle the exception
124
- Sidekiq.logger.warn("Unable to flush stats: #{ex}")
127
+ logger.warn("Unable to flush stats: #{ex}")
125
128
  end
126
129
  end
127
130
 
@@ -130,23 +133,10 @@ module Sidekiq
130
133
  fails = procd = 0
131
134
 
132
135
  begin
133
- fails = Processor::FAILURE.reset
134
- procd = Processor::PROCESSED.reset
135
- curstate = Processor::WORK_STATE.dup
136
-
137
- nowdate = Time.now.utc.strftime("%Y-%m-%d")
136
+ flush_stats
138
137
 
138
+ curstate = Processor::WORK_STATE.dup
139
139
  redis do |conn|
140
- conn.multi do |transaction|
141
- transaction.incrby("stat:processed", procd)
142
- transaction.incrby("stat:processed:#{nowdate}", procd)
143
- transaction.expire("stat:processed:#{nowdate}", STATS_TTL)
144
-
145
- transaction.incrby("stat:failed", fails)
146
- transaction.incrby("stat:failed:#{nowdate}", fails)
147
- transaction.expire("stat:failed:#{nowdate}", STATS_TTL)
148
- end
149
-
150
140
  # work is the current set of executing jobs
151
141
  work_key = "#{key}:work"
152
142
  conn.pipelined do |transaction|
@@ -166,7 +156,7 @@ module Sidekiq
166
156
  _, exists, _, _, msg = redis { |conn|
167
157
  conn.multi { |transaction|
168
158
  transaction.sadd("processes", [key])
169
- transaction.exists?(key)
159
+ transaction.exists(key)
170
160
  transaction.hmset(key, "info", to_json,
171
161
  "busy", curstate.size,
172
162
  "beat", Time.now.to_f,
@@ -179,7 +169,7 @@ module Sidekiq
179
169
  }
180
170
 
181
171
  # first heartbeat or recovering from an outage and need to reestablish our heartbeat
182
- fire_event(:heartbeat) unless exists
172
+ fire_event(:heartbeat) unless exists > 0
183
173
  fire_event(:beat, oneshot: false)
184
174
 
185
175
  return unless msg
@@ -251,9 +241,9 @@ module Sidekiq
251
241
  "started_at" => Time.now.to_f,
252
242
  "pid" => ::Process.pid,
253
243
  "tag" => @config[:tag] || "",
254
- "concurrency" => @config[:concurrency],
255
- "queues" => @config[:queues].uniq,
256
- "labels" => @config[:labels],
244
+ "concurrency" => @config.total_concurrency,
245
+ "queues" => @config.capsules.values.map { |cap| cap.queues }.flatten.uniq,
246
+ "labels" => @config[:labels].to_a,
257
247
  "identity" => identity
258
248
  }
259
249
  end
@@ -31,7 +31,7 @@ module Sidekiq
31
31
  "fatal" => 4
32
32
  }
33
33
  LEVELS.default_proc = proc do |_, level|
34
- Sidekiq.logger.warn("Invalid log level: #{level.inspect}")
34
+ puts("Invalid log level: #{level.inspect}")
35
35
  nil
36
36
  end
37
37
 
@@ -70,36 +70,11 @@ module Sidekiq
70
70
  ensure
71
71
  self.local_level = old_local_level
72
72
  end
73
-
74
- # Redefined to check severity against #level, and thus the thread-local level, rather than +@level+.
75
- # FIXME: Remove when the minimum Ruby version supports overriding Logger#level.
76
- def add(severity, message = nil, progname = nil, &block)
77
- severity ||= ::Logger::UNKNOWN
78
- progname ||= @progname
79
-
80
- return true if @logdev.nil? || severity < level
81
-
82
- if message.nil?
83
- if block
84
- message = yield
85
- else
86
- message = progname
87
- progname = @progname
88
- end
89
- end
90
-
91
- @logdev.write format_message(format_severity(severity), Time.now, progname, message)
92
- end
93
73
  end
94
74
 
95
75
  class Logger < ::Logger
96
76
  include LoggingUtils
97
77
 
98
- def initialize(*args, **kwargs)
99
- super
100
- self.formatter = Sidekiq.log_formatter
101
- end
102
-
103
78
  module Formatters
104
79
  class Base < ::Logger::Formatter
105
80
  def tid
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sidekiq/processor"
4
- require "sidekiq/fetch"
5
4
  require "set"
6
5
 
7
6
  module Sidekiq
@@ -23,19 +22,19 @@ module Sidekiq
23
22
  include Sidekiq::Component
24
23
 
25
24
  attr_reader :workers
25
+ attr_reader :capsule
26
26
 
27
- def initialize(options = {})
28
- @config = options
29
- logger.debug { options.inspect }
30
- @count = options[:concurrency] || 10
27
+ def initialize(capsule)
28
+ @config = @capsule = capsule
29
+ @count = capsule.concurrency
31
30
  raise ArgumentError, "Concurrency of #{@count} is not supported" if @count < 1
32
31
 
33
32
  @done = false
34
33
  @workers = Set.new
34
+ @plock = Mutex.new
35
35
  @count.times do
36
36
  @workers << Processor.new(@config, &method(:processor_result))
37
37
  end
38
- @plock = Mutex.new
39
38
  end
40
39
 
41
40
  def start
@@ -46,14 +45,12 @@ module Sidekiq
46
45
  return if @done
47
46
  @done = true
48
47
 
49
- logger.info { "Terminating quiet threads" }
48
+ logger.info { "Terminating quiet threads for #{capsule.name} capsule" }
50
49
  @workers.each(&:terminate)
51
- fire_event(:quiet, reverse: true)
52
50
  end
53
51
 
54
52
  def stop(deadline)
55
53
  quiet
56
- fire_event(:shutdown, reverse: true)
57
54
 
58
55
  # some of the shutdown events can be async,
59
56
  # we don't have any way to know when they're done but
@@ -66,6 +63,8 @@ module Sidekiq
66
63
  return if @workers.empty?
67
64
 
68
65
  hard_shutdown
66
+ ensure
67
+ capsule.stop
69
68
  end
70
69
 
71
70
  def processor_result(processor, reason = nil)
@@ -105,8 +104,7 @@ module Sidekiq
105
104
  # contract says that jobs are run AT LEAST once. Process termination
106
105
  # is delayed until we're certain the jobs are back in Redis because
107
106
  # it is worse to lose a job than to run it twice.
108
- strategy = @config[:fetch]
109
- strategy.bulk_requeue(jobs, @config)
107
+ capsule.fetcher.bulk_requeue(jobs, nil)
110
108
  end
111
109
 
112
110
  cleanup.each do |processor|
@@ -13,9 +13,9 @@ module Sidekiq
13
13
  # NB: all metrics and times/dates are UTC only. We specifically do not
14
14
  # support timezones.
15
15
  class Query
16
- def initialize(pool: Sidekiq.redis_pool, now: Time.now)
16
+ def initialize(pool: nil, now: Time.now)
17
17
  @time = now.utc
18
- @pool = pool
18
+ @pool = pool || Sidekiq.default_configuration.redis_pool
19
19
  @klass = nil
20
20
  end
21
21
 
@@ -123,7 +123,7 @@ module Sidekiq
123
123
  def series_avg(metric = "ms")
124
124
  series[metric].each_with_object(Hash.new(0)) do |(bucket, value), result|
125
125
  completed = series.dig("p", bucket) - series.dig("f", bucket)
126
- result[bucket] = (completed == 0) ? 0 : value.to_f / completed
126
+ result[bucket] = completed == 0 ? 0 : value.to_f / completed
127
127
  end
128
128
  end
129
129
  end
@@ -2,7 +2,8 @@ require "concurrent"
2
2
 
3
3
  module Sidekiq
4
4
  module Metrics
5
- # TODO Support apps without concurrent-ruby
5
+ # This is the only dependency on concurrent-ruby in Sidekiq but it's
6
+ # mandatory for thread-safety until MRI supports atomic operations on values.
6
7
  Counter = ::Concurrent::AtomicFixnum
7
8
 
8
9
  # Implements space-efficient but statistically useful histogram storage.
@@ -38,7 +39,6 @@ module Sidekiq
38
39
  "65s", "100s", "150s", "225s", "335s",
39
40
  "Slow"
40
41
  ]
41
-
42
42
  FETCH = "GET u16 #0 GET u16 #1 GET u16 #2 GET u16 #3 \
43
43
  GET u16 #4 GET u16 #5 GET u16 #6 GET u16 #7 \
44
44
  GET u16 #8 GET u16 #9 GET u16 #10 GET u16 #11 \
@@ -46,6 +46,7 @@ module Sidekiq
46
46
  GET u16 #16 GET u16 #17 GET u16 #18 GET u16 #19 \
47
47
  GET u16 #20 GET u16 #21 GET u16 #22 GET u16 #23 \
48
48
  GET u16 #24 GET u16 #25".split
49
+ HISTOGRAM_TTL = 8 * 60 * 60
49
50
 
50
51
  def each
51
52
  buckets.each { |counter| yield counter.value }
@@ -86,7 +87,7 @@ module Sidekiq
86
87
  end
87
88
 
88
89
  conn.bitfield(*cmd) if cmd.size > 3
89
- conn.expire(key, 86400)
90
+ conn.expire(key, HISTOGRAM_TTL)
90
91
  key
91
92
  end
92
93
  end
@@ -48,8 +48,8 @@ module Sidekiq
48
48
  end
49
49
  end
50
50
 
51
- LONG_TERM = 90 * 24 * 60 * 60
52
- MID_TERM = 7 * 24 * 60 * 60
51
+ # LONG_TERM = 90 * 24 * 60 * 60
52
+ # MID_TERM = 7 * 24 * 60 * 60
53
53
  SHORT_TERM = 8 * 60 * 60
54
54
 
55
55
  def flush(time = Time.now)
@@ -59,12 +59,13 @@ module Sidekiq
59
59
  return if procd == 0 && fails == 0
60
60
 
61
61
  now = time.utc
62
- nowdate = now.strftime("%Y%m%d")
63
- nowhour = now.strftime("%Y%m%d|%-H")
62
+ # nowdate = now.strftime("%Y%m%d")
63
+ # nowhour = now.strftime("%Y%m%d|%-H")
64
64
  nowmin = now.strftime("%Y%m%d|%-H:%-M")
65
65
  count = 0
66
66
 
67
67
  redis do |conn|
68
+ # persist fine-grained histogram data
68
69
  if grams.size > 0
69
70
  conn.pipelined do |pipe|
70
71
  grams.each do |_, gram|
@@ -73,15 +74,16 @@ module Sidekiq
73
74
  end
74
75
  end
75
76
 
77
+ # persist coarse grained execution count + execution millis.
78
+ # note as of today we don't use or do anything with the
79
+ # daily or hourly rollups.
76
80
  [
77
- ["j", jobs, nowdate, LONG_TERM],
78
- ["j", jobs, nowhour, MID_TERM],
81
+ # ["j", jobs, nowdate, LONG_TERM],
82
+ # ["j", jobs, nowhour, MID_TERM],
79
83
  ["j", jobs, nowmin, SHORT_TERM]
80
84
  ].each do |prefix, data, bucket, ttl|
81
- # Quietly seed the new 7.0 stats format so migration is painless.
82
85
  conn.pipelined do |xa|
83
86
  stats = "#{prefix}|#{bucket}"
84
- # logger.debug "Flushing metrics #{stats}"
85
87
  data.each_pair do |key, value|
86
88
  xa.hincrby stats, key, value
87
89
  count += 1
@@ -89,7 +91,7 @@ module Sidekiq
89
91
  xa.expire(stats, ttl)
90
92
  end
91
93
  end
92
- logger.info "Flushed #{count} metrics"
94
+ logger.debug "Flushed #{count} metrics"
93
95
  count
94
96
  end
95
97
  end
@@ -121,14 +123,12 @@ module Sidekiq
121
123
  end
122
124
  end
123
125
 
124
- if ENV["SIDEKIQ_METRICS_BETA"] == "1"
125
- Sidekiq.configure_server do |config|
126
- exec = Sidekiq::Metrics::ExecutionTracker.new(config)
127
- config.server_middleware do |chain|
128
- chain.add Sidekiq::Metrics::Middleware, exec
129
- end
130
- config.on(:beat) do
131
- exec.flush
132
- end
126
+ Sidekiq.configure_server do |config|
127
+ exec = Sidekiq::Metrics::ExecutionTracker.new(config)
128
+ config.server_middleware do |chain|
129
+ chain.add Sidekiq::Metrics::Middleware, exec
130
+ end
131
+ config.on(:beat) do
132
+ exec.flush
133
133
  end
134
134
  end
@@ -80,15 +80,6 @@ module Sidekiq
80
80
  class Chain
81
81
  include Enumerable
82
82
 
83
- # A unique instance of the middleware chain is created for
84
- # each job executed in order to be thread-safe.
85
- # @param copy [Sidekiq::Middleware::Chain] New instance of Chain
86
- # @returns nil
87
- def initialize_copy(copy)
88
- copy.instance_variable_set(:@entries, entries.dup)
89
- nil
90
- end
91
-
92
83
  # Iterate through each middleware in the chain
93
84
  def each(&block)
94
85
  entries.each(&block)
@@ -105,6 +96,12 @@ module Sidekiq
105
96
  @entries ||= []
106
97
  end
107
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
+
108
105
  # Remove all middleware matching the given Class
109
106
  # @param klass [Class]
110
107
  def remove(klass)
@@ -152,6 +149,7 @@ module Sidekiq
152
149
  def exists?(klass)
153
150
  any? { |entry| entry.klass == klass }
154
151
  end
152
+ alias_method :include?, :exists?
155
153
 
156
154
  # @return [Boolean] if the chain contains no middleware
157
155
  def empty?
@@ -50,14 +50,9 @@ module Sidekiq
50
50
  end
51
51
  end
52
52
 
53
- def self.persist(klass)
54
- Sidekiq.configure_client do |config|
55
- config.client_middleware.add Save, klass.to_s
56
- end
57
- Sidekiq.configure_server do |config|
58
- config.client_middleware.add Save, klass.to_s
59
- config.server_middleware.add Load, klass.to_s
60
- end
53
+ def self.persist(klass, config = Sidekiq.default_configuration)
54
+ config.client_middleware.add Save, klass.to_s
55
+ config.server_middleware.add Load, klass.to_s
61
56
  end
62
57
  end
63
58
  end
@@ -101,7 +101,7 @@ class Sidekiq::Monitor
101
101
  tags = [
102
102
  process["tag"],
103
103
  process["labels"],
104
- ((process["quiet"] == "true") ? "quiet" : nil)
104
+ (process["quiet"] == "true" ? "quiet" : nil)
105
105
  ].flatten.compact
106
106
  tags.any? ? "[#{tags.join("] [")}]" : nil
107
107
  end
@@ -3,7 +3,7 @@
3
3
  module Sidekiq
4
4
  module Paginator
5
5
  def page(key, pageidx = 1, page_size = 25, opts = nil)
6
- current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
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 = []
@@ -43,13 +43,5 @@ module Sidekiq
43
43
  end
44
44
  end
45
45
  end
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
54
46
  end
55
47
  end
@@ -0,0 +1,7 @@
1
+ module Sidekiq
2
+ module PoolAccess
3
+ def redis_pool
4
+ Thread.current[:sidekiq_redis_pool] || (@redis ||= Sidekiq::RedisConnection.create)
5
+ end
6
+ end
7
+ end
@@ -26,18 +26,18 @@ module Sidekiq
26
26
 
27
27
  attr_reader :thread
28
28
  attr_reader :job
29
+ attr_reader :capsule
29
30
 
30
- def initialize(options, &block)
31
+ def initialize(capsule, &block)
32
+ @config = @capsule = capsule
31
33
  @callback = block
32
34
  @down = false
33
35
  @done = false
34
36
  @job = nil
35
37
  @thread = nil
36
- @config = options
37
- @strategy = options[:fetch]
38
- @reloader = options[:reloader] || proc { |&block| block.call }
39
- @job_logger = (options[:job_logger] || Sidekiq::JobLogger).new
40
- @retrier = Sidekiq::JobRetry.new(options)
38
+ @reloader = Sidekiq.default_configuration[:reloader]
39
+ @job_logger = (capsule.config[:job_logger] || Sidekiq::JobLogger).new(logger)
40
+ @retrier = Sidekiq::JobRetry.new(capsule)
41
41
  end
42
42
 
43
43
  def terminate(wait = false)
@@ -59,12 +59,16 @@ module Sidekiq
59
59
  end
60
60
 
61
61
  def start
62
- @thread ||= safe_thread("processor", &method(:run))
62
+ @thread ||= safe_thread("#{config.name}/processor", &method(:run))
63
63
  end
64
64
 
65
65
  private unless $TESTING
66
66
 
67
67
  def run
68
+ # By setting this thread-local, Sidekiq.redis will access +Sidekiq::Capsule#redis_pool+
69
+ # instead of the global pool in +Sidekiq::Config#redis_pool+.
70
+ Thread.current[:sidekiq_capsule] = @capsule
71
+
68
72
  process_one until @done
69
73
  @callback.call(self)
70
74
  rescue Sidekiq::Shutdown
@@ -80,7 +84,7 @@ module Sidekiq
80
84
  end
81
85
 
82
86
  def get_one
83
- uow = @strategy.retrieve_work
87
+ uow = capsule.fetcher.retrieve_work
84
88
  if @down
85
89
  logger.info { "Redis is online, #{::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - @down} sec downtime" }
86
90
  @down = nil
@@ -129,7 +133,7 @@ module Sidekiq
129
133
  # the Reloader. It handles code loading, db connection management, etc.
130
134
  # Effectively this block denotes a "unit of work" to Rails.
131
135
  @reloader.call do
132
- klass = constantize(job_hash["class"])
136
+ klass = Object.const_get(job_hash["class"])
133
137
  inst = klass.new
134
138
  inst.jid = job_hash["jid"]
135
139
  @retrier.local(inst, jobstr, queue) do
@@ -153,11 +157,11 @@ module Sidekiq
153
157
  rescue => ex
154
158
  handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
155
159
  now = Time.now.to_f
156
- config.redis do |conn|
160
+ redis do |conn|
157
161
  conn.multi do |xa|
158
162
  xa.zadd("dead", now.to_s, jobstr)
159
- xa.zremrangebyscore("dead", "-inf", now - config[:dead_timeout_in_seconds])
160
- xa.zremrangebyrank("dead", 0, - config[:dead_max_jobs])
163
+ xa.zremrangebyscore("dead", "-inf", now - @capsule.config[:dead_timeout_in_seconds])
164
+ xa.zremrangebyrank("dead", 0, - @capsule.config[:dead_max_jobs])
161
165
  end
162
166
  end
163
167
  return uow.acknowledge
@@ -166,7 +170,7 @@ module Sidekiq
166
170
  ack = false
167
171
  begin
168
172
  dispatch(job_hash, queue, jobstr) do |inst|
169
- @config.server_middleware.invoke(inst, job_hash, queue) do
173
+ config.server_middleware.invoke(inst, job_hash, queue) do
170
174
  execute_job(inst, job_hash["args"])
171
175
  end
172
176
  end
@@ -269,18 +273,5 @@ module Sidekiq
269
273
  PROCESSED.incr
270
274
  end
271
275
  end
272
-
273
- def constantize(str)
274
- return Object.const_get(str) unless str.include?("::")
275
-
276
- names = str.split("::")
277
- names.shift if names.empty? || names.first.empty?
278
-
279
- names.inject(Object) do |constant, name|
280
- # the false flag limits search for name to under the constant namespace
281
- # which mimics Rails' behaviour
282
- constant.const_get(name, false)
283
- end
284
- end
285
276
  end
286
277
  end
data/lib/sidekiq/rails.rb CHANGED
@@ -37,6 +37,17 @@ module Sidekiq
37
37
  end
38
38
  end
39
39
 
40
+ initializer "sidekiq.rails_logger" do
41
+ Sidekiq.configure_server do |config|
42
+ # This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
43
+ # it will appear in the Sidekiq console with all of the job context. See #5021 and
44
+ # https://github.com/rails/rails/blob/b5f2b550f69a99336482739000c58e4e04e033aa/railties/lib/rails/commands/server/server_command.rb#L82-L84
45
+ unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
46
+ ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
47
+ end
48
+ end
49
+ end
50
+
40
51
  config.before_configuration do
41
52
  dep = ActiveSupport::Deprecation.new("7.0", "Sidekiq")
42
53
  dep.deprecate_methods(Sidekiq.singleton_class,
@@ -51,16 +62,6 @@ module Sidekiq
51
62
  config.after_initialize do
52
63
  Sidekiq.configure_server do |config|
53
64
  config[:reloader] = Sidekiq::Rails::Reloader.new
54
-
55
- # This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
56
- # it will appear in the Sidekiq console with all of the job context.
57
- unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
58
- if ::Rails::VERSION::STRING < "7.1"
59
- ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
60
- else
61
- ::Rails.logger.broadcast_to(config.logger)
62
- end
63
- end
64
65
  end
65
66
  end
66
67
  end