sr-sidekiq 4.1.6

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 (186) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/3.0-Upgrade.md +70 -0
  4. data/4.0-Upgrade.md +50 -0
  5. data/COMM-LICENSE (sidekiq) +95 -0
  6. data/Changes.md +1241 -0
  7. data/Ent-Changes.md +112 -0
  8. data/Gemfile +29 -0
  9. data/LICENSE (sidekiq) +9 -0
  10. data/LICENSE (sr-sidekiq) +5 -0
  11. data/Pro-2.0-Upgrade.md +138 -0
  12. data/Pro-3.0-Upgrade.md +44 -0
  13. data/Pro-Changes.md +539 -0
  14. data/README.md +8 -0
  15. data/Rakefile +9 -0
  16. data/bin/sidekiq +18 -0
  17. data/bin/sidekiqctl +99 -0
  18. data/bin/sidekiqload +167 -0
  19. data/code_of_conduct.md +50 -0
  20. data/lib/generators/sidekiq/templates/worker.rb.erb +9 -0
  21. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +6 -0
  22. data/lib/generators/sidekiq/templates/worker_test.rb.erb +8 -0
  23. data/lib/generators/sidekiq/worker_generator.rb +49 -0
  24. data/lib/sidekiq.rb +237 -0
  25. data/lib/sidekiq/api.rb +844 -0
  26. data/lib/sidekiq/cli.rb +389 -0
  27. data/lib/sidekiq/client.rb +260 -0
  28. data/lib/sidekiq/core_ext.rb +106 -0
  29. data/lib/sidekiq/exception_handler.rb +31 -0
  30. data/lib/sidekiq/extensions/action_mailer.rb +57 -0
  31. data/lib/sidekiq/extensions/active_record.rb +40 -0
  32. data/lib/sidekiq/extensions/class_methods.rb +40 -0
  33. data/lib/sidekiq/extensions/generic_proxy.rb +25 -0
  34. data/lib/sidekiq/fetch.rb +81 -0
  35. data/lib/sidekiq/launcher.rb +160 -0
  36. data/lib/sidekiq/logging.rb +106 -0
  37. data/lib/sidekiq/manager.rb +137 -0
  38. data/lib/sidekiq/middleware/chain.rb +150 -0
  39. data/lib/sidekiq/middleware/i18n.rb +42 -0
  40. data/lib/sidekiq/middleware/server/active_record.rb +13 -0
  41. data/lib/sidekiq/middleware/server/logging.rb +40 -0
  42. data/lib/sidekiq/middleware/server/retry_jobs.rb +205 -0
  43. data/lib/sidekiq/paginator.rb +43 -0
  44. data/lib/sidekiq/processor.rb +186 -0
  45. data/lib/sidekiq/rails.rb +39 -0
  46. data/lib/sidekiq/redis_connection.rb +97 -0
  47. data/lib/sidekiq/scheduled.rb +146 -0
  48. data/lib/sidekiq/testing.rb +316 -0
  49. data/lib/sidekiq/testing/inline.rb +29 -0
  50. data/lib/sidekiq/util.rb +62 -0
  51. data/lib/sidekiq/version.rb +4 -0
  52. data/lib/sidekiq/web.rb +278 -0
  53. data/lib/sidekiq/web_helpers.rb +255 -0
  54. data/lib/sidekiq/worker.rb +121 -0
  55. data/sidekiq.gemspec +26 -0
  56. data/sr-sidekiq-4.1.3.gem +0 -0
  57. data/sr-sidekiq-4.1.4.gem +0 -0
  58. data/sr-sidekiq-4.1.5.gem +0 -0
  59. data/test/config.yml +9 -0
  60. data/test/env_based_config.yml +11 -0
  61. data/test/fake_env.rb +1 -0
  62. data/test/fixtures/en.yml +2 -0
  63. data/test/helper.rb +75 -0
  64. data/test/test_actors.rb +138 -0
  65. data/test/test_api.rb +528 -0
  66. data/test/test_cli.rb +406 -0
  67. data/test/test_client.rb +262 -0
  68. data/test/test_exception_handler.rb +56 -0
  69. data/test/test_extensions.rb +127 -0
  70. data/test/test_fetch.rb +50 -0
  71. data/test/test_launcher.rb +85 -0
  72. data/test/test_logging.rb +35 -0
  73. data/test/test_manager.rb +50 -0
  74. data/test/test_middleware.rb +158 -0
  75. data/test/test_processor.rb +201 -0
  76. data/test/test_rails.rb +22 -0
  77. data/test/test_redis_connection.rb +127 -0
  78. data/test/test_retry.rb +326 -0
  79. data/test/test_retry_exhausted.rb +149 -0
  80. data/test/test_scheduled.rb +115 -0
  81. data/test/test_scheduling.rb +50 -0
  82. data/test/test_sidekiq.rb +107 -0
  83. data/test/test_testing.rb +143 -0
  84. data/test/test_testing_fake.rb +357 -0
  85. data/test/test_testing_inline.rb +94 -0
  86. data/test/test_util.rb +13 -0
  87. data/test/test_web.rb +614 -0
  88. data/test/test_web_helpers.rb +54 -0
  89. data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
  90. data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
  91. data/web/assets/images/favicon.ico +0 -0
  92. data/web/assets/images/logo.png +0 -0
  93. data/web/assets/images/status-sd8051fd480.png +0 -0
  94. data/web/assets/images/status/active.png +0 -0
  95. data/web/assets/images/status/idle.png +0 -0
  96. data/web/assets/javascripts/application.js +88 -0
  97. data/web/assets/javascripts/dashboard.js +300 -0
  98. data/web/assets/javascripts/locales/README.md +27 -0
  99. data/web/assets/javascripts/locales/jquery.timeago.ar.js +96 -0
  100. data/web/assets/javascripts/locales/jquery.timeago.bg.js +18 -0
  101. data/web/assets/javascripts/locales/jquery.timeago.bs.js +49 -0
  102. data/web/assets/javascripts/locales/jquery.timeago.ca.js +18 -0
  103. data/web/assets/javascripts/locales/jquery.timeago.cs.js +18 -0
  104. data/web/assets/javascripts/locales/jquery.timeago.cy.js +20 -0
  105. data/web/assets/javascripts/locales/jquery.timeago.da.js +18 -0
  106. data/web/assets/javascripts/locales/jquery.timeago.de.js +18 -0
  107. data/web/assets/javascripts/locales/jquery.timeago.el.js +18 -0
  108. data/web/assets/javascripts/locales/jquery.timeago.en-short.js +20 -0
  109. data/web/assets/javascripts/locales/jquery.timeago.en.js +20 -0
  110. data/web/assets/javascripts/locales/jquery.timeago.es.js +18 -0
  111. data/web/assets/javascripts/locales/jquery.timeago.et.js +18 -0
  112. data/web/assets/javascripts/locales/jquery.timeago.fa.js +22 -0
  113. data/web/assets/javascripts/locales/jquery.timeago.fi.js +28 -0
  114. data/web/assets/javascripts/locales/jquery.timeago.fr-short.js +16 -0
  115. data/web/assets/javascripts/locales/jquery.timeago.fr.js +17 -0
  116. data/web/assets/javascripts/locales/jquery.timeago.he.js +18 -0
  117. data/web/assets/javascripts/locales/jquery.timeago.hr.js +49 -0
  118. data/web/assets/javascripts/locales/jquery.timeago.hu.js +18 -0
  119. data/web/assets/javascripts/locales/jquery.timeago.hy.js +18 -0
  120. data/web/assets/javascripts/locales/jquery.timeago.id.js +18 -0
  121. data/web/assets/javascripts/locales/jquery.timeago.it.js +16 -0
  122. data/web/assets/javascripts/locales/jquery.timeago.ja.js +19 -0
  123. data/web/assets/javascripts/locales/jquery.timeago.ko.js +17 -0
  124. data/web/assets/javascripts/locales/jquery.timeago.lt.js +20 -0
  125. data/web/assets/javascripts/locales/jquery.timeago.mk.js +20 -0
  126. data/web/assets/javascripts/locales/jquery.timeago.nl.js +20 -0
  127. data/web/assets/javascripts/locales/jquery.timeago.no.js +18 -0
  128. data/web/assets/javascripts/locales/jquery.timeago.pl.js +31 -0
  129. data/web/assets/javascripts/locales/jquery.timeago.pt-br.js +16 -0
  130. data/web/assets/javascripts/locales/jquery.timeago.pt.js +16 -0
  131. data/web/assets/javascripts/locales/jquery.timeago.ro.js +18 -0
  132. data/web/assets/javascripts/locales/jquery.timeago.rs.js +49 -0
  133. data/web/assets/javascripts/locales/jquery.timeago.ru.js +34 -0
  134. data/web/assets/javascripts/locales/jquery.timeago.sk.js +18 -0
  135. data/web/assets/javascripts/locales/jquery.timeago.sl.js +44 -0
  136. data/web/assets/javascripts/locales/jquery.timeago.sv.js +18 -0
  137. data/web/assets/javascripts/locales/jquery.timeago.th.js +20 -0
  138. data/web/assets/javascripts/locales/jquery.timeago.tr.js +16 -0
  139. data/web/assets/javascripts/locales/jquery.timeago.uk.js +34 -0
  140. data/web/assets/javascripts/locales/jquery.timeago.uz.js +19 -0
  141. data/web/assets/javascripts/locales/jquery.timeago.zh-cn.js +20 -0
  142. data/web/assets/javascripts/locales/jquery.timeago.zh-tw.js +20 -0
  143. data/web/assets/stylesheets/application.css +754 -0
  144. data/web/assets/stylesheets/bootstrap.css +9 -0
  145. data/web/locales/cs.yml +78 -0
  146. data/web/locales/da.yml +68 -0
  147. data/web/locales/de.yml +69 -0
  148. data/web/locales/el.yml +68 -0
  149. data/web/locales/en.yml +79 -0
  150. data/web/locales/es.yml +69 -0
  151. data/web/locales/fr.yml +78 -0
  152. data/web/locales/hi.yml +75 -0
  153. data/web/locales/it.yml +69 -0
  154. data/web/locales/ja.yml +78 -0
  155. data/web/locales/ko.yml +68 -0
  156. data/web/locales/nb.yml +77 -0
  157. data/web/locales/nl.yml +68 -0
  158. data/web/locales/pl.yml +59 -0
  159. data/web/locales/pt-br.yml +68 -0
  160. data/web/locales/pt.yml +67 -0
  161. data/web/locales/ru.yml +78 -0
  162. data/web/locales/sv.yml +68 -0
  163. data/web/locales/ta.yml +75 -0
  164. data/web/locales/uk.yml +76 -0
  165. data/web/locales/zh-cn.yml +68 -0
  166. data/web/locales/zh-tw.yml +68 -0
  167. data/web/views/_footer.erb +17 -0
  168. data/web/views/_job_info.erb +88 -0
  169. data/web/views/_nav.erb +66 -0
  170. data/web/views/_paging.erb +23 -0
  171. data/web/views/_poll_js.erb +5 -0
  172. data/web/views/_poll_link.erb +7 -0
  173. data/web/views/_status.erb +4 -0
  174. data/web/views/_summary.erb +40 -0
  175. data/web/views/busy.erb +94 -0
  176. data/web/views/dashboard.erb +75 -0
  177. data/web/views/dead.erb +34 -0
  178. data/web/views/layout.erb +32 -0
  179. data/web/views/morgue.erb +71 -0
  180. data/web/views/queue.erb +45 -0
  181. data/web/views/queues.erb +28 -0
  182. data/web/views/retries.erb +74 -0
  183. data/web/views/retry.erb +34 -0
  184. data/web/views/scheduled.erb +54 -0
  185. data/web/views/scheduled_job_info.erb +8 -0
  186. metadata +408 -0
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+ require 'time'
3
+ require 'logger'
4
+ require 'fcntl'
5
+
6
+ module Sidekiq
7
+ module Logging
8
+
9
+ class Pretty < Logger::Formatter
10
+ SPACE = " "
11
+
12
+ # Provide a call() method that returns the formatted message.
13
+ def call(severity, time, program_name, message)
14
+ "#{time.utc.iso8601(3)} #{::Process.pid} TID-#{Thread.current.object_id.to_s(36)}#{context} #{severity}: #{message}\n"
15
+ end
16
+
17
+ def context
18
+ c = Thread.current[:sidekiq_context]
19
+ " #{c.join(SPACE)}" if c && c.any?
20
+ end
21
+ end
22
+
23
+ class WithoutTimestamp < Pretty
24
+ def call(severity, time, program_name, message)
25
+ "#{::Process.pid} TID-#{Thread.current.object_id.to_s(36)}#{context} #{severity}: #{message}\n"
26
+ end
27
+ end
28
+
29
+ def self.with_context(msg)
30
+ Thread.current[:sidekiq_context] ||= []
31
+ Thread.current[:sidekiq_context] << msg
32
+ yield
33
+ ensure
34
+ Thread.current[:sidekiq_context].pop
35
+ end
36
+
37
+ def self.initialize_logger(log_target = STDOUT)
38
+ oldlogger = defined?(@logger) ? @logger : nil
39
+ @logger = Logger.new(log_target)
40
+ @logger.level = Logger::INFO
41
+ @logger.formatter = ENV['DYNO'] ? WithoutTimestamp.new : Pretty.new
42
+ oldlogger.close if oldlogger && !$TESTING # don't want to close testing's STDOUT logging
43
+ @logger
44
+ end
45
+
46
+ def self.logger
47
+ defined?(@logger) ? @logger : initialize_logger
48
+ end
49
+
50
+ def self.logger=(log)
51
+ @logger = (log ? log : Logger.new(File::NULL))
52
+ end
53
+
54
+ # This reopens ALL logfiles in the process that have been rotated
55
+ # using logrotate(8) (without copytruncate) or similar tools.
56
+ # A +File+ object is considered for reopening if it is:
57
+ # 1) opened with the O_APPEND and O_WRONLY flags
58
+ # 2) the current open file handle does not match its original open path
59
+ # 3) unbuffered (as far as userspace buffering goes, not O_SYNC)
60
+ # Returns the number of files reopened
61
+ def self.reopen_logs
62
+ to_reopen = []
63
+ append_flags = File::WRONLY | File::APPEND
64
+
65
+ ObjectSpace.each_object(File) do |fp|
66
+ begin
67
+ if !fp.closed? && fp.stat.file? && fp.sync && (fp.fcntl(Fcntl::F_GETFL) & append_flags) == append_flags
68
+ to_reopen << fp
69
+ end
70
+ rescue IOError, Errno::EBADF
71
+ end
72
+ end
73
+
74
+ nr = 0
75
+ to_reopen.each do |fp|
76
+ orig_st = begin
77
+ fp.stat
78
+ rescue IOError, Errno::EBADF
79
+ next
80
+ end
81
+
82
+ begin
83
+ b = File.stat(fp.path)
84
+ next if orig_st.ino == b.ino && orig_st.dev == b.dev
85
+ rescue Errno::ENOENT
86
+ end
87
+
88
+ begin
89
+ File.open(fp.path, 'a') { |tmpfp| fp.reopen(tmpfp) }
90
+ fp.sync = true
91
+ nr += 1
92
+ rescue IOError, Errno::EBADF
93
+ # not much we can do...
94
+ end
95
+ end
96
+ nr
97
+ rescue RuntimeError => ex
98
+ # RuntimeError: ObjectSpace is disabled; each_object will only work with Class, pass -X+O to enable
99
+ puts "Unable to reopen logs: #{ex.message}"
100
+ end
101
+
102
+ def logger
103
+ Sidekiq::Logging.logger
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+ require 'sidekiq/util'
4
+ require 'sidekiq/processor'
5
+ require 'sidekiq/fetch'
6
+ require 'thread'
7
+
8
+ module Sidekiq
9
+
10
+ ##
11
+ # The Manager is the central coordination point in Sidekiq, controlling
12
+ # the lifecycle of the Processors and feeding them jobs as necessary.
13
+ #
14
+ # Tasks:
15
+ #
16
+ # 1. start: Spin up Processors.
17
+ # 3. processor_died: Handle job failure, throw away Processor, create new one.
18
+ # 4. quiet: shutdown idle Processors.
19
+ # 5. stop: hard stop the Processors by deadline.
20
+ #
21
+ # Note that only the last task requires its own Thread since it has to monitor
22
+ # the shutdown process. The other tasks are performed by other threads.
23
+ #
24
+ class Manager
25
+ include Util
26
+
27
+ attr_reader :workers
28
+ attr_reader :options
29
+
30
+ def initialize(options={})
31
+ logger.debug { options.inspect }
32
+ @options = options
33
+ @count = options[:concurrency] || 25
34
+ raise ArgumentError, "Concurrency of #{@count} is not supported" if @count < 1
35
+
36
+ @done = false
37
+ @workers = Set.new
38
+ @count.times do
39
+ @workers << Processor.new(self)
40
+ end
41
+ @plock = Mutex.new
42
+ end
43
+
44
+ def start
45
+ @workers.each do |x|
46
+ x.start
47
+ end
48
+ end
49
+
50
+ def quiet
51
+ return if @done
52
+ @done = true
53
+
54
+ logger.info { "Terminating quiet workers" }
55
+ @workers.each { |x| x.terminate }
56
+ fire_event(:quiet, true)
57
+ end
58
+
59
+ # hack for quicker development / testing environment #2774
60
+ PAUSE_TIME = STDOUT.tty? ? 0.1 : 0.5
61
+
62
+ def stop(deadline)
63
+ quiet
64
+ fire_event(:shutdown, true)
65
+
66
+ # some of the shutdown events can be async,
67
+ # we don't have any way to know when they're done but
68
+ # give them a little time to take effect
69
+ sleep PAUSE_TIME
70
+ return if @workers.empty?
71
+
72
+ logger.info { "Pausing to allow workers to finish..." }
73
+ remaining = deadline - Time.now
74
+ while remaining > PAUSE_TIME
75
+ return if @workers.empty?
76
+ sleep PAUSE_TIME
77
+ remaining = deadline - Time.now
78
+ end
79
+ return if @workers.empty?
80
+
81
+ hard_shutdown
82
+ end
83
+
84
+ def processor_stopped(processor)
85
+ @plock.synchronize do
86
+ @workers.delete(processor)
87
+ end
88
+ end
89
+
90
+ def processor_died(processor, reason)
91
+ @plock.synchronize do
92
+ @workers.delete(processor)
93
+ unless @done
94
+ p = Processor.new(self)
95
+ @workers << p
96
+ p.start
97
+ end
98
+ end
99
+ end
100
+
101
+ def stopped?
102
+ @done
103
+ end
104
+
105
+ private
106
+
107
+ def hard_shutdown
108
+ # We've reached the timeout and we still have busy workers.
109
+ # They must die but their jobs shall live on.
110
+ cleanup = nil
111
+ @plock.synchronize do
112
+ cleanup = @workers.dup
113
+ end
114
+
115
+ if cleanup.size > 0
116
+ jobs = cleanup.map {|p| p.job }.compact
117
+
118
+ logger.warn { "Terminating #{cleanup.size} busy worker threads" }
119
+ logger.warn { "Work still in progress #{jobs.inspect}" }
120
+
121
+ # Re-enqueue unfinished jobs
122
+ # NOTE: You may notice that we may push a job back to redis before
123
+ # the worker thread is terminated. This is ok because Sidekiq's
124
+ # contract says that jobs are run AT LEAST once. Process termination
125
+ # is delayed until we're certain the jobs are back in Redis because
126
+ # it is worse to lose a job than to run it twice.
127
+ strategy = (@options[:fetch] || Sidekiq::BasicFetch)
128
+ strategy.bulk_requeue(jobs, @options)
129
+ end
130
+
131
+ cleanup.each do |processor|
132
+ processor.kill
133
+ end
134
+ end
135
+
136
+ end
137
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+ module Sidekiq
3
+ # Middleware is code configured to run before/after
4
+ # a message is processed. It is patterned after Rack
5
+ # middleware. Middleware exists for the client side
6
+ # (pushing jobs onto the queue) as well as the server
7
+ # side (when jobs are actually processed).
8
+ #
9
+ # To add middleware for the client:
10
+ #
11
+ # Sidekiq.configure_client do |config|
12
+ # config.client_middleware do |chain|
13
+ # chain.add MyClientHook
14
+ # end
15
+ # end
16
+ #
17
+ # To modify middleware for the server, just call
18
+ # with another block:
19
+ #
20
+ # Sidekiq.configure_server do |config|
21
+ # config.server_middleware do |chain|
22
+ # chain.add MyServerHook
23
+ # chain.remove ActiveRecord
24
+ # end
25
+ # end
26
+ #
27
+ # To insert immediately preceding another entry:
28
+ #
29
+ # Sidekiq.configure_client do |config|
30
+ # config.client_middleware do |chain|
31
+ # chain.insert_before ActiveRecord, MyClientHook
32
+ # end
33
+ # end
34
+ #
35
+ # To insert immediately after another entry:
36
+ #
37
+ # Sidekiq.configure_client do |config|
38
+ # config.client_middleware do |chain|
39
+ # chain.insert_after ActiveRecord, MyClientHook
40
+ # end
41
+ # end
42
+ #
43
+ # This is an example of a minimal server middleware:
44
+ #
45
+ # class MyServerHook
46
+ # def call(worker_instance, msg, queue)
47
+ # puts "Before work"
48
+ # yield
49
+ # puts "After work"
50
+ # end
51
+ # end
52
+ #
53
+ # This is an example of a minimal client middleware, note
54
+ # the method must return the result or the job will not push
55
+ # to Redis:
56
+ #
57
+ # class MyClientHook
58
+ # def call(worker_class, msg, queue, redis_pool)
59
+ # puts "Before push"
60
+ # result = yield
61
+ # puts "After push"
62
+ # result
63
+ # end
64
+ # end
65
+ #
66
+ module Middleware
67
+ class Chain
68
+ include Enumerable
69
+ attr_reader :entries
70
+
71
+ def initialize_copy(copy)
72
+ copy.instance_variable_set(:@entries, entries.dup)
73
+ end
74
+
75
+ def each(&block)
76
+ entries.each(&block)
77
+ end
78
+
79
+ def initialize
80
+ @entries = []
81
+ yield self if block_given?
82
+ end
83
+
84
+ def remove(klass)
85
+ entries.delete_if { |entry| entry.klass == klass }
86
+ end
87
+
88
+ def add(klass, *args)
89
+ remove(klass) if exists?(klass)
90
+ entries << Entry.new(klass, *args)
91
+ end
92
+
93
+ def prepend(klass, *args)
94
+ remove(klass) if exists?(klass)
95
+ entries.insert(0, Entry.new(klass, *args))
96
+ end
97
+
98
+ def insert_before(oldklass, newklass, *args)
99
+ i = entries.index { |entry| entry.klass == newklass }
100
+ new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
101
+ i = entries.index { |entry| entry.klass == oldklass } || 0
102
+ entries.insert(i, new_entry)
103
+ end
104
+
105
+ def insert_after(oldklass, newklass, *args)
106
+ i = entries.index { |entry| entry.klass == newklass }
107
+ new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
108
+ i = entries.index { |entry| entry.klass == oldklass } || entries.count - 1
109
+ entries.insert(i+1, new_entry)
110
+ end
111
+
112
+ def exists?(klass)
113
+ any? { |entry| entry.klass == klass }
114
+ end
115
+
116
+ def retrieve
117
+ map(&:make_new)
118
+ end
119
+
120
+ def clear
121
+ entries.clear
122
+ end
123
+
124
+ def invoke(*args)
125
+ chain = retrieve.dup
126
+ traverse_chain = lambda do
127
+ if chain.empty?
128
+ yield
129
+ else
130
+ chain.shift.call(*args, &traverse_chain)
131
+ end
132
+ end
133
+ traverse_chain.call
134
+ end
135
+ end
136
+
137
+ class Entry
138
+ attr_reader :klass
139
+
140
+ def initialize(klass, *args)
141
+ @klass = klass
142
+ @args = args
143
+ end
144
+
145
+ def make_new
146
+ @klass.new(*@args)
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Simple middleware to save the current locale and restore it when the job executes.
4
+ # Use it by requiring it in your initializer:
5
+ #
6
+ # require 'sidekiq/middleware/i18n'
7
+ #
8
+ module Sidekiq::Middleware::I18n
9
+ # Get the current locale and store it in the message
10
+ # to be sent to Sidekiq.
11
+ class Client
12
+ def call(worker_class, msg, queue, redis_pool)
13
+ msg['locale'] ||= I18n.locale
14
+ yield
15
+ end
16
+ end
17
+
18
+ # Pull the msg locale out and set the current thread to use it.
19
+ class Server
20
+ def call(worker, msg, queue)
21
+ I18n.locale = msg['locale'] || I18n.default_locale
22
+ yield
23
+ ensure
24
+ I18n.locale = I18n.default_locale
25
+ end
26
+ end
27
+ end
28
+
29
+ Sidekiq.configure_client do |config|
30
+ config.client_middleware do |chain|
31
+ chain.add Sidekiq::Middleware::I18n::Client
32
+ end
33
+ end
34
+
35
+ Sidekiq.configure_server do |config|
36
+ config.client_middleware do |chain|
37
+ chain.add Sidekiq::Middleware::I18n::Client
38
+ end
39
+ config.server_middleware do |chain|
40
+ chain.add Sidekiq::Middleware::I18n::Server
41
+ end
42
+ end
@@ -0,0 +1,13 @@
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