sidekiq 3.5.4 → 7.2.0

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 (228) hide show
  1. checksums.yaml +5 -5
  2. data/Changes.md +992 -6
  3. data/LICENSE.txt +9 -0
  4. data/README.md +52 -43
  5. data/bin/sidekiq +22 -4
  6. data/bin/sidekiqload +209 -115
  7. data/bin/sidekiqmon +11 -0
  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/job_spec.rb.erb +6 -0
  11. data/lib/generators/sidekiq/templates/job_test.rb.erb +8 -0
  12. data/lib/sidekiq/api.rb +633 -295
  13. data/lib/sidekiq/capsule.rb +127 -0
  14. data/lib/sidekiq/cli.rb +270 -248
  15. data/lib/sidekiq/client.rb +139 -108
  16. data/lib/sidekiq/component.rb +68 -0
  17. data/lib/sidekiq/config.rb +287 -0
  18. data/lib/sidekiq/deploy.rb +62 -0
  19. data/lib/sidekiq/embedded.rb +61 -0
  20. data/lib/sidekiq/fetch.rb +53 -121
  21. data/lib/sidekiq/job.rb +374 -0
  22. data/lib/sidekiq/job_logger.rb +51 -0
  23. data/lib/sidekiq/job_retry.rb +301 -0
  24. data/lib/sidekiq/job_util.rb +107 -0
  25. data/lib/sidekiq/launcher.rb +241 -69
  26. data/lib/sidekiq/logger.rb +131 -0
  27. data/lib/sidekiq/manager.rb +88 -190
  28. data/lib/sidekiq/metrics/query.rb +155 -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 +114 -56
  32. data/lib/sidekiq/middleware/current_attributes.rb +95 -0
  33. data/lib/sidekiq/middleware/i18n.rb +8 -7
  34. data/lib/sidekiq/middleware/modules.rb +21 -0
  35. data/lib/sidekiq/monitor.rb +146 -0
  36. data/lib/sidekiq/paginator.rb +29 -16
  37. data/lib/sidekiq/processor.rb +238 -118
  38. data/lib/sidekiq/rails.rb +57 -27
  39. data/lib/sidekiq/redis_client_adapter.rb +111 -0
  40. data/lib/sidekiq/redis_connection.rb +49 -50
  41. data/lib/sidekiq/ring_buffer.rb +29 -0
  42. data/lib/sidekiq/scheduled.rb +173 -52
  43. data/lib/sidekiq/sd_notify.rb +149 -0
  44. data/lib/sidekiq/systemd.rb +24 -0
  45. data/lib/sidekiq/testing/inline.rb +7 -5
  46. data/lib/sidekiq/testing.rb +197 -65
  47. data/lib/sidekiq/transaction_aware_client.rb +44 -0
  48. data/lib/sidekiq/version.rb +4 -1
  49. data/lib/sidekiq/web/action.rb +93 -0
  50. data/lib/sidekiq/web/application.rb +463 -0
  51. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  52. data/lib/sidekiq/web/helpers.rb +364 -0
  53. data/lib/sidekiq/web/router.rb +104 -0
  54. data/lib/sidekiq/web.rb +113 -216
  55. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  56. data/lib/sidekiq.rb +99 -142
  57. data/sidekiq.gemspec +26 -23
  58. data/web/assets/images/apple-touch-icon.png +0 -0
  59. data/web/assets/javascripts/application.js +163 -74
  60. data/web/assets/javascripts/base-charts.js +106 -0
  61. data/web/assets/javascripts/chart.min.js +13 -0
  62. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  63. data/web/assets/javascripts/dashboard-charts.js +182 -0
  64. data/web/assets/javascripts/dashboard.js +37 -280
  65. data/web/assets/javascripts/metrics.js +298 -0
  66. data/web/assets/stylesheets/application-dark.css +147 -0
  67. data/web/assets/stylesheets/application-rtl.css +153 -0
  68. data/web/assets/stylesheets/application.css +181 -198
  69. data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  70. data/web/assets/stylesheets/bootstrap.css +4 -8
  71. data/web/locales/ar.yml +87 -0
  72. data/web/locales/cs.yml +62 -52
  73. data/web/locales/da.yml +60 -53
  74. data/web/locales/de.yml +65 -53
  75. data/web/locales/el.yml +43 -24
  76. data/web/locales/en.yml +86 -62
  77. data/web/locales/es.yml +70 -53
  78. data/web/locales/fa.yml +80 -0
  79. data/web/locales/fr.yml +86 -56
  80. data/web/locales/gd.yml +99 -0
  81. data/web/locales/he.yml +80 -0
  82. data/web/locales/hi.yml +59 -59
  83. data/web/locales/it.yml +53 -53
  84. data/web/locales/ja.yml +78 -56
  85. data/web/locales/ko.yml +52 -52
  86. data/web/locales/lt.yml +83 -0
  87. data/web/locales/nb.yml +61 -61
  88. data/web/locales/nl.yml +52 -52
  89. data/web/locales/pl.yml +45 -45
  90. data/web/locales/pt-br.yml +83 -55
  91. data/web/locales/pt.yml +51 -51
  92. data/web/locales/ru.yml +68 -60
  93. data/web/locales/sv.yml +53 -53
  94. data/web/locales/ta.yml +60 -60
  95. data/web/locales/uk.yml +62 -61
  96. data/web/locales/ur.yml +80 -0
  97. data/web/locales/vi.yml +83 -0
  98. data/web/locales/zh-cn.yml +43 -16
  99. data/web/locales/zh-tw.yml +42 -8
  100. data/web/views/_footer.erb +10 -9
  101. data/web/views/_job_info.erb +26 -5
  102. data/web/views/_metrics_period_select.erb +12 -0
  103. data/web/views/_nav.erb +6 -20
  104. data/web/views/_paging.erb +3 -1
  105. data/web/views/_poll_link.erb +3 -6
  106. data/web/views/_summary.erb +7 -7
  107. data/web/views/busy.erb +87 -28
  108. data/web/views/dashboard.erb +51 -21
  109. data/web/views/dead.erb +4 -4
  110. data/web/views/filtering.erb +7 -0
  111. data/web/views/layout.erb +15 -5
  112. data/web/views/metrics.erb +91 -0
  113. data/web/views/metrics_for_job.erb +59 -0
  114. data/web/views/morgue.erb +25 -22
  115. data/web/views/queue.erb +35 -25
  116. data/web/views/queues.erb +23 -7
  117. data/web/views/retries.erb +28 -23
  118. data/web/views/retry.erb +5 -5
  119. data/web/views/scheduled.erb +19 -17
  120. data/web/views/scheduled_job_info.erb +1 -1
  121. metadata +86 -268
  122. data/.gitignore +0 -12
  123. data/.travis.yml +0 -16
  124. data/3.0-Upgrade.md +0 -70
  125. data/COMM-LICENSE +0 -95
  126. data/Contributing.md +0 -32
  127. data/Ent-Changes.md +0 -39
  128. data/Gemfile +0 -27
  129. data/LICENSE +0 -9
  130. data/Pro-2.0-Upgrade.md +0 -138
  131. data/Pro-Changes.md +0 -454
  132. data/Rakefile +0 -9
  133. data/bin/sidekiqctl +0 -93
  134. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +0 -6
  135. data/lib/generators/sidekiq/templates/worker_test.rb.erb +0 -8
  136. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  137. data/lib/sidekiq/actor.rb +0 -39
  138. data/lib/sidekiq/core_ext.rb +0 -105
  139. data/lib/sidekiq/exception_handler.rb +0 -30
  140. data/lib/sidekiq/extensions/action_mailer.rb +0 -56
  141. data/lib/sidekiq/extensions/active_record.rb +0 -39
  142. data/lib/sidekiq/extensions/class_methods.rb +0 -39
  143. data/lib/sidekiq/extensions/generic_proxy.rb +0 -24
  144. data/lib/sidekiq/logging.rb +0 -104
  145. data/lib/sidekiq/middleware/server/active_record.rb +0 -13
  146. data/lib/sidekiq/middleware/server/logging.rb +0 -40
  147. data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -206
  148. data/lib/sidekiq/util.rb +0 -68
  149. data/lib/sidekiq/web_helpers.rb +0 -249
  150. data/lib/sidekiq/worker.rb +0 -103
  151. data/test/config.yml +0 -9
  152. data/test/env_based_config.yml +0 -11
  153. data/test/fake_env.rb +0 -0
  154. data/test/fixtures/en.yml +0 -2
  155. data/test/helper.rb +0 -49
  156. data/test/test_api.rb +0 -493
  157. data/test/test_cli.rb +0 -335
  158. data/test/test_client.rb +0 -194
  159. data/test/test_exception_handler.rb +0 -55
  160. data/test/test_extensions.rb +0 -126
  161. data/test/test_fetch.rb +0 -104
  162. data/test/test_logging.rb +0 -34
  163. data/test/test_manager.rb +0 -168
  164. data/test/test_middleware.rb +0 -159
  165. data/test/test_processor.rb +0 -237
  166. data/test/test_rails.rb +0 -21
  167. data/test/test_redis_connection.rb +0 -126
  168. data/test/test_retry.rb +0 -325
  169. data/test/test_scheduled.rb +0 -114
  170. data/test/test_scheduling.rb +0 -49
  171. data/test/test_sidekiq.rb +0 -99
  172. data/test/test_testing.rb +0 -142
  173. data/test/test_testing_fake.rb +0 -268
  174. data/test/test_testing_inline.rb +0 -93
  175. data/test/test_util.rb +0 -16
  176. data/test/test_web.rb +0 -608
  177. data/test/test_web_helpers.rb +0 -53
  178. data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
  179. data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
  180. data/web/assets/images/status/active.png +0 -0
  181. data/web/assets/images/status/idle.png +0 -0
  182. data/web/assets/javascripts/locales/README.md +0 -27
  183. data/web/assets/javascripts/locales/jquery.timeago.ar.js +0 -96
  184. data/web/assets/javascripts/locales/jquery.timeago.bg.js +0 -18
  185. data/web/assets/javascripts/locales/jquery.timeago.bs.js +0 -49
  186. data/web/assets/javascripts/locales/jquery.timeago.ca.js +0 -18
  187. data/web/assets/javascripts/locales/jquery.timeago.cs.js +0 -18
  188. data/web/assets/javascripts/locales/jquery.timeago.cy.js +0 -20
  189. data/web/assets/javascripts/locales/jquery.timeago.da.js +0 -18
  190. data/web/assets/javascripts/locales/jquery.timeago.de.js +0 -18
  191. data/web/assets/javascripts/locales/jquery.timeago.el.js +0 -18
  192. data/web/assets/javascripts/locales/jquery.timeago.en-short.js +0 -20
  193. data/web/assets/javascripts/locales/jquery.timeago.en.js +0 -20
  194. data/web/assets/javascripts/locales/jquery.timeago.es.js +0 -18
  195. data/web/assets/javascripts/locales/jquery.timeago.et.js +0 -18
  196. data/web/assets/javascripts/locales/jquery.timeago.fa.js +0 -22
  197. data/web/assets/javascripts/locales/jquery.timeago.fi.js +0 -28
  198. data/web/assets/javascripts/locales/jquery.timeago.fr-short.js +0 -16
  199. data/web/assets/javascripts/locales/jquery.timeago.fr.js +0 -17
  200. data/web/assets/javascripts/locales/jquery.timeago.he.js +0 -18
  201. data/web/assets/javascripts/locales/jquery.timeago.hr.js +0 -49
  202. data/web/assets/javascripts/locales/jquery.timeago.hu.js +0 -18
  203. data/web/assets/javascripts/locales/jquery.timeago.hy.js +0 -18
  204. data/web/assets/javascripts/locales/jquery.timeago.id.js +0 -18
  205. data/web/assets/javascripts/locales/jquery.timeago.it.js +0 -16
  206. data/web/assets/javascripts/locales/jquery.timeago.ja.js +0 -19
  207. data/web/assets/javascripts/locales/jquery.timeago.ko.js +0 -17
  208. data/web/assets/javascripts/locales/jquery.timeago.lt.js +0 -20
  209. data/web/assets/javascripts/locales/jquery.timeago.mk.js +0 -20
  210. data/web/assets/javascripts/locales/jquery.timeago.nl.js +0 -20
  211. data/web/assets/javascripts/locales/jquery.timeago.no.js +0 -18
  212. data/web/assets/javascripts/locales/jquery.timeago.pl.js +0 -31
  213. data/web/assets/javascripts/locales/jquery.timeago.pt-br.js +0 -16
  214. data/web/assets/javascripts/locales/jquery.timeago.pt.js +0 -16
  215. data/web/assets/javascripts/locales/jquery.timeago.ro.js +0 -18
  216. data/web/assets/javascripts/locales/jquery.timeago.rs.js +0 -49
  217. data/web/assets/javascripts/locales/jquery.timeago.ru.js +0 -34
  218. data/web/assets/javascripts/locales/jquery.timeago.sk.js +0 -18
  219. data/web/assets/javascripts/locales/jquery.timeago.sl.js +0 -44
  220. data/web/assets/javascripts/locales/jquery.timeago.sv.js +0 -18
  221. data/web/assets/javascripts/locales/jquery.timeago.th.js +0 -20
  222. data/web/assets/javascripts/locales/jquery.timeago.tr.js +0 -16
  223. data/web/assets/javascripts/locales/jquery.timeago.uk.js +0 -34
  224. data/web/assets/javascripts/locales/jquery.timeago.uz.js +0 -19
  225. data/web/assets/javascripts/locales/jquery.timeago.zh-cn.js +0 -20
  226. data/web/assets/javascripts/locales/jquery.timeago.zh-tw.js +0 -20
  227. data/web/views/_poll_js.erb +0 -5
  228. /data/web/assets/images/{status-sd8051fd480.png → status.png} +0 -0
@@ -1,105 +0,0 @@
1
- begin
2
- require 'active_support/core_ext/class/attribute'
3
- rescue LoadError
4
-
5
- # A dumbed down version of ActiveSupport's
6
- # Class#class_attribute helper.
7
- class Class
8
- def class_attribute(*attrs)
9
- instance_writer = true
10
-
11
- attrs.each do |name|
12
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
13
- def self.#{name}() nil end
14
- def self.#{name}?() !!#{name} end
15
-
16
- def self.#{name}=(val)
17
- singleton_class.class_eval do
18
- define_method(:#{name}) { val }
19
- end
20
-
21
- if singleton_class?
22
- class_eval do
23
- def #{name}
24
- defined?(@#{name}) ? @#{name} : singleton_class.#{name}
25
- end
26
- end
27
- end
28
- val
29
- end
30
-
31
- def #{name}
32
- defined?(@#{name}) ? @#{name} : self.class.#{name}
33
- end
34
-
35
- def #{name}?
36
- !!#{name}
37
- end
38
- RUBY
39
-
40
- attr_writer name if instance_writer
41
- end
42
- end
43
-
44
- private
45
- def singleton_class?
46
- ancestors.first != self
47
- end
48
- end
49
- end
50
-
51
- begin
52
- require 'active_support/core_ext/hash/keys'
53
- require 'active_support/core_ext/hash/deep_merge'
54
- rescue LoadError
55
- class Hash
56
- def stringify_keys
57
- keys.each do |key|
58
- self[key.to_s] = delete(key)
59
- end
60
- self
61
- end if !{}.respond_to?(:stringify_keys)
62
-
63
- def symbolize_keys
64
- keys.each do |key|
65
- self[(key.to_sym rescue key) || key] = delete(key)
66
- end
67
- self
68
- end if !{}.respond_to?(:symbolize_keys)
69
-
70
- def deep_merge(other_hash, &block)
71
- dup.deep_merge!(other_hash, &block)
72
- end if !{}.respond_to?(:deep_merge)
73
-
74
- def deep_merge!(other_hash, &block)
75
- other_hash.each_pair do |k,v|
76
- tv = self[k]
77
- if tv.is_a?(Hash) && v.is_a?(Hash)
78
- self[k] = tv.deep_merge(v, &block)
79
- else
80
- self[k] = block && tv ? block.call(k, tv, v) : v
81
- end
82
- end
83
- self
84
- end if !{}.respond_to?(:deep_merge!)
85
- end
86
- end
87
-
88
- begin
89
- require 'active_support/core_ext/string/inflections'
90
- rescue LoadError
91
- class String
92
- def constantize
93
- names = self.split('::')
94
- names.shift if names.empty? || names.first.empty?
95
-
96
- constant = Object
97
- names.each do |name|
98
- constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
99
- end
100
- constant
101
- end
102
- end if !"".respond_to?(:constantize)
103
- end
104
-
105
-
@@ -1,30 +0,0 @@
1
- require 'sidekiq'
2
-
3
- module Sidekiq
4
- module ExceptionHandler
5
-
6
- class Logger
7
- def call(ex, ctxHash)
8
- Sidekiq.logger.warn(ctxHash) if !ctxHash.empty?
9
- Sidekiq.logger.warn "#{ex.class.name}: #{ex.message}"
10
- Sidekiq.logger.warn ex.backtrace.join("\n") unless ex.backtrace.nil?
11
- end
12
-
13
- # Set up default handler which just logs the error
14
- Sidekiq.error_handlers << Sidekiq::ExceptionHandler::Logger.new
15
- end
16
-
17
- def handle_exception(ex, ctxHash={})
18
- Sidekiq.error_handlers.each do |handler|
19
- begin
20
- handler.call(ex, ctxHash)
21
- rescue => ex
22
- Sidekiq.logger.error "!!! ERROR HANDLER THREW AN ERROR !!!"
23
- Sidekiq.logger.error ex
24
- Sidekiq.logger.error ex.backtrace.join("\n") unless ex.backtrace.nil?
25
- end
26
- end
27
- end
28
-
29
- end
30
- end
@@ -1,56 +0,0 @@
1
- require 'sidekiq/extensions/generic_proxy'
2
-
3
- module Sidekiq
4
- module Extensions
5
- ##
6
- # Adds 'delay', 'delay_for' and `delay_until` methods to ActionMailer to offload arbitrary email
7
- # delivery to Sidekiq. Example:
8
- #
9
- # UserMailer.delay.send_welcome_email(new_user)
10
- # UserMailer.delay_for(5.days).send_welcome_email(new_user)
11
- # UserMailer.delay_until(5.days.from_now).send_welcome_email(new_user)
12
- class DelayedMailer
13
- include Sidekiq::Worker
14
-
15
- def perform(yml)
16
- (target, method_name, args) = YAML.load(yml)
17
- msg = target.public_send(method_name, *args)
18
- # The email method can return nil, which causes ActionMailer to return
19
- # an undeliverable empty message.
20
- if msg
21
- deliver(msg)
22
- else
23
- raise "#{target.name}##{method_name} returned an undeliverable mail object"
24
- end
25
- end
26
-
27
- private
28
-
29
- def deliver(msg)
30
- if msg.respond_to?(:deliver_now)
31
- # Rails 4.2/5.0
32
- msg.deliver_now
33
- else
34
- # Rails 3.2/4.0/4.1
35
- msg.deliver
36
- end
37
- end
38
- end
39
-
40
- module ActionMailer
41
- def sidekiq_delay(options={})
42
- Proxy.new(DelayedMailer, self, options)
43
- end
44
- def sidekiq_delay_for(interval, options={})
45
- Proxy.new(DelayedMailer, self, options.merge('at' => Time.now.to_f + interval.to_f))
46
- end
47
- def sidekiq_delay_until(timestamp, options={})
48
- Proxy.new(DelayedMailer, self, options.merge('at' => timestamp.to_f))
49
- end
50
- alias_method :delay, :sidekiq_delay
51
- alias_method :delay_for, :sidekiq_delay_for
52
- alias_method :delay_until, :sidekiq_delay_until
53
- end
54
-
55
- end
56
- end
@@ -1,39 +0,0 @@
1
- require 'sidekiq/extensions/generic_proxy'
2
-
3
- module Sidekiq
4
- module Extensions
5
- ##
6
- # Adds 'delay', 'delay_for' and `delay_until` methods to ActiveRecord to offload instance method
7
- # execution to Sidekiq. Examples:
8
- #
9
- # User.recent_signups.each { |user| user.delay.mark_as_awesome }
10
- #
11
- # Please note, this is not recommended as this will serialize the entire
12
- # object to Redis. Your Sidekiq jobs should pass IDs, not entire instances.
13
- # This is here for backwards compatibility with Delayed::Job only.
14
- class DelayedModel
15
- include Sidekiq::Worker
16
-
17
- def perform(yml)
18
- (target, method_name, args) = YAML.load(yml)
19
- target.__send__(method_name, *args)
20
- end
21
- end
22
-
23
- module ActiveRecord
24
- def sidekiq_delay(options={})
25
- Proxy.new(DelayedModel, self, options)
26
- end
27
- def sidekiq_delay_for(interval, options={})
28
- Proxy.new(DelayedModel, self, options.merge('at' => Time.now.to_f + interval.to_f))
29
- end
30
- def sidekiq_delay_until(timestamp, options={})
31
- Proxy.new(DelayedModel, self, options.merge('at' => timestamp.to_f))
32
- end
33
- alias_method :delay, :sidekiq_delay
34
- alias_method :delay_for, :sidekiq_delay_for
35
- alias_method :delay_until, :sidekiq_delay_until
36
- end
37
-
38
- end
39
- end
@@ -1,39 +0,0 @@
1
- require 'sidekiq/extensions/generic_proxy'
2
-
3
- module Sidekiq
4
- module Extensions
5
- ##
6
- # Adds 'delay', 'delay_for' and `delay_until` methods to all Classes to offload class method
7
- # execution to Sidekiq. Examples:
8
- #
9
- # User.delay.delete_inactive
10
- # Wikipedia.delay.download_changes_for(Date.today)
11
- #
12
- class DelayedClass
13
- include Sidekiq::Worker
14
-
15
- def perform(yml)
16
- (target, method_name, args) = YAML.load(yml)
17
- target.__send__(method_name, *args)
18
- end
19
- end
20
-
21
- module Klass
22
- def sidekiq_delay(options={})
23
- Proxy.new(DelayedClass, self, options)
24
- end
25
- def sidekiq_delay_for(interval, options={})
26
- Proxy.new(DelayedClass, self, options.merge('at' => Time.now.to_f + interval.to_f))
27
- end
28
- def sidekiq_delay_until(timestamp, options={})
29
- Proxy.new(DelayedClass, self, options.merge('at' => timestamp.to_f))
30
- end
31
- alias_method :delay, :sidekiq_delay
32
- alias_method :delay_for, :sidekiq_delay_for
33
- alias_method :delay_until, :sidekiq_delay_until
34
- end
35
-
36
- end
37
- end
38
-
39
- Module.__send__(:include, Sidekiq::Extensions::Klass) unless defined?(::Rails)
@@ -1,24 +0,0 @@
1
- require 'yaml'
2
-
3
- module Sidekiq
4
- module Extensions
5
- class Proxy < BasicObject
6
- def initialize(performable, target, options={})
7
- @performable = performable
8
- @target = target
9
- @opts = options
10
- end
11
-
12
- def method_missing(name, *args)
13
- # Sidekiq has a limitation in that its message must be JSON.
14
- # JSON can't round trip real Ruby objects so we use YAML to
15
- # serialize the objects to a String. The YAML will be converted
16
- # to JSON and then deserialized on the other side back into a
17
- # Ruby object.
18
- obj = [@target, name, args]
19
- @performable.client_push({ 'class' => @performable, 'args' => [::YAML.dump(obj)] }.merge(@opts))
20
- end
21
- end
22
-
23
- end
24
- end
@@ -1,104 +0,0 @@
1
- require 'time'
2
- require 'logger'
3
-
4
- module Sidekiq
5
- module Logging
6
-
7
- class Pretty < Logger::Formatter
8
- SPACE = " "
9
-
10
- # Provide a call() method that returns the formatted message.
11
- def call(severity, time, program_name, message)
12
- "#{time.utc.iso8601(3)} #{::Process.pid} TID-#{Thread.current.object_id.to_s(36)}#{context} #{severity}: #{message}\n"
13
- end
14
-
15
- def context
16
- c = Thread.current[:sidekiq_context]
17
- " #{c.join(SPACE)}" if c && c.any?
18
- end
19
- end
20
-
21
- class WithoutTimestamp < Pretty
22
- def call(severity, time, program_name, message)
23
- "#{::Process.pid} TID-#{Thread.current.object_id.to_s(36)}#{context} #{severity}: #{message}\n"
24
- end
25
- end
26
-
27
- def self.with_context(msg)
28
- Thread.current[:sidekiq_context] ||= []
29
- Thread.current[:sidekiq_context] << msg
30
- yield
31
- ensure
32
- Thread.current[:sidekiq_context].pop
33
- end
34
-
35
- def self.initialize_logger(log_target = STDOUT)
36
- oldlogger = defined?(@logger) ? @logger : nil
37
- @logger = Logger.new(log_target)
38
- @logger.level = Logger::INFO
39
- @logger.formatter = ENV['DYNO'] ? WithoutTimestamp.new : Pretty.new
40
- oldlogger.close if oldlogger && !$TESTING # don't want to close testing's STDOUT logging
41
- @logger
42
- end
43
-
44
- def self.logger
45
- defined?(@logger) ? @logger : initialize_logger
46
- end
47
-
48
- def self.logger=(log)
49
- @logger = (log ? log : Logger.new('/dev/null'))
50
- end
51
-
52
- # This reopens ALL logfiles in the process that have been rotated
53
- # using logrotate(8) (without copytruncate) or similar tools.
54
- # A +File+ object is considered for reopening if it is:
55
- # 1) opened with the O_APPEND and O_WRONLY flags
56
- # 2) the current open file handle does not match its original open path
57
- # 3) unbuffered (as far as userspace buffering goes, not O_SYNC)
58
- # Returns the number of files reopened
59
- def self.reopen_logs
60
- to_reopen = []
61
- append_flags = File::WRONLY | File::APPEND
62
-
63
- ObjectSpace.each_object(File) do |fp|
64
- begin
65
- if !fp.closed? && fp.stat.file? && fp.sync && (fp.fcntl(Fcntl::F_GETFL) & append_flags) == append_flags
66
- to_reopen << fp
67
- end
68
- rescue IOError, Errno::EBADF
69
- end
70
- end
71
-
72
- nr = 0
73
- to_reopen.each do |fp|
74
- orig_st = begin
75
- fp.stat
76
- rescue IOError, Errno::EBADF
77
- next
78
- end
79
-
80
- begin
81
- b = File.stat(fp.path)
82
- next if orig_st.ino == b.ino && orig_st.dev == b.dev
83
- rescue Errno::ENOENT
84
- end
85
-
86
- begin
87
- File.open(fp.path, 'a') { |tmpfp| fp.reopen(tmpfp) }
88
- fp.sync = true
89
- nr += 1
90
- rescue IOError, Errno::EBADF
91
- # not much we can do...
92
- end
93
- end
94
- nr
95
- rescue RuntimeError => ex
96
- # RuntimeError: ObjectSpace is disabled; each_object will only work with Class, pass -X+O to enable
97
- puts "Unable to reopen logs: #{ex.message}"
98
- end
99
-
100
- def logger
101
- Sidekiq::Logging.logger
102
- end
103
- end
104
- end
@@ -1,13 +0,0 @@
1
- module Sidekiq
2
- module Middleware
3
- module Server
4
- class ActiveRecord
5
- def call(*args)
6
- yield
7
- ensure
8
- ::ActiveRecord::Base.clear_active_connections!
9
- end
10
- end
11
- end
12
- end
13
- end
@@ -1,40 +0,0 @@
1
- module Sidekiq
2
- module Middleware
3
- module Server
4
- class Logging
5
-
6
- def call(worker, item, queue)
7
- Sidekiq::Logging.with_context(log_context(worker, item)) do
8
- begin
9
- start = Time.now
10
- logger.info { "start" }
11
- yield
12
- logger.info { "done: #{elapsed(start)} sec" }
13
- rescue Exception
14
- logger.info { "fail: #{elapsed(start)} sec" }
15
- raise
16
- end
17
- end
18
- end
19
-
20
- private
21
-
22
- # If we're using a wrapper class, like ActiveJob, use the "wrapped"
23
- # attribute to expose the underlying thing.
24
- def log_context(worker, item)
25
- klass = item['wrapped'.freeze] || worker.class.to_s
26
- "#{klass} JID-#{item['jid'.freeze]}#{" BID-#{item['bid'.freeze]}" if item['bid'.freeze]}"
27
- end
28
-
29
- def elapsed(start)
30
- (Time.now - start).round(3)
31
- end
32
-
33
- def logger
34
- Sidekiq.logger
35
- end
36
- end
37
- end
38
- end
39
- end
40
-
@@ -1,206 +0,0 @@
1
- require 'sidekiq/scheduled'
2
- require 'sidekiq/api'
3
-
4
- module Sidekiq
5
- module Middleware
6
- module Server
7
- ##
8
- # Automatically retry jobs that fail in Sidekiq.
9
- # Sidekiq's retry support assumes a typical development lifecycle:
10
- #
11
- # 0. push some code changes with a bug in it
12
- # 1. bug causes job processing to fail, sidekiq's middleware captures
13
- # the job and pushes it onto a retry queue
14
- # 2. sidekiq retries jobs in the retry queue multiple times with
15
- # an exponential delay, the job continues to fail
16
- # 3. after a few days, a developer deploys a fix. the job is
17
- # reprocessed successfully.
18
- # 4. once retries are exhausted, sidekiq will give up and move the
19
- # job to the Dead Job Queue (aka morgue) where it must be dealt with
20
- # manually in the Web UI.
21
- # 5. After 6 months on the DJQ, Sidekiq will discard the job.
22
- #
23
- # A job looks like:
24
- #
25
- # { 'class' => 'HardWorker', 'args' => [1, 2, 'foo'], 'retry' => true }
26
- #
27
- # The 'retry' option also accepts a number (in place of 'true'):
28
- #
29
- # { 'class' => 'HardWorker', 'args' => [1, 2, 'foo'], 'retry' => 5 }
30
- #
31
- # The job will be retried this number of times before giving up. (If simply
32
- # 'true', Sidekiq retries 25 times)
33
- #
34
- # We'll add a bit more data to the job to support retries:
35
- #
36
- # * 'queue' - the queue to use
37
- # * 'retry_count' - number of times we've retried so far.
38
- # * 'error_message' - the message from the exception
39
- # * 'error_class' - the exception class
40
- # * 'failed_at' - the first time it failed
41
- # * 'retried_at' - the last time it was retried
42
- # * 'backtrace' - the number of lines of error backtrace to store
43
- #
44
- # We don't store the backtrace by default as that can add a lot of overhead
45
- # to the job and everyone is using an error service, right?
46
- #
47
- # The default number of retry attempts is 25 which works out to about 3 weeks
48
- # of retries. You can pass a value for the max number of retry attempts when
49
- # adding the middleware using the options hash:
50
- #
51
- # Sidekiq.configure_server do |config|
52
- # config.server_middleware do |chain|
53
- # chain.add Sidekiq::Middleware::Server::RetryJobs, :max_retries => 7
54
- # end
55
- # end
56
- #
57
- # or limit the number of retries for a particular worker with:
58
- #
59
- # class MyWorker
60
- # include Sidekiq::Worker
61
- # sidekiq_options :retry => 10
62
- # end
63
- #
64
- class RetryJobs
65
- include Sidekiq::Util
66
-
67
- DEFAULT_MAX_RETRY_ATTEMPTS = 25
68
-
69
- def initialize(options = {})
70
- @max_retries = options.fetch(:max_retries, DEFAULT_MAX_RETRY_ATTEMPTS)
71
- end
72
-
73
- def call(worker, msg, queue)
74
- yield
75
- rescue Sidekiq::Shutdown
76
- # ignore, will be pushed back onto queue during hard_shutdown
77
- raise
78
- rescue Exception => e
79
- # ignore, will be pushed back onto queue during hard_shutdown
80
- raise Sidekiq::Shutdown if exception_caused_by_shutdown?(e)
81
-
82
- raise e unless msg['retry']
83
- attempt_retry(worker, msg, queue, e)
84
- end
85
-
86
- private
87
-
88
- def attempt_retry(worker, msg, queue, exception)
89
- max_retry_attempts = retry_attempts_from(msg['retry'], @max_retries)
90
-
91
- msg['queue'] = if msg['retry_queue']
92
- msg['retry_queue']
93
- else
94
- queue
95
- end
96
-
97
- # App code can stuff all sorts of crazy binary data into the error message
98
- # that won't convert to JSON.
99
- m = exception.message.to_s[0, 10_000]
100
- if m.respond_to?(:scrub!)
101
- m.force_encoding("utf-8")
102
- m.scrub!
103
- end
104
-
105
- msg['error_message'] = m
106
- msg['error_class'] = exception.class.name
107
- count = if msg['retry_count']
108
- msg['retried_at'] = Time.now.to_f
109
- msg['retry_count'] += 1
110
- else
111
- msg['failed_at'] = Time.now.to_f
112
- msg['retry_count'] = 0
113
- end
114
-
115
- if msg['backtrace'] == true
116
- msg['error_backtrace'] = exception.backtrace
117
- elsif !msg['backtrace']
118
- # do nothing
119
- elsif msg['backtrace'].to_i != 0
120
- msg['error_backtrace'] = exception.backtrace[0...msg['backtrace'].to_i]
121
- end
122
-
123
- if count < max_retry_attempts
124
- delay = delay_for(worker, count, exception)
125
- logger.debug { "Failure! Retry #{count} in #{delay} seconds" }
126
- retry_at = Time.now.to_f + delay
127
- payload = Sidekiq.dump_json(msg)
128
- Sidekiq.redis do |conn|
129
- conn.zadd('retry', retry_at.to_s, payload)
130
- end
131
- else
132
- # Goodbye dear message, you (re)tried your best I'm sure.
133
- retries_exhausted(worker, msg)
134
- end
135
-
136
- raise exception
137
- end
138
-
139
- def retries_exhausted(worker, msg)
140
- logger.debug { "Dropping message after hitting the retry maximum: #{msg}" }
141
- begin
142
- if worker.sidekiq_retries_exhausted_block?
143
- worker.sidekiq_retries_exhausted_block.call(msg)
144
- end
145
- rescue => e
146
- handle_exception(e, { context: "Error calling retries_exhausted for #{worker.class}", job: msg })
147
- end
148
-
149
- send_to_morgue(msg) unless msg['dead'] == false
150
- end
151
-
152
- def send_to_morgue(msg)
153
- Sidekiq.logger.info { "Adding dead #{msg['class']} job #{msg['jid']}" }
154
- payload = Sidekiq.dump_json(msg)
155
- now = Time.now.to_f
156
- Sidekiq.redis do |conn|
157
- conn.multi do
158
- conn.zadd('dead', now, payload)
159
- conn.zremrangebyscore('dead', '-inf', now - DeadSet.timeout)
160
- conn.zremrangebyrank('dead', 0, -DeadSet.max_jobs)
161
- end
162
- end
163
- end
164
-
165
- def retry_attempts_from(msg_retry, default)
166
- if msg_retry.is_a?(Fixnum)
167
- msg_retry
168
- else
169
- default
170
- end
171
- end
172
-
173
- def delay_for(worker, count, exception)
174
- worker.sidekiq_retry_in_block? && retry_in(worker, count, exception) || seconds_to_delay(count)
175
- end
176
-
177
- # delayed_job uses the same basic formula
178
- def seconds_to_delay(count)
179
- (count ** 4) + 15 + (rand(30)*(count+1))
180
- end
181
-
182
- def retry_in(worker, count, exception)
183
- begin
184
- worker.sidekiq_retry_in_block.call(count, exception).to_i
185
- rescue Exception => e
186
- handle_exception(e, { context: "Failure scheduling retry using the defined `sidekiq_retry_in` in #{worker.class.name}, falling back to default" })
187
- nil
188
- end
189
- end
190
-
191
- def exception_caused_by_shutdown?(e, checked_causes = [])
192
- # In Ruby 2.1.0 only, check if exception is a result of shutdown.
193
- return false unless defined?(e.cause)
194
-
195
- # Handle circular causes
196
- checked_causes << e.object_id
197
- return false if checked_causes.include?(e.cause.object_id)
198
-
199
- e.cause.instance_of?(Sidekiq::Shutdown) ||
200
- exception_caused_by_shutdown?(e.cause, checked_causes)
201
- end
202
-
203
- end
204
- end
205
- end
206
- end