sidekiq 5.0.0

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 (116) hide show
  1. checksums.yaml +7 -0
  2. data/.github/contributing.md +32 -0
  3. data/.github/issue_template.md +9 -0
  4. data/.gitignore +13 -0
  5. data/.travis.yml +18 -0
  6. data/3.0-Upgrade.md +70 -0
  7. data/4.0-Upgrade.md +53 -0
  8. data/5.0-Upgrade.md +56 -0
  9. data/COMM-LICENSE +95 -0
  10. data/Changes.md +1402 -0
  11. data/Ent-Changes.md +174 -0
  12. data/Gemfile +29 -0
  13. data/LICENSE +9 -0
  14. data/Pro-2.0-Upgrade.md +138 -0
  15. data/Pro-3.0-Upgrade.md +44 -0
  16. data/Pro-Changes.md +632 -0
  17. data/README.md +107 -0
  18. data/Rakefile +12 -0
  19. data/bin/sidekiq +18 -0
  20. data/bin/sidekiqctl +99 -0
  21. data/bin/sidekiqload +149 -0
  22. data/code_of_conduct.md +50 -0
  23. data/lib/generators/sidekiq/templates/worker.rb.erb +9 -0
  24. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +6 -0
  25. data/lib/generators/sidekiq/templates/worker_test.rb.erb +8 -0
  26. data/lib/generators/sidekiq/worker_generator.rb +49 -0
  27. data/lib/sidekiq.rb +228 -0
  28. data/lib/sidekiq/api.rb +871 -0
  29. data/lib/sidekiq/cli.rb +413 -0
  30. data/lib/sidekiq/client.rb +238 -0
  31. data/lib/sidekiq/core_ext.rb +119 -0
  32. data/lib/sidekiq/delay.rb +21 -0
  33. data/lib/sidekiq/exception_handler.rb +31 -0
  34. data/lib/sidekiq/extensions/action_mailer.rb +57 -0
  35. data/lib/sidekiq/extensions/active_record.rb +40 -0
  36. data/lib/sidekiq/extensions/class_methods.rb +40 -0
  37. data/lib/sidekiq/extensions/generic_proxy.rb +31 -0
  38. data/lib/sidekiq/fetch.rb +81 -0
  39. data/lib/sidekiq/job_logger.rb +27 -0
  40. data/lib/sidekiq/job_retry.rb +235 -0
  41. data/lib/sidekiq/launcher.rb +167 -0
  42. data/lib/sidekiq/logging.rb +106 -0
  43. data/lib/sidekiq/manager.rb +138 -0
  44. data/lib/sidekiq/middleware/chain.rb +150 -0
  45. data/lib/sidekiq/middleware/i18n.rb +42 -0
  46. data/lib/sidekiq/middleware/server/active_record.rb +22 -0
  47. data/lib/sidekiq/paginator.rb +43 -0
  48. data/lib/sidekiq/processor.rb +238 -0
  49. data/lib/sidekiq/rails.rb +60 -0
  50. data/lib/sidekiq/redis_connection.rb +106 -0
  51. data/lib/sidekiq/scheduled.rb +147 -0
  52. data/lib/sidekiq/testing.rb +324 -0
  53. data/lib/sidekiq/testing/inline.rb +29 -0
  54. data/lib/sidekiq/util.rb +63 -0
  55. data/lib/sidekiq/version.rb +4 -0
  56. data/lib/sidekiq/web.rb +213 -0
  57. data/lib/sidekiq/web/action.rb +89 -0
  58. data/lib/sidekiq/web/application.rb +331 -0
  59. data/lib/sidekiq/web/helpers.rb +286 -0
  60. data/lib/sidekiq/web/router.rb +100 -0
  61. data/lib/sidekiq/worker.rb +144 -0
  62. data/sidekiq.gemspec +32 -0
  63. data/web/assets/images/favicon.ico +0 -0
  64. data/web/assets/images/logo.png +0 -0
  65. data/web/assets/images/status.png +0 -0
  66. data/web/assets/javascripts/application.js +92 -0
  67. data/web/assets/javascripts/dashboard.js +298 -0
  68. data/web/assets/stylesheets/application-rtl.css +246 -0
  69. data/web/assets/stylesheets/application.css +1111 -0
  70. data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  71. data/web/assets/stylesheets/bootstrap.css +5 -0
  72. data/web/locales/ar.yml +80 -0
  73. data/web/locales/cs.yml +78 -0
  74. data/web/locales/da.yml +68 -0
  75. data/web/locales/de.yml +69 -0
  76. data/web/locales/el.yml +68 -0
  77. data/web/locales/en.yml +79 -0
  78. data/web/locales/es.yml +69 -0
  79. data/web/locales/fa.yml +80 -0
  80. data/web/locales/fr.yml +78 -0
  81. data/web/locales/he.yml +79 -0
  82. data/web/locales/hi.yml +75 -0
  83. data/web/locales/it.yml +69 -0
  84. data/web/locales/ja.yml +78 -0
  85. data/web/locales/ko.yml +68 -0
  86. data/web/locales/nb.yml +77 -0
  87. data/web/locales/nl.yml +68 -0
  88. data/web/locales/pl.yml +59 -0
  89. data/web/locales/pt-br.yml +68 -0
  90. data/web/locales/pt.yml +67 -0
  91. data/web/locales/ru.yml +78 -0
  92. data/web/locales/sv.yml +68 -0
  93. data/web/locales/ta.yml +75 -0
  94. data/web/locales/uk.yml +76 -0
  95. data/web/locales/ur.yml +80 -0
  96. data/web/locales/zh-cn.yml +68 -0
  97. data/web/locales/zh-tw.yml +68 -0
  98. data/web/views/_footer.erb +17 -0
  99. data/web/views/_job_info.erb +88 -0
  100. data/web/views/_nav.erb +66 -0
  101. data/web/views/_paging.erb +23 -0
  102. data/web/views/_poll_link.erb +7 -0
  103. data/web/views/_status.erb +4 -0
  104. data/web/views/_summary.erb +40 -0
  105. data/web/views/busy.erb +94 -0
  106. data/web/views/dashboard.erb +75 -0
  107. data/web/views/dead.erb +34 -0
  108. data/web/views/layout.erb +40 -0
  109. data/web/views/morgue.erb +75 -0
  110. data/web/views/queue.erb +45 -0
  111. data/web/views/queues.erb +28 -0
  112. data/web/views/retries.erb +76 -0
  113. data/web/views/retry.erb +34 -0
  114. data/web/views/scheduled.erb +54 -0
  115. data/web/views/scheduled_job_info.erb +8 -0
  116. metadata +366 -0
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+ begin
3
+ require 'active_support/core_ext/class/attribute'
4
+ rescue LoadError
5
+
6
+ # A dumbed down version of ActiveSupport's
7
+ # Class#class_attribute helper.
8
+ class Class
9
+ def class_attribute(*attrs)
10
+ instance_writer = true
11
+
12
+ attrs.each do |name|
13
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
14
+ def self.#{name}() nil end
15
+ def self.#{name}?() !!#{name} end
16
+
17
+ def self.#{name}=(val)
18
+ singleton_class.class_eval do
19
+ define_method(:#{name}) { val }
20
+ end
21
+
22
+ if singleton_class?
23
+ class_eval do
24
+ def #{name}
25
+ defined?(@#{name}) ? @#{name} : singleton_class.#{name}
26
+ end
27
+ end
28
+ end
29
+ val
30
+ end
31
+
32
+ def #{name}
33
+ defined?(@#{name}) ? @#{name} : self.class.#{name}
34
+ end
35
+
36
+ def #{name}?
37
+ !!#{name}
38
+ end
39
+ RUBY
40
+
41
+ attr_writer name if instance_writer
42
+ end
43
+ end
44
+
45
+ private
46
+ def singleton_class?
47
+ ancestors.first != self
48
+ end
49
+ end
50
+ end
51
+
52
+ begin
53
+ require 'active_support/core_ext/hash/keys'
54
+ require 'active_support/core_ext/hash/deep_merge'
55
+ rescue LoadError
56
+ class Hash
57
+ def stringify_keys
58
+ keys.each do |key|
59
+ self[key.to_s] = delete(key)
60
+ end
61
+ self
62
+ end if !{}.respond_to?(:stringify_keys)
63
+
64
+ def symbolize_keys
65
+ keys.each do |key|
66
+ self[(key.to_sym rescue key) || key] = delete(key)
67
+ end
68
+ self
69
+ end if !{}.respond_to?(:symbolize_keys)
70
+
71
+ def deep_merge(other_hash, &block)
72
+ dup.deep_merge!(other_hash, &block)
73
+ end if !{}.respond_to?(:deep_merge)
74
+
75
+ def deep_merge!(other_hash, &block)
76
+ other_hash.each_pair do |k,v|
77
+ tv = self[k]
78
+ if tv.is_a?(Hash) && v.is_a?(Hash)
79
+ self[k] = tv.deep_merge(v, &block)
80
+ else
81
+ self[k] = block && tv ? block.call(k, tv, v) : v
82
+ end
83
+ end
84
+ self
85
+ end if !{}.respond_to?(:deep_merge!)
86
+ end
87
+ end
88
+
89
+ begin
90
+ require 'active_support/core_ext/string/inflections'
91
+ rescue LoadError
92
+ class String
93
+ def constantize
94
+ names = self.split('::')
95
+ names.shift if names.empty? || names.first.empty?
96
+
97
+ constant = Object
98
+ names.each do |name|
99
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
100
+ end
101
+ constant
102
+ end
103
+ end if !"".respond_to?(:constantize)
104
+ end
105
+
106
+
107
+ begin
108
+ require 'active_support/core_ext/kernel/reporting'
109
+ rescue LoadError
110
+ module Kernel
111
+ module_function
112
+ def silence_warnings
113
+ old_verbose, $VERBOSE = $VERBOSE, nil
114
+ yield
115
+ ensure
116
+ $VERBOSE = old_verbose
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,21 @@
1
+ module Sidekiq
2
+ module Extensions
3
+
4
+ def self.enable_delay!
5
+ if defined?(::ActiveSupport)
6
+ ActiveSupport.on_load(:active_record) do
7
+ require 'sidekiq/extensions/active_record'
8
+ include Sidekiq::Extensions::ActiveRecord
9
+ end
10
+ ActiveSupport.on_load(:action_mailer) do
11
+ require 'sidekiq/extensions/action_mailer'
12
+ extend Sidekiq::Extensions::ActionMailer
13
+ end
14
+ end
15
+
16
+ require 'sidekiq/extensions/class_methods'
17
+ Module.__send__(:include, Sidekiq::Extensions::Klass)
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ require 'sidekiq'
3
+
4
+ module Sidekiq
5
+ module ExceptionHandler
6
+
7
+ class Logger
8
+ def call(ex, ctxHash)
9
+ Sidekiq.logger.warn(Sidekiq.dump_json(ctxHash)) if !ctxHash.empty?
10
+ Sidekiq.logger.warn "#{ex.class.name}: #{ex.message}"
11
+ Sidekiq.logger.warn ex.backtrace.join("\n") unless ex.backtrace.nil?
12
+ end
13
+
14
+ # Set up default handler which just logs the error
15
+ Sidekiq.error_handlers << Sidekiq::ExceptionHandler::Logger.new
16
+ end
17
+
18
+ def handle_exception(ex, ctxHash={})
19
+ Sidekiq.error_handlers.each do |handler|
20
+ begin
21
+ handler.call(ex, ctxHash)
22
+ rescue => ex
23
+ Sidekiq.logger.error "!!! ERROR HANDLER THREW AN ERROR !!!"
24
+ Sidekiq.logger.error ex
25
+ Sidekiq.logger.error ex.backtrace.join("\n") unless ex.backtrace.nil?
26
+ end
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+ require 'sidekiq/extensions/generic_proxy'
3
+
4
+ module Sidekiq
5
+ module Extensions
6
+ ##
7
+ # Adds 'delay', 'delay_for' and `delay_until` methods to ActionMailer to offload arbitrary email
8
+ # delivery to Sidekiq. Example:
9
+ #
10
+ # UserMailer.delay.send_welcome_email(new_user)
11
+ # UserMailer.delay_for(5.days).send_welcome_email(new_user)
12
+ # UserMailer.delay_until(5.days.from_now).send_welcome_email(new_user)
13
+ class DelayedMailer
14
+ include Sidekiq::Worker
15
+
16
+ def perform(yml)
17
+ (target, method_name, args) = YAML.load(yml)
18
+ msg = target.public_send(method_name, *args)
19
+ # The email method can return nil, which causes ActionMailer to return
20
+ # an undeliverable empty message.
21
+ if msg
22
+ deliver(msg)
23
+ else
24
+ raise "#{target.name}##{method_name} returned an undeliverable mail object"
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def deliver(msg)
31
+ if msg.respond_to?(:deliver_now)
32
+ # Rails 4.2/5.0
33
+ msg.deliver_now
34
+ else
35
+ # Rails 3.2/4.0/4.1
36
+ msg.deliver
37
+ end
38
+ end
39
+ end
40
+
41
+ module ActionMailer
42
+ def sidekiq_delay(options={})
43
+ Proxy.new(DelayedMailer, self, options)
44
+ end
45
+ def sidekiq_delay_for(interval, options={})
46
+ Proxy.new(DelayedMailer, self, options.merge('at' => Time.now.to_f + interval.to_f))
47
+ end
48
+ def sidekiq_delay_until(timestamp, options={})
49
+ Proxy.new(DelayedMailer, self, options.merge('at' => timestamp.to_f))
50
+ end
51
+ alias_method :delay, :sidekiq_delay
52
+ alias_method :delay_for, :sidekiq_delay_for
53
+ alias_method :delay_until, :sidekiq_delay_until
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ require 'sidekiq/extensions/generic_proxy'
3
+
4
+ module Sidekiq
5
+ module Extensions
6
+ ##
7
+ # Adds 'delay', 'delay_for' and `delay_until` methods to ActiveRecord to offload instance method
8
+ # execution to Sidekiq. Examples:
9
+ #
10
+ # User.recent_signups.each { |user| user.delay.mark_as_awesome }
11
+ #
12
+ # Please note, this is not recommended as this will serialize the entire
13
+ # object to Redis. Your Sidekiq jobs should pass IDs, not entire instances.
14
+ # This is here for backwards compatibility with Delayed::Job only.
15
+ class DelayedModel
16
+ include Sidekiq::Worker
17
+
18
+ def perform(yml)
19
+ (target, method_name, args) = YAML.load(yml)
20
+ target.__send__(method_name, *args)
21
+ end
22
+ end
23
+
24
+ module ActiveRecord
25
+ def sidekiq_delay(options={})
26
+ Proxy.new(DelayedModel, self, options)
27
+ end
28
+ def sidekiq_delay_for(interval, options={})
29
+ Proxy.new(DelayedModel, self, options.merge('at' => Time.now.to_f + interval.to_f))
30
+ end
31
+ def sidekiq_delay_until(timestamp, options={})
32
+ Proxy.new(DelayedModel, self, options.merge('at' => timestamp.to_f))
33
+ end
34
+ alias_method :delay, :sidekiq_delay
35
+ alias_method :delay_for, :sidekiq_delay_for
36
+ alias_method :delay_until, :sidekiq_delay_until
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ require 'sidekiq/extensions/generic_proxy'
3
+
4
+ module Sidekiq
5
+ module Extensions
6
+ ##
7
+ # Adds 'delay', 'delay_for' and `delay_until` methods to all Classes to offload class method
8
+ # execution to Sidekiq. Examples:
9
+ #
10
+ # User.delay.delete_inactive
11
+ # Wikipedia.delay.download_changes_for(Date.today)
12
+ #
13
+ class DelayedClass
14
+ include Sidekiq::Worker
15
+
16
+ def perform(yml)
17
+ (target, method_name, args) = YAML.load(yml)
18
+ target.__send__(method_name, *args)
19
+ end
20
+ end
21
+
22
+ module Klass
23
+ def sidekiq_delay(options={})
24
+ Proxy.new(DelayedClass, self, options)
25
+ end
26
+ def sidekiq_delay_for(interval, options={})
27
+ Proxy.new(DelayedClass, self, options.merge('at' => Time.now.to_f + interval.to_f))
28
+ end
29
+ def sidekiq_delay_until(timestamp, options={})
30
+ Proxy.new(DelayedClass, self, options.merge('at' => timestamp.to_f))
31
+ end
32
+ alias_method :delay, :sidekiq_delay
33
+ alias_method :delay_for, :sidekiq_delay_for
34
+ alias_method :delay_until, :sidekiq_delay_until
35
+ end
36
+
37
+ end
38
+ end
39
+
40
+ Module.__send__(:include, Sidekiq::Extensions::Klass) unless defined?(::Rails)
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ require 'yaml'
3
+
4
+ module Sidekiq
5
+ module Extensions
6
+ SIZE_LIMIT = 8_192
7
+
8
+ class Proxy < BasicObject
9
+ def initialize(performable, target, options={})
10
+ @performable = performable
11
+ @target = target
12
+ @opts = options
13
+ end
14
+
15
+ def method_missing(name, *args)
16
+ # Sidekiq has a limitation in that its message must be JSON.
17
+ # JSON can't round trip real Ruby objects so we use YAML to
18
+ # serialize the objects to a String. The YAML will be converted
19
+ # to JSON and then deserialized on the other side back into a
20
+ # Ruby object.
21
+ obj = [@target, name, args]
22
+ marshalled = ::YAML.dump(obj)
23
+ if marshalled.size > SIZE_LIMIT
24
+ ::Sidekiq.logger.warn { "#{@target}.#{name} job argument is #{marshalled.bytesize} bytes, you should refactor it to reduce the size" }
25
+ end
26
+ @performable.client_push({ 'class' => @performable, 'args' => [marshalled] }.merge(@opts))
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+ require 'sidekiq'
3
+
4
+ module Sidekiq
5
+ class BasicFetch
6
+ # We want the fetch operation to timeout every few seconds so the thread
7
+ # can check if the process is shutting down.
8
+ TIMEOUT = 2
9
+
10
+ UnitOfWork = Struct.new(:queue, :job) do
11
+ def acknowledge
12
+ # nothing to do
13
+ end
14
+
15
+ def queue_name
16
+ queue.sub(/.*queue:/, ''.freeze)
17
+ end
18
+
19
+ def requeue
20
+ Sidekiq.redis do |conn|
21
+ conn.rpush("queue:#{queue_name}", job)
22
+ end
23
+ end
24
+ end
25
+
26
+ def initialize(options)
27
+ @strictly_ordered_queues = !!options[:strict]
28
+ @queues = options[:queues].map { |q| "queue:#{q}" }
29
+ if @strictly_ordered_queues
30
+ @queues = @queues.uniq
31
+ @queues << TIMEOUT
32
+ end
33
+ end
34
+
35
+ def retrieve_work
36
+ work = Sidekiq.redis { |conn| conn.brpop(*queues_cmd) }
37
+ UnitOfWork.new(*work) if work
38
+ end
39
+
40
+ # Creating the Redis#brpop command takes into account any
41
+ # configured queue weights. By default Redis#brpop returns
42
+ # data from the first queue that has pending elements. We
43
+ # recreate the queue command each time we invoke Redis#brpop
44
+ # to honor weights and avoid queue starvation.
45
+ def queues_cmd
46
+ if @strictly_ordered_queues
47
+ @queues
48
+ else
49
+ queues = @queues.shuffle.uniq
50
+ queues << TIMEOUT
51
+ queues
52
+ end
53
+ end
54
+
55
+
56
+ # By leaving this as a class method, it can be pluggable and used by the Manager actor. Making it
57
+ # an instance method will make it async to the Fetcher actor
58
+ def self.bulk_requeue(inprogress, options)
59
+ return if inprogress.empty?
60
+
61
+ Sidekiq.logger.debug { "Re-queueing terminated jobs" }
62
+ jobs_to_requeue = {}
63
+ inprogress.each do |unit_of_work|
64
+ jobs_to_requeue[unit_of_work.queue_name] ||= []
65
+ jobs_to_requeue[unit_of_work.queue_name] << unit_of_work.job
66
+ end
67
+
68
+ Sidekiq.redis do |conn|
69
+ conn.pipelined do
70
+ jobs_to_requeue.each do |queue, jobs|
71
+ conn.rpush("queue:#{queue}", jobs)
72
+ end
73
+ end
74
+ end
75
+ Sidekiq.logger.info("Pushed #{inprogress.size} jobs back to Redis")
76
+ rescue => ex
77
+ Sidekiq.logger.warn("Failed to requeue #{inprogress.size} jobs: #{ex.message}")
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,27 @@
1
+ module Sidekiq
2
+ class JobLogger
3
+
4
+ def call(item, queue)
5
+ begin
6
+ start = Time.now
7
+ logger.info("start".freeze)
8
+ yield
9
+ logger.info("done: #{elapsed(start)} sec")
10
+ rescue Exception
11
+ logger.info("fail: #{elapsed(start)} sec")
12
+ raise
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def elapsed(start)
19
+ (Time.now - start).round(3)
20
+ end
21
+
22
+ def logger
23
+ Sidekiq.logger
24
+ end
25
+ end
26
+ end
27
+