kulesa-sidekiq 1.2.2
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.
- data/.gitignore +6 -0
- data/.rvmrc +4 -0
- data/COMM-LICENSE +83 -0
- data/Changes.md +207 -0
- data/Gemfile +12 -0
- data/LICENSE +22 -0
- data/README.md +61 -0
- data/Rakefile +9 -0
- data/bin/client +7 -0
- data/bin/sidekiq +14 -0
- data/bin/sidekiqctl +74 -0
- data/config.ru +18 -0
- data/examples/chef/cookbooks/sidekiq/README.rdoc +11 -0
- data/examples/chef/cookbooks/sidekiq/recipes/default.rb +55 -0
- data/examples/chef/cookbooks/sidekiq/templates/default/monitrc.conf.erb +8 -0
- data/examples/chef/cookbooks/sidekiq/templates/default/sidekiq.erb +219 -0
- data/examples/chef/cookbooks/sidekiq/templates/default/sidekiq.yml.erb +22 -0
- data/examples/clockwork.rb +44 -0
- data/examples/config.yml +10 -0
- data/examples/monitrc.conf +6 -0
- data/examples/por.rb +27 -0
- data/examples/scheduling.rb +37 -0
- data/examples/sinkiq.rb +59 -0
- data/examples/web-ui.png +0 -0
- data/lib/sidekiq.rb +98 -0
- data/lib/sidekiq/capistrano.rb +35 -0
- data/lib/sidekiq/cli.rb +214 -0
- data/lib/sidekiq/client.rb +72 -0
- data/lib/sidekiq/extensions/action_mailer.rb +26 -0
- data/lib/sidekiq/extensions/active_record.rb +27 -0
- data/lib/sidekiq/extensions/generic_proxy.rb +21 -0
- data/lib/sidekiq/fetch.rb +76 -0
- data/lib/sidekiq/logging.rb +46 -0
- data/lib/sidekiq/manager.rb +163 -0
- data/lib/sidekiq/middleware/chain.rb +96 -0
- data/lib/sidekiq/middleware/client/unique_jobs.rb +36 -0
- data/lib/sidekiq/middleware/server/active_record.rb +13 -0
- data/lib/sidekiq/middleware/server/exception_handler.rb +38 -0
- data/lib/sidekiq/middleware/server/failure_jobs.rb +25 -0
- data/lib/sidekiq/middleware/server/logging.rb +31 -0
- data/lib/sidekiq/middleware/server/retry_jobs.rb +69 -0
- data/lib/sidekiq/middleware/server/timeout.rb +21 -0
- data/lib/sidekiq/middleware/server/unique_jobs.rb +17 -0
- data/lib/sidekiq/processor.rb +92 -0
- data/lib/sidekiq/rails.rb +21 -0
- data/lib/sidekiq/redis_connection.rb +27 -0
- data/lib/sidekiq/retry.rb +59 -0
- data/lib/sidekiq/testing.rb +44 -0
- data/lib/sidekiq/testing/inline.rb +37 -0
- data/lib/sidekiq/util.rb +40 -0
- data/lib/sidekiq/version.rb +3 -0
- data/lib/sidekiq/web.rb +185 -0
- data/lib/sidekiq/worker.rb +62 -0
- data/lib/sidekiq/yaml_patch.rb +21 -0
- data/myapp/.gitignore +15 -0
- data/myapp/Capfile +5 -0
- data/myapp/Gemfile +19 -0
- data/myapp/Rakefile +7 -0
- data/myapp/app/controllers/application_controller.rb +3 -0
- data/myapp/app/controllers/work_controller.rb +38 -0
- data/myapp/app/helpers/application_helper.rb +2 -0
- data/myapp/app/mailers/.gitkeep +0 -0
- data/myapp/app/mailers/user_mailer.rb +9 -0
- data/myapp/app/models/.gitkeep +0 -0
- data/myapp/app/models/post.rb +5 -0
- data/myapp/app/views/layouts/application.html.erb +14 -0
- data/myapp/app/views/user_mailer/greetings.html.erb +3 -0
- data/myapp/app/views/work/index.html.erb +1 -0
- data/myapp/app/workers/hard_worker.rb +10 -0
- data/myapp/config.ru +4 -0
- data/myapp/config/application.rb +59 -0
- data/myapp/config/boot.rb +6 -0
- data/myapp/config/database.yml +25 -0
- data/myapp/config/deploy.rb +15 -0
- data/myapp/config/environment.rb +5 -0
- data/myapp/config/environments/development.rb +38 -0
- data/myapp/config/environments/production.rb +67 -0
- data/myapp/config/environments/test.rb +37 -0
- data/myapp/config/initializers/backtrace_silencers.rb +7 -0
- data/myapp/config/initializers/inflections.rb +15 -0
- data/myapp/config/initializers/mime_types.rb +5 -0
- data/myapp/config/initializers/secret_token.rb +7 -0
- data/myapp/config/initializers/session_store.rb +8 -0
- data/myapp/config/initializers/sidekiq.rb +6 -0
- data/myapp/config/initializers/wrap_parameters.rb +14 -0
- data/myapp/config/locales/en.yml +5 -0
- data/myapp/config/routes.rb +10 -0
- data/myapp/db/migrate/20120123214055_create_posts.rb +10 -0
- data/myapp/db/seeds.rb +7 -0
- data/myapp/lib/assets/.gitkeep +0 -0
- data/myapp/lib/tasks/.gitkeep +0 -0
- data/myapp/log/.gitkeep +0 -0
- data/myapp/script/rails +6 -0
- data/sidekiq.gemspec +27 -0
- data/test/config.yml +9 -0
- data/test/fake_env.rb +0 -0
- data/test/helper.rb +16 -0
- data/test/test_cli.rb +168 -0
- data/test/test_client.rb +119 -0
- data/test/test_extensions.rb +69 -0
- data/test/test_manager.rb +51 -0
- data/test/test_middleware.rb +92 -0
- data/test/test_processor.rb +32 -0
- data/test/test_retry.rb +125 -0
- data/test/test_stats.rb +68 -0
- data/test/test_testing.rb +97 -0
- data/test/test_testing_inline.rb +75 -0
- data/test/test_web.rb +122 -0
- data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
- data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
- data/web/assets/javascripts/application.js +20 -0
- data/web/assets/javascripts/vendor/bootstrap.js +12 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-alert.js +91 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-button.js +98 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-carousel.js +154 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-collapse.js +136 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-dropdown.js +92 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-modal.js +210 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-popover.js +95 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-scrollspy.js +125 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-tab.js +130 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-tooltip.js +270 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-transition.js +51 -0
- data/web/assets/javascripts/vendor/bootstrap/bootstrap-typeahead.js +271 -0
- data/web/assets/javascripts/vendor/jquery.js +9266 -0
- data/web/assets/javascripts/vendor/jquery.timeago.js +148 -0
- data/web/assets/stylesheets/application.css +27 -0
- data/web/assets/stylesheets/vendor/bootstrap-responsive.css +567 -0
- data/web/assets/stylesheets/vendor/bootstrap.css +3365 -0
- data/web/views/index.slim +48 -0
- data/web/views/layout.slim +26 -0
- data/web/views/queue.slim +11 -0
- data/web/views/retries.slim +29 -0
- data/web/views/retry.slim +52 -0
- metadata +371 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'sidekiq/extensions/generic_proxy'
|
|
2
|
+
|
|
3
|
+
module Sidekiq
|
|
4
|
+
module Extensions
|
|
5
|
+
##
|
|
6
|
+
# Adds a 'delay' method to ActionMailer to offload arbitrary email
|
|
7
|
+
# delivery to Sidekiq. Example:
|
|
8
|
+
#
|
|
9
|
+
# UserMailer.delay.send_welcome_email(new_user)
|
|
10
|
+
class DelayedMailer
|
|
11
|
+
include Sidekiq::Worker
|
|
12
|
+
|
|
13
|
+
def perform(yml)
|
|
14
|
+
(target, method_name, args) = YAML.load(yml)
|
|
15
|
+
target.send(method_name, *args).deliver
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module ActionMailer
|
|
20
|
+
def delay
|
|
21
|
+
Proxy.new(DelayedMailer, self)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'sidekiq/extensions/generic_proxy'
|
|
2
|
+
|
|
3
|
+
module Sidekiq
|
|
4
|
+
module Extensions
|
|
5
|
+
##
|
|
6
|
+
# Adds a 'delay' method to ActiveRecord to offload arbitrary method
|
|
7
|
+
# execution to Sidekiq. Examples:
|
|
8
|
+
#
|
|
9
|
+
# User.delay.delete_inactive
|
|
10
|
+
# User.recent_signups.each { |user| user.delay.mark_as_awesome }
|
|
11
|
+
class DelayedModel
|
|
12
|
+
include Sidekiq::Worker
|
|
13
|
+
|
|
14
|
+
def perform(yml)
|
|
15
|
+
(target, method_name, args) = YAML.load(yml)
|
|
16
|
+
target.send(method_name, *args)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module ActiveRecord
|
|
21
|
+
def delay
|
|
22
|
+
Proxy.new(DelayedModel, self)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Sidekiq
|
|
2
|
+
module Extensions
|
|
3
|
+
class Proxy < (RUBY_VERSION < '1.9' ? Object : BasicObject)
|
|
4
|
+
def initialize(performable, target)
|
|
5
|
+
@performable = performable
|
|
6
|
+
@target = target
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def method_missing(name, *args)
|
|
10
|
+
# Sidekiq has a limitation in that its message must be JSON.
|
|
11
|
+
# JSON can't round trip real Ruby objects so we use YAML to
|
|
12
|
+
# serialize the objects to a String. The YAML will be converted
|
|
13
|
+
# to JSON and then deserialized on the other side back into a
|
|
14
|
+
# Ruby object.
|
|
15
|
+
obj = [@target, name, args]
|
|
16
|
+
@performable.perform_async(::YAML.dump(obj))
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require 'sidekiq'
|
|
2
|
+
require 'celluloid'
|
|
3
|
+
|
|
4
|
+
module Sidekiq
|
|
5
|
+
##
|
|
6
|
+
# The Fetcher blocks on Redis, waiting for a message to process
|
|
7
|
+
# from the queues. It gets the message and hands it to the Manager
|
|
8
|
+
# to assign to a ready Processor.
|
|
9
|
+
class Fetcher
|
|
10
|
+
include Celluloid
|
|
11
|
+
include Sidekiq::Util
|
|
12
|
+
|
|
13
|
+
TIMEOUT = 1
|
|
14
|
+
|
|
15
|
+
def initialize(mgr, queues)
|
|
16
|
+
@mgr = mgr
|
|
17
|
+
@queues = queues.map { |q| "queue:#{q}" }
|
|
18
|
+
@unique_queues = @queues.uniq
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Fetching is straightforward: the Manager makes a fetch
|
|
22
|
+
# request for each idle processor when Sidekiq starts and
|
|
23
|
+
# then issues a new fetch request every time a Processor
|
|
24
|
+
# finishes a message.
|
|
25
|
+
#
|
|
26
|
+
# Because we have to shut down cleanly, we can't block
|
|
27
|
+
# forever and we can't loop forever. Instead we reschedule
|
|
28
|
+
# a new fetch if the current fetch turned up nothing.
|
|
29
|
+
def fetch
|
|
30
|
+
watchdog('Fetcher#fetch died') do
|
|
31
|
+
return if Sidekiq::Fetcher.done?
|
|
32
|
+
|
|
33
|
+
begin
|
|
34
|
+
queue = nil
|
|
35
|
+
msg = nil
|
|
36
|
+
Sidekiq.redis { |conn| queue, msg = conn.blpop(*queues_cmd) }
|
|
37
|
+
|
|
38
|
+
if msg
|
|
39
|
+
@mgr.assign!(msg, queue.gsub(/.*queue:/, ''))
|
|
40
|
+
else
|
|
41
|
+
after(0) { fetch }
|
|
42
|
+
end
|
|
43
|
+
rescue => ex
|
|
44
|
+
logger.error("Error fetching message: #{ex}")
|
|
45
|
+
logger.error(ex.backtrace.first)
|
|
46
|
+
sleep(TIMEOUT)
|
|
47
|
+
after(0) { fetch }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Ugh. Say hello to a bloody hack.
|
|
53
|
+
# Can't find a clean way to get the fetcher to just stop processing
|
|
54
|
+
# its mailbox when shutdown starts.
|
|
55
|
+
def self.done!
|
|
56
|
+
@done = true
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.done?
|
|
60
|
+
@done
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
# Creating the Redis#blpop command takes into account any
|
|
66
|
+
# configured queue weights. By default Redis#blpop returns
|
|
67
|
+
# data from the first queue that has pending elements. We
|
|
68
|
+
# recreate the queue command each time we invoke Redis#blpop
|
|
69
|
+
# to honor weights and avoid queue starvation.
|
|
70
|
+
def queues_cmd
|
|
71
|
+
queues = @queues.sample(@unique_queues.size).uniq
|
|
72
|
+
queues.concat(@unique_queues - queues)
|
|
73
|
+
queues << TIMEOUT
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
require 'logger'
|
|
3
|
+
|
|
4
|
+
module Sidekiq
|
|
5
|
+
module Logging
|
|
6
|
+
|
|
7
|
+
class Pretty < Logger::Formatter
|
|
8
|
+
# Provide a call() method that returns the formatted message.
|
|
9
|
+
def call(severity, time, program_name, message)
|
|
10
|
+
"#{time.utc.iso8601} #{Process.pid} TID-#{Thread.current.object_id.to_s(36)}#{context} #{severity}: #{message}\n"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def context
|
|
14
|
+
c = Thread.current[:sidekiq_context]
|
|
15
|
+
c ? " #{c}" : ''
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.with_context(msg)
|
|
20
|
+
begin
|
|
21
|
+
Thread.current[:sidekiq_context] = msg
|
|
22
|
+
yield
|
|
23
|
+
ensure
|
|
24
|
+
Thread.current[:sidekiq_context] = nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.logger
|
|
29
|
+
@logger ||= begin
|
|
30
|
+
log = Logger.new(STDOUT)
|
|
31
|
+
log.level = Logger::INFO
|
|
32
|
+
log.formatter = Pretty.new
|
|
33
|
+
log
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.logger=(log)
|
|
38
|
+
@logger = (log ? log : Logger.new('/dev/null'))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def logger
|
|
42
|
+
Sidekiq::Logging.logger
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
require 'celluloid'
|
|
2
|
+
require 'multi_json'
|
|
3
|
+
|
|
4
|
+
require 'sidekiq/util'
|
|
5
|
+
require 'sidekiq/processor'
|
|
6
|
+
require 'sidekiq/fetch'
|
|
7
|
+
|
|
8
|
+
module Sidekiq
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# The main router in the system. This
|
|
12
|
+
# manages the processor state and accepts messages
|
|
13
|
+
# from Redis to be dispatched to an idle processor.
|
|
14
|
+
#
|
|
15
|
+
class Manager
|
|
16
|
+
include Util
|
|
17
|
+
include Celluloid
|
|
18
|
+
|
|
19
|
+
trap_exit :processor_died
|
|
20
|
+
|
|
21
|
+
def initialize(options={})
|
|
22
|
+
logger.info "Booting sidekiq #{Sidekiq::VERSION} with Redis at #{redis {|x| x.client.id}}"
|
|
23
|
+
logger.info "Running in #{RUBY_DESCRIPTION}"
|
|
24
|
+
logger.debug { options.inspect }
|
|
25
|
+
@count = options[:concurrency] || 25
|
|
26
|
+
@done_callback = nil
|
|
27
|
+
|
|
28
|
+
@in_progress = {}
|
|
29
|
+
@done = false
|
|
30
|
+
@busy = []
|
|
31
|
+
@fetcher = Fetcher.new(current_actor, options[:queues])
|
|
32
|
+
@ready = @count.times.map { Processor.new_link(current_actor) }
|
|
33
|
+
procline
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def stop(options={})
|
|
37
|
+
watchdog('Manager#stop died') do
|
|
38
|
+
shutdown = options[:shutdown]
|
|
39
|
+
timeout = options[:timeout]
|
|
40
|
+
|
|
41
|
+
@done = true
|
|
42
|
+
Sidekiq::Fetcher.done!
|
|
43
|
+
@fetcher.terminate! if @fetcher.alive?
|
|
44
|
+
|
|
45
|
+
logger.info { "Shutting down #{@ready.size} quiet workers" }
|
|
46
|
+
@ready.each { |x| x.terminate if x.alive? }
|
|
47
|
+
@ready.clear
|
|
48
|
+
|
|
49
|
+
logger.debug { "Clearing workers in redis" }
|
|
50
|
+
Sidekiq.redis do |conn|
|
|
51
|
+
workers = conn.smembers('workers')
|
|
52
|
+
workers.each do |name|
|
|
53
|
+
conn.srem('workers', name) if name =~ /:#{process_id}-/
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
return after(0) { signal(:shutdown) } if @busy.empty?
|
|
58
|
+
logger.info { "Pausing up to #{timeout} seconds to allow workers to finish..." }
|
|
59
|
+
hard_shutdown_in timeout if shutdown
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def start
|
|
64
|
+
@ready.each { dispatch }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def when_done(&blk)
|
|
68
|
+
@done_callback = blk
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def processor_done(processor)
|
|
72
|
+
watchdog('Manager#processor_done died') do
|
|
73
|
+
@done_callback.call(processor) if @done_callback
|
|
74
|
+
@in_progress.delete(processor.object_id)
|
|
75
|
+
@busy.delete(processor)
|
|
76
|
+
if stopped?
|
|
77
|
+
processor.terminate if processor.alive?
|
|
78
|
+
signal(:shutdown) if @busy.empty?
|
|
79
|
+
else
|
|
80
|
+
@ready << processor if processor.alive?
|
|
81
|
+
end
|
|
82
|
+
dispatch
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def processor_died(processor, reason)
|
|
87
|
+
watchdog("Manager#processor_died died") do
|
|
88
|
+
@in_progress.delete(processor.object_id)
|
|
89
|
+
@busy.delete(processor)
|
|
90
|
+
|
|
91
|
+
unless stopped?
|
|
92
|
+
@ready << Processor.new_link(current_actor)
|
|
93
|
+
dispatch
|
|
94
|
+
else
|
|
95
|
+
signal(:shutdown) if @busy.empty?
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def assign(msg, queue)
|
|
101
|
+
watchdog("Manager#assign died") do
|
|
102
|
+
if stopped?
|
|
103
|
+
# Race condition between Manager#stop if Fetcher
|
|
104
|
+
# is blocked on redis and gets a message after
|
|
105
|
+
# all the ready Processors have been stopped.
|
|
106
|
+
# Push the message back to redis.
|
|
107
|
+
Sidekiq.redis do |conn|
|
|
108
|
+
conn.lpush("queue:#{queue}", msg)
|
|
109
|
+
end
|
|
110
|
+
else
|
|
111
|
+
processor = @ready.pop
|
|
112
|
+
@in_progress[processor.object_id] = [msg, queue]
|
|
113
|
+
@busy << processor
|
|
114
|
+
processor.process!(Sidekiq.load_json(msg), queue)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def hard_shutdown_in(delay)
|
|
122
|
+
after(delay) do
|
|
123
|
+
watchdog("Manager#watch_for_shutdown died") do
|
|
124
|
+
# We've reached the timeout and we still have busy workers.
|
|
125
|
+
# They must die but their messages shall live on.
|
|
126
|
+
logger.info("Still waiting for #{@busy.size} busy workers")
|
|
127
|
+
|
|
128
|
+
Sidekiq.redis do |conn|
|
|
129
|
+
@busy.each do |processor|
|
|
130
|
+
# processor is an actor proxy and we can't call any methods
|
|
131
|
+
# that would go to the actor (since it's busy). Instead
|
|
132
|
+
# we'll use the object_id to track the worker's data here.
|
|
133
|
+
msg, queue = @in_progress[processor.object_id]
|
|
134
|
+
conn.lpush("queue:#{queue}", msg)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
logger.info("Pushed #{@busy.size} messages back to Redis")
|
|
138
|
+
|
|
139
|
+
after(0) { signal(:shutdown) }
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def dispatch
|
|
145
|
+
return if stopped?
|
|
146
|
+
# This is a safety check to ensure we haven't leaked
|
|
147
|
+
# processors somehow.
|
|
148
|
+
raise "BUG: No processors, cannot continue!" if @ready.empty? && @busy.empty?
|
|
149
|
+
raise "No ready processor!?" if @ready.empty?
|
|
150
|
+
|
|
151
|
+
@fetcher.fetch!
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def stopped?
|
|
155
|
+
@done
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def procline
|
|
159
|
+
$0 = "sidekiq #{Sidekiq::VERSION} [#{@busy.size} of #{@count} busy]#{stopped? ? ' stopping' : ''}"
|
|
160
|
+
after(5) { procline }
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
module Sidekiq
|
|
2
|
+
# Middleware is code configured to run before/after
|
|
3
|
+
# a message is processed. It is patterned after Rack
|
|
4
|
+
# middleware. Middleware exists for the client side
|
|
5
|
+
# (pushing jobs onto the queue) as well as the server
|
|
6
|
+
# side (when jobs are actually processed).
|
|
7
|
+
#
|
|
8
|
+
# To add middleware for the client:
|
|
9
|
+
#
|
|
10
|
+
# Sidekiq.client_middleware do |chain|
|
|
11
|
+
# chain.add MyClientHook
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# To modify middleware for the server, just call
|
|
15
|
+
# with another block:
|
|
16
|
+
#
|
|
17
|
+
# Sidekiq.server_middleware do |chain|
|
|
18
|
+
# chain.add MyServerHook
|
|
19
|
+
# chain.remove ActiveRecord
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# This is an example of a minimal server middleware:
|
|
23
|
+
#
|
|
24
|
+
# class MyServerHook
|
|
25
|
+
# def call(worker, msg, queue)
|
|
26
|
+
# puts "Before work"
|
|
27
|
+
# yield
|
|
28
|
+
# puts "After work"
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# This is an example of a minimal client middleware:
|
|
33
|
+
#
|
|
34
|
+
# class MyClientHook
|
|
35
|
+
# def call(msg, queue)
|
|
36
|
+
# puts "Before push"
|
|
37
|
+
# yield
|
|
38
|
+
# puts "After push"
|
|
39
|
+
# end
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
module Middleware
|
|
43
|
+
class Chain
|
|
44
|
+
attr_reader :entries
|
|
45
|
+
|
|
46
|
+
def initialize
|
|
47
|
+
@entries = []
|
|
48
|
+
yield self if block_given?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def remove(klass)
|
|
52
|
+
entries.delete_if { |entry| entry.klass == klass }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def add(klass, *args)
|
|
56
|
+
entries << Entry.new(klass, *args) unless exists?(klass)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def exists?(klass)
|
|
60
|
+
entries.any? { |entry| entry.klass == klass }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def retrieve
|
|
64
|
+
entries.map(&:make_new)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def clear
|
|
68
|
+
entries.clear
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def invoke(*args, &final_action)
|
|
72
|
+
chain = retrieve.dup
|
|
73
|
+
traverse_chain = lambda do
|
|
74
|
+
if chain.empty?
|
|
75
|
+
final_action.call
|
|
76
|
+
else
|
|
77
|
+
chain.shift.call(*args, &traverse_chain)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
traverse_chain.call
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
class Entry
|
|
85
|
+
attr_reader :klass
|
|
86
|
+
def initialize(klass, *args)
|
|
87
|
+
@klass = klass
|
|
88
|
+
@args = args
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def make_new
|
|
92
|
+
@klass.new(*@args)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|