1
0
Fork 0
mirror of https://github.com/mperham/sidekiq.git synced 2022-11-09 13:52:34 -05:00
mperham--sidekiq/docs/component.md
Mike Perham 67daa7a408
Prepare for upcoming Sidekiq::Config redesign (#5340)
* Prepare for upcoming Sidekiq::Config redesign

Adjust the server internals to use a config object rather than refering directly to the Sidekiq module.
2022-05-31 13:37:31 -07:00

2.4 KiB

Sidekiq 7.0 Components

Sidekiq 7.0 contains the largest internal refactoring since Sidekiq 4.0. This refactoring is designed to improve deployment flexibility and allow new use cases.

The Problem

Sidekiq today uses a large number of class-level methods to access things like the Redis connection pool, the logger, and process configuration, e.g.

Sidekiq.logger.info "Hello world"
Sidekiq.redis {|c| c.sadd("some_set", "new_member") }
Sidekiq.configure_server {|config| config... }

The problem is that this pattern implies a global, mutable singleton. It does not work with Ractors. It does not allow multiple instances in one process. It does not allow embedding within another Ruby process (e.g. puma). Today the only supported Sidekiq deployment pattern is running bundle exec sidekiq.

The Solution

Sidekiq 7.0 aims to refactor Sidekiq internals to allow more flexibility in how Sidekiq can be used.

Sidekiq::Config

Sidekiq::Config represents the configuration for an instance of Sidekiq. Sidekiq::CLI creates a Sidekiq::Config instance and mutates it according to the command line parameters and the data in config/sidekiq.yml.

Sidekiq::Launcher is the top-level component which takes a Sidekiq::Config and creates the tree of runtime components. Once passed to Launcher, the Config is frozen and immutable.

Every internal component of Sidekiq takes a Sidekiq::Config instance and uses it. The Config holds previously "global" state like the connection pool, error handlers, lifecycle callbacks, etc.

Sidekiq::Component

Sidekiq::Component is a module which provides helpful methods based on a config reader:

module Sidekiq::Component
  def config
    @config
  end

  def redis(&block)
    config.redis(&block)
  end

  def logger
    config.logger
  end

  def handle_exception(ex, ctx)
    # avoids calling `Sidekiq.error_handlers...`
    config.handle_exception(ex, ctx)
  end
end

class Sidekiq::Processor
  include Sidekiq::Component

  def initialize(config)
    @config = config
  end

  def ...
    # old
    Sidekiq.redis {|c| ... }
    Sidekiq.logger.info "Hello world!"
    # new
    redis {|c| ... }
    logger.info "Hello world!"
  rescue => ex
    handle_exception(ex, ...)
  end
end

With this pattern, we greatly reduce the use of global APIs throughout Sidekiq internals. Where beforefore we'd call Sidekiq.xyz, we instead provide similar functionality like config.xyz.