mirror of
https://github.com/mperham/sidekiq.git
synced 2022-11-09 13:52:34 -05:00
106 lines
2.9 KiB
Ruby
106 lines
2.9 KiB
Ruby
|
require "sidekiq/component"
|
||
|
require "sidekiq/fetch"
|
||
|
|
||
|
module Sidekiq
|
||
|
# A Sidekiq::Capsule is the set of resources necessary to
|
||
|
# process one or more queues with a given concurrency.
|
||
|
# One "default" Capsule is started but the user may declare additional
|
||
|
# Capsules in the initializer.
|
||
|
#
|
||
|
# To process a "single" queue with one thread so jobs are processed
|
||
|
# serially, you can do this:
|
||
|
#
|
||
|
# Sidekiq.configure_server do |config|
|
||
|
# config.capsule("single-threaded") do |cap|
|
||
|
# cap.concurrency = 1
|
||
|
# cap.queues = %w(single)
|
||
|
# end
|
||
|
# end
|
||
|
class Capsule
|
||
|
include Sidekiq::Component
|
||
|
|
||
|
attr_reader :name
|
||
|
attr_reader :queues
|
||
|
attr_reader :strict
|
||
|
attr_accessor :concurrency
|
||
|
attr_accessor :fetch_class
|
||
|
|
||
|
def initialize(name, config)
|
||
|
@name = name
|
||
|
@config = config
|
||
|
@queues = ["default"]
|
||
|
@concurrency = 10
|
||
|
@strict = true
|
||
|
@fetch_class = Sidekiq::BasicFetch
|
||
|
end
|
||
|
|
||
|
def fetcher
|
||
|
@fetcher ||= fetch_class.new(self)
|
||
|
end
|
||
|
|
||
|
def stop
|
||
|
fetcher&.bulk_requeue([], nil)
|
||
|
end
|
||
|
|
||
|
def queues=(val)
|
||
|
@strict = true
|
||
|
@queues = Array(val).each_with_object([]) do |qstr, memo|
|
||
|
arr = qstr
|
||
|
arr = qstr.split(",") if qstr.is_a?(String)
|
||
|
name, weight = arr
|
||
|
@strict = false if weight.to_i > 0
|
||
|
[weight.to_i, 1].max.times do
|
||
|
memo << name
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Allow the middleware to be different per-capsule.
|
||
|
# Avoid if possible and add middleware globally so all
|
||
|
# capsules share the same chains. Easier to debug that way.
|
||
|
def client_middleware
|
||
|
@client_chain ||= config.client_middleware.dup
|
||
|
yield @client_chain if block_given?
|
||
|
@client_chain
|
||
|
end
|
||
|
|
||
|
def server_middleware
|
||
|
@server_chain ||= config.server_middleware.dup
|
||
|
yield @server_chain if block_given?
|
||
|
@server_chain
|
||
|
end
|
||
|
|
||
|
def redis_pool
|
||
|
# connection pool is lazy, it will not create connections unless you actually need them
|
||
|
# so don't be skimpy!
|
||
|
@redis ||= config.new_redis_pool(@concurrency)
|
||
|
end
|
||
|
|
||
|
def redis
|
||
|
raise ArgumentError, "requires a block" unless block_given?
|
||
|
redis_pool.with do |conn|
|
||
|
retryable = true
|
||
|
begin
|
||
|
yield conn
|
||
|
rescue RedisClientAdapter::BaseError => ex
|
||
|
# 2550 Failover can cause the server to become a replica, need
|
||
|
# to disconnect and reopen the socket to get back to the primary.
|
||
|
# 4495 Use the same logic if we have a "Not enough replicas" error from the primary
|
||
|
# 4985 Use the same logic when a blocking command is force-unblocked
|
||
|
# The same retry logic is also used in client.rb
|
||
|
if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
|
||
|
conn.close
|
||
|
retryable = false
|
||
|
retry
|
||
|
end
|
||
|
raise
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def logger
|
||
|
config.logger
|
||
|
end
|
||
|
end
|
||
|
end
|