3.7 KiB
Sidekiq Internals
This document explains Sidekiq 7.0 internal code structure and implementation.
bundle exec sidekiq
When you start a Sidekiq instance using bundle exec sidekiq
, execution starts in bin/sidekiq
.
This executable creates an instance of Sidekiq::CLI
and runs it.
Sidekiq::CLI
has three main responsibilities:
- parse any command line args, ENV args or configuration file (e.g.
config/sidekiq.yml
), put that data into aSidekiq::Config
instance atSidekiq.default_configuration
- boot any Rails application or script specified via
-r
- trap any process signals and handle them (TERM, TTIN, TSTP)
Once booted, Sidekiq::CLI creates an instance of Sidekiq::Launcher
for the Sidekiq::Config
instance.
Sidekiq::Launcher
has several responsibilities:
- provide a heartbeat thread which informs the Web UI that we are alive and well
- provide a scheduler thread which enqueues jobs which have reached their scheduled time and can be enqueued for immediate execution
- create a
Sidekiq::Manager
for each configuredSidekiq::Capsule
.
Sidekiq::Capsule
represents resources necessary to process one or more queues. The user
configures a set of queues along with the specified concurrency. Each capsule will have a Sidekiq::BasicFetch
instance.
Sidekiq::Manager
manages the resources for a given Sidekiq::Capsule
. It creates and starts N Sidekiq::Processor
instances. It listens for any errors reported by a Processor thread. If a Processor reports an error, the Manager will discard that Processor and spin up a new one, to ensure there is no cross-contamination when executing future jobs. Finally, the Manager is responsible for cleanly shutting down all the Processor threads at shutdown.
Each Sidekiq::Processor
instance is a separate thread. A Processor thread knows how to fetch a job from Redis, run any configured middleware for its Capsule and then execute the job. A special
component in the execution path, Sidekiq::JobRetry
, catches any Exception from this block and moves the job plus the error information into the retry
set in Redis for future retries if necessary.
embedded
Sidekiq 7.0's new embedded mode of operation allows the user to run a Sidekiq instance within an arbitrary Ruby process. Sidekiq::Embedded
takes the place of Sidekiq::CLI and allows the user to create, configure and manage their own Sidekiq::Launcher
instance.
As noted above, when embedded, Sidekiq expects the process starter to take Sidekiq::CLI
's responsibilities.
For instance, within puma
we expect puma to parse command line arguments, boot the app, handle signals, etc.
Your Ruby code creates and configures the embedded Sidekiq instance.
The standard Ruby runtime does not scale well with lots of Threads so embedded mode defaults to a concurrency of only 2 as it needs to share that limited pool of Threads with the process owner, often a library like puma
running CPU-hungry Rails apps. If your puma thread count is 5, I would not touch Sidekiq's default concurrency of 2. Keep watch on your CPU usage and tune puma's threads X, Y
value or Sidekiq's config.concurrency = N
so that your Ruby processes aren't maxxed out of CPU. I would never recommend more than 10 threads executing application code per core unless you've specifically tested and know your app to be CPU-light.
RULE OF THUMB: if you are using embedded mode, you should be monitoring the CPU usage of that process!
Note the other Threads enumerated above. The concurrency value controls how many Processor threads will be started but there are also separate threads for heartbeat, scheduler, and other internal services. These threads are relatively CPU-light but they still need regular, predictable access to the CPU for their own work.