merge 6.5

This commit is contained in:
Mike Perham 2022-06-09 14:06:33 -07:00
commit 2ee7e27697
89 changed files with 1199 additions and 830 deletions

View File

@ -90,6 +90,11 @@ rails s
git checkout -b new_feature_name
```
### 11. Keep your forked branch up to date with changes in main repo
```
git pull upstream main
```
## Legal
By submitting a Pull Request, you disavow any rights or claims to any changes

6
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View File

@ -6,6 +6,9 @@ on:
pull_request:
branches: [ "main", "7-0" ]
permissions:
contents: read
jobs:
test:
@ -27,7 +30,7 @@ jobs:
- 6379:6379
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Ruby
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
# change this to (see https://github.com/ruby/setup-ruby#versioning):

14
.github/workflows/depsreview.yaml vendored Normal file
View File

@ -0,0 +1,14 @@
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v3
- name: 'Dependency Review'
uses: actions/dependency-review-action@v1

1
.gitignore vendored
View File

@ -16,3 +16,4 @@ development.log
/Makefile
/docker-compose.yml
Gemfile.lock
*.DS_Store

View File

@ -2,12 +2,12 @@ ruby_version: 2.7.0
fix: true
parallel: true
ignore:
- test/**/*
- examples/**/*
- myapp/**/*
- 'lib/sidekiq.rb':
- Lint/InheritException
- 'lib/**/*':
- '**/*':
- Lint/RescueException
- Security/YAMLLoad
- Style/GlobalVars
- 'test/test*.rb':
- Lint/ConstantDefinitionInBlock

View File

@ -2,20 +2,22 @@
[Sidekiq Changes](https://github.com/mperham/sidekiq/blob/main/Changes.md) | [Sidekiq Pro Changes](https://github.com/mperham/sidekiq/blob/main/Pro-Changes.md) | [Sidekiq Enterprise Changes](https://github.com/mperham/sidekiq/blob/main/Ent-Changes.md)
HEAD
6.5.0
---------
- Add **beta** support for the `redis-client` gem**. This will become the default Redis driver in Sidekiq 7.0. [#5298]
- Substantial refactoring of Sidekiq server internals, part of a larger effort
to reduce Sidekiq's internal usage of global methods and data, see [docs/global_to_local.md](docs/global_to_local.md) and [docs/middleware.md](docs/middleware.md).
- **Add beta support for the `redis-client` gem**. This will become the default Redis driver in Sidekiq 7.0. [#5298]
Read more: https://github.com/mperham/sidekiq/wiki/Using-redis-client
- Add **beta** support for DB transaction-aware client [#5291]
- **Add beta support for DB transaction-aware client** [#5291]
Add this line to your initializer and any jobs created during a transaction
will only be pushed to Redis **after the transaction commits**. You will need to add the
`after_commit_everywhere` gem to your Gemfile.
```ruby
Sidekiq.transactional_push!
```
This feature is still beta quality; please try it out and let us know if you
have any issues. It will be fully supported in Sidekiq 7.0 or removed if it
This feature does not have a lot of production usage yet; please try it out and let us
know if you have any issues. It will be fully supported in Sidekiq 7.0 or removed if it
proves problematic.
- Fix regression with middleware arguments [#5312]

View File

@ -4,6 +4,14 @@
Please see [sidekiq.org](https://sidekiq.org) for more details and how to buy.
2.5.0
-------------
- Per the 2.0 upgrade notes, Sidekiq Enterprise will stop if you do not have valid
credentials configured on startup.
- Internal refactoring for Sidekiq 6.5.
- Requires Sidekiq 6.5, Pro 5.5.
2.3.1
-------------

View File

@ -4,7 +4,7 @@ gemspec
gem "rake"
gem "rails"
gem "redis-client", github: "redis-rb/redis-client"
gem "redis-client"
# Required for Ruby 3.1
# https://github.com/mikel/mail/pull/1439
@ -15,6 +15,7 @@ gem "net-pop"
gem "sqlite3", platforms: :ruby
gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
gem "after_commit_everywhere"
gem "yard"
group :test do
gem "minitest"

View File

@ -4,7 +4,7 @@
Please see [sidekiq.org](https://sidekiq.org/) for more details and how to buy.
HEAD
5.5.0
---------
- DEPRECATION: remove support for statsd-ruby via `Sidekiq::Pro.statsd`.
@ -20,6 +20,8 @@ end
- Excise "worker" terminology from codebase [#4955]
- Ensure batch callback metrics are always fired [#5217]
- Added `error_type` tag for `job.failures` metrics [#5211]
- Internal refactoring for Sidekiq 6.5.
- Requires Sidekiq 6.5.
5.3.1
---------

View File

@ -1,6 +1,16 @@
require "bundler/gem_tasks"
require "rake/testtask"
require "standard/rake"
require "rdoc/task"
RDoc::Task.new do |rdoc|
rdoc.main = "docs/rdoc.rdoc"
rdoc.rdoc_files.include("docs/rdoc.rdoc",
"lib/sidekiq/api.rb",
"lib/sidekiq/client.rb",
"lib/sidekiq/worker.rb",
"lib/sidekiq/job.rb")
end
Rake::TestTask.new(:test) do |test|
test.warning = true

View File

@ -52,7 +52,7 @@ Both Heroku and ECS now use 30 second shutdown timeout
by default and we want Sidekiq to take advantage of this time. If you
have deployment scripts which depend on the old default timeout, use `-t 8` to
get the old behavior. [#3968]
* **Rails <5** is no longer supported. Rails 6+ only works in zeitwerk mode.
* **Rails <5** is no longer supported.
* **Ruby <2.5** is no longer supported.
* **Redis <4** is no longer supported.

86
docs/component.md Normal file
View File

@ -0,0 +1,86 @@
# 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.
```ruby
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:
```ruby
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`.

56
docs/global_to_local.md Normal file
View File

@ -0,0 +1,56 @@
# Reducing Global API usage in Sidekiq 7.0
In Sidekiq 6.x, we rely heavily on top-level APIs and options within the Sidekiq module itself. APIs like `Sidekiq.options` are used everywhere to pull dynamic configuration.
This makes Sidekiq incompatible with Ractors or embedding within other processes.
# Implicit Configuration
Since the beginning, Sidekiq has used a global singleton for configuration. This was
accessed via `Sidekiq.configure_{client,server} do |config|`. You don't create a Sidekiq
instance but one is implicitly provided for you to configure.
Moving forward we want move to a more explicit `Sidekiq::Config` API which encapsulates this data. We provide backwards compatibility for the most widely used patterns but a few things
will need to change:
```ruby
# In 6.x `config` points to the Sidekiq module directly.
# In 7.x it will be an instance of Sidekiq::Config.
Sidekiq.configure_server do |config|
config.options[:concurrency] = 5 # Sidekiq.options is deprecated
config.concurrency = 5 # new
end
```
To be clear: `Sidekiq.configure_{client,server}` will remain supported for the
foreseeable future. How Sidekiq works by default will remain very, very similar but these
small tweaks will allow a new mode of operation for Sidekiq and unlock a few, new usecases...
# Explicit Configuration
Sidekiq 7.0 is expected to provide a new Ruby API for configuring and launching Sidekiq
instances within an existing Ruby process. For instance, you could launch Sidekiq
within a Puma process if you wanted to minimize the memory required for two separate
processes (of course, the normal GIL caveats apply: one core can only handle so many requests and jobs). I call this **embedded mode**.
Another idea: you could start a second, small instance within your main Sidekiq process
to limit concurrent job execution for a special queue. Maybe those jobs aren't thread-safe
or use a 3rd party service which imposes a very limited access policy.
```ruby
cfg = Sidekiq::Config.new
cfg.concurrency = 1
cfg.queues = %w[limited]
launcher = Sidekiq::Launcher.new(cfg) # cfg is now frozen!
launcher.start
# processing jobs!
launcher.stop
```
## Notes
- Every `Sidekiq::Launcher` instance is considered a separate "process" in the Web UI. You
could launch N instances within one Ruby process and the Web UI will show N "processes". Why you'd want to do this, I don't know. "Just because you can doesn't mean you should" remains true.
- How this configuration refactoring affects Sidekiq Pro and Sidekiq Enterprise remains to be
seen. Existing functionality will remain supported and any breakage will be
documented in the major version upgrade notes.

87
docs/middleware.md Normal file
View File

@ -0,0 +1,87 @@
# Middleware Changes in Sidekiq 7.0
With the internal refactoring coming in Sidekiq 7.0 it is necessary
to make minor changes to the Middleware API.
> tl;dr - middleware should now include Sidekiq::ClientMiddleware or Sidekiq::ServerMiddleware.
Currently the middleware API looks like this:
## Existing Client API
Client middleware is run when pushing a job to Redis.
```ruby
class Client
def initialize(optional_args)
@args = optional_args
end
def call(worker, job, queue, redis_pool)
yield
end
end
Sidekiq.configure_client do |config|
config.client_middleware do |chain|
chain.add Client, optional_args
end
end
```
## Server
Server middleware is run around job execution.
```ruby
class Server
def initialize(optional_args)
@args = optional_args
end
def call(worker, job, queue)
Sidekiq.redis {|c| c.do_something }
Sidekiq.logger.info { "Some message" }
yield
end
end
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add Server, optional_args
end
end
```
## Updated API
The updated middleware API requires the middleware class to include
a helper module.
```ruby
class Client
include Sidekiq::ClientMiddleware
def initialize(optional_args)
@args = optional_args
end
def call(worker, job, queue, redis_pool)
yield
end
end
```
```ruby
class Server
include Sidekiq::ServerMiddleware
def initialize(optional_args)
@args = optional_args
end
def call(worker, job, queue)
# note we no longer need to use the global Sidekiq module
# to access Redis and the logger
redis {|c| c.do_something }
logger.info { "Some message" }
yield
end
end
```

View File

@ -1,4 +1,4 @@
require 'sidekiq'
require "sidekiq"
# Start up sidekiq via
# ./bin/sidekiq -r ./examples/por.rb
@ -10,7 +10,7 @@ require 'sidekiq'
class PlainOldRuby
include Sidekiq::Job
def perform(how_hard="super hard", how_long=1)
def perform(how_hard = "super hard", how_long = 1)
sleep how_long
puts "Workin' #{how_hard}"
end

View File

@ -5,6 +5,7 @@ fail "Sidekiq #{Sidekiq::VERSION} does not support Ruby versions below 2.7.0." i
require "sidekiq/logger"
require "sidekiq/client"
require "sidekiq/transaction_aware_client"
require "sidekiq/job"
require "sidekiq/worker_compatibility_alias"
require "sidekiq/redis_client_adapter"
@ -51,19 +52,79 @@ module Sidekiq
puts "Take a deep breath and count to ten..."
end
# config.concurrency = 5
def self.concurrency=(val)
self[:concurrency] = Integer(val)
end
# config.queues = %w( high default low ) # strict
# config.queues = %w( high,3 default,2 low,1 ) # weighted
# config.queues = %w( feature1,1 feature2,1 feature3,1 ) # random
#
# With weighted priority, queue will be checked first (weight / total) of the time.
# high will be checked first (3/6) or 50% of the time.
# I'd recommend setting weights between 1-10. Weights in the hundreds or thousands
# are ridiculous and unnecessarily expensive. You can get random queue ordering
# by explicitly setting all weights to 1.
def self.queues=(val)
self[:queues] = Array(val).each_with_object([]) do |qstr, memo|
name, weight = qstr.split(",")
self[:strict] = false if weight.to_i > 0
[weight.to_i, 1].max.times do
memo << name
end
end
end
### Private APIs
def self.default_error_handler(ex, ctx)
logger.warn(dump_json(ctx)) unless ctx.empty?
logger.warn("#{ex.class.name}: #{ex.message}")
logger.warn(ex.backtrace.join("\n")) unless ex.backtrace.nil?
end
@config = DEFAULTS.dup
def self.options
@options ||= DEFAULTS.dup
logger.warn "`config.options[:key] = value` is deprecated, use `config[:key] = value`: #{caller(1..2)}"
@config
end
def self.options=(opts)
@options = opts
logger.warn "config.options = hash` is deprecated, use `config.merge!(hash)`: #{caller(1..2)}"
@config = opts
end
def self.[](key)
@config[key]
end
def self.[]=(key, val)
@config[key] = val
end
def self.merge!(hash)
@config.merge!(hash)
end
def self.fetch(...)
@config.fetch(...)
end
def self.handle_exception(ex, ctx = {})
self[:error_handlers].each do |handler|
handler.call(ex, ctx)
rescue => ex
logger.error "!!! ERROR HANDLER THREW AN ERROR !!!"
logger.error ex
logger.error ex.backtrace.join("\n") unless ex.backtrace.nil?
end
end
###
##
# Configuration for Sidekiq server, use like:
#
# Sidekiq.configure_server do |config|
# config.redis = { :size => 25, :url => 'redis://myhost:8877/0' }
# config.server_middleware do |chain|
# chain.add MyServerHook
# end
@ -76,7 +137,7 @@ module Sidekiq
# Configuration for Sidekiq client, use like:
#
# Sidekiq.configure_client do |config|
# config.redis = { :size => 1, :url => 'redis://myhost:8877/0' }
# config.redis = { size: 1, url: 'redis://myhost:8877/0' }
# end
def self.configure_client
yield self unless server?
@ -131,7 +192,7 @@ module Sidekiq
end
def self.client_middleware
@client_chain ||= Middleware::Chain.new
@client_chain ||= Middleware::Chain.new(self)
yield @client_chain if block_given?
@client_chain
end
@ -143,7 +204,7 @@ module Sidekiq
end
def self.default_server_middleware
Middleware::Chain.new
Middleware::Chain.new(self)
end
def self.default_worker_options=(hash) # deprecated
@ -172,7 +233,7 @@ module Sidekiq
# end
# end
def self.death_handlers
options[:death_handlers]
self[:death_handlers]
end
def self.load_json(string)
@ -225,7 +286,7 @@ module Sidekiq
#
# See sidekiq/scheduled.rb for an in-depth explanation of this value
def self.average_scheduled_poll_interval=(interval)
options[:average_scheduled_poll_interval] = interval
self[:average_scheduled_poll_interval] = interval
end
# Register a proc to handle any error which occurs within the Sidekiq process.
@ -236,7 +297,7 @@ module Sidekiq
#
# The default error handler logs errors to Sidekiq.logger.
def self.error_handlers
options[:error_handlers]
self[:error_handlers]
end
# Register a block to run at a point in the Sidekiq lifecycle.
@ -249,12 +310,12 @@ module Sidekiq
# end
def self.on(event, &block)
raise ArgumentError, "Symbols only please: #{event}" unless event.is_a?(Symbol)
raise ArgumentError, "Invalid event name: #{event}" unless options[:lifecycle_events].key?(event)
options[:lifecycle_events][event] << block
raise ArgumentError, "Invalid event name: #{event}" unless self[:lifecycle_events].key?(event)
self[:lifecycle_events][event] << block
end
def self.strict_args!(mode = :raise)
options[:on_complex_arguments] = mode
self[:on_complex_arguments] = mode
end
# We are shutting down Sidekiq but what about threads that

BIN
lib/sidekiq/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -217,24 +217,30 @@ module Sidekiq
include Enumerable
##
# Return all known queues within Redis.
# Fetch all known queues within Redis.
#
# @return [Array<Sidekiq::Queue>]
def self.all
Sidekiq.redis { |c| c.sscan_each("queues").to_a }.sort.map { |q| Sidekiq::Queue.new(q) }
end
attr_reader :name
# @param name [String] the name of the queue
def initialize(name = "default")
@name = name.to_s
@rname = "queue:#{name}"
end
# The current size of the queue within Redis.
# This value is real-time and can change between calls.
#
# @return [Integer] the size
def size
Sidekiq.redis { |con| con.llen(@rname) }
end
# Sidekiq Pro overrides this
# @return [Boolean] if the queue is currently paused
def paused?
false
end
@ -243,7 +249,7 @@ module Sidekiq
# Calculates this queue's latency, the difference in seconds since the oldest
# job in the queue was enqueued.
#
# @return Float
# @return [Float] in seconds
def latency
entry = Sidekiq.redis { |conn|
conn.lrange(@rname, -1, -1)
@ -279,12 +285,17 @@ module Sidekiq
##
# Find the job with the given JID within this queue.
#
# This is a slow, inefficient operation. Do not use under
# This is a *slow, inefficient* operation. Do not use under
# normal conditions.
#
# @param jid [String] the job_id to look for
# @return [Sidekiq::JobRecord]
# @return [nil] if not found
def find_job(jid)
detect { |j| j.jid == jid }
end
# delete all jobs within this queue
def clear
Sidekiq.redis do |conn|
conn.multi do |transaction|
@ -294,6 +305,10 @@ module Sidekiq
end
end
alias_method :💣, :clear
def as_json(options = nil) # :nodoc:
{name: name} # 5336
end
end
##
@ -306,15 +321,16 @@ module Sidekiq
class JobRecord
attr_reader :item
attr_reader :value
attr_reader :queue
def initialize(item, queue_name = nil)
def initialize(item, queue_name = nil) # :nodoc:
@args = nil
@value = item
@item = item.is_a?(Hash) ? item : parse(item)
@queue = queue_name || @item["queue"]
end
def parse(item)
def parse(item) # :nodoc:
Sidekiq.load_json(item)
rescue JSON::ParserError
# If the job payload in Redis is invalid JSON, we'll load
@ -398,15 +414,12 @@ module Sidekiq
end
end
attr_reader :queue
def latency
now = Time.now.to_f
now - (@item["enqueued_at"] || @item["created_at"] || now)
end
##
# Remove this job from the queue.
# Remove this job from the queue
def delete
count = Sidekiq.redis { |conn|
conn.lrem("queue:#{@queue}", 1, @value)
@ -414,6 +427,7 @@ module Sidekiq
count != 0
end
# Access arbitrary attributes within the job hash
def [](name)
# nil will happen if the JSON fails to parse.
# We don't guarantee Sidekiq will work with bad job JSON but we should
@ -430,11 +444,13 @@ module Sidekiq
end
end
# Represents a job within a Redis sorted set where the score
# represents a timestamp for the job.
class SortedEntry < JobRecord
attr_reader :score
attr_reader :parent
def initialize(parent, score, item)
def initialize(parent, score, item) # :nodoc:
super(item)
@score = Float(score)
@parent = parent
@ -452,12 +468,17 @@ module Sidekiq
end
end
# Change the scheduled time for this job.
#
# @param [Time] the new timestamp when this job will be enqueued.
def reschedule(at)
Sidekiq.redis do |conn|
conn.zincrby(@parent.name, at.to_f - @score, Sidekiq.dump_json(@item))
end
end
# Enqueue this job from the scheduled or dead set so it will
# be executed at some point in the near future.
def add_to_queue
remove_job do |message|
msg = Sidekiq.load_json(message)
@ -465,6 +486,8 @@ module Sidekiq
end
end
# enqueue this job from the retry set so it will be executed
# at some point in the near future.
def retry
remove_job do |message|
msg = Sidekiq.load_json(message)
@ -473,8 +496,7 @@ module Sidekiq
end
end
##
# Place job in the dead set
# Move this job from its current set into the Dead set.
def kill
remove_job do |message|
DeadSet.new.kill(message)
@ -553,6 +575,10 @@ module Sidekiq
end
end
alias_method :💣, :clear
def as_json(options = nil) # :nodoc:
{name: name} # 5336
end
end
class JobSet < SortedSet
@ -716,11 +742,11 @@ module Sidekiq
end
def self.max_jobs
Sidekiq.options[:dead_max_jobs]
Sidekiq[:dead_max_jobs]
end
def self.timeout
Sidekiq.options[:dead_timeout_in_seconds]
Sidekiq[:dead_timeout_in_seconds]
end
end

View File

@ -9,18 +9,23 @@ require "erb"
require "fileutils"
require "sidekiq"
require "sidekiq/component"
require "sidekiq/launcher"
require "sidekiq/util"
module Sidekiq
module Sidekiq # :nodoc:
class CLI
include Util
include Sidekiq::Component
include Singleton unless $TESTING
attr_accessor :launcher
attr_accessor :environment
attr_accessor :config
def parse(args = ARGV.dup)
@config = Sidekiq
@config[:error_handlers].clear
@config[:error_handlers] << @config.method(:default_error_handler)
setup_options(args)
initialize_logger
validate!
@ -36,7 +41,7 @@ module Sidekiq
def run(boot_app: true)
boot_application if boot_app
if environment == "development" && $stdout.tty? && Sidekiq.log_formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
if environment == "development" && $stdout.tty? && @config.log_formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
print_banner
end
logger.info "Booted Rails #{::Rails.version} application in #{environment} environment" if rails_app?
@ -67,7 +72,7 @@ module Sidekiq
# touch the connection pool so it is created before we
# fire startup and start multithreading.
info = Sidekiq.redis_info
info = @config.redis_info
ver = Gem::Version.new(info["redis_version"])
raise "You are connecting to Redis #{ver}, Sidekiq requires Redis 6.2.0 or greater" if ver < Gem::Version.new("6.2.0")
@ -85,22 +90,22 @@ module Sidekiq
# Since the user can pass us a connection pool explicitly in the initializer, we
# need to verify the size is large enough or else Sidekiq's performance is dramatically slowed.
cursize = Sidekiq.redis_pool.size
needed = Sidekiq.options[:concurrency] + 2
cursize = @config.redis_pool.size
needed = @config[:concurrency] + 2
raise "Sidekiq's pool of #{cursize} Redis connections is too small, please increase the size to at least #{needed}" if cursize < needed
# cache process identity
Sidekiq.options[:identity] = identity
@config[:identity] = identity
# Touch middleware so it isn't lazy loaded by multiple threads, #3043
Sidekiq.server_middleware
@config.server_middleware
# Before this point, the process is initializing with just the main thread.
# Starting here the process will now have multiple threads running.
fire_event(:startup, reverse: false, reraise: true)
logger.debug { "Client Middleware: #{Sidekiq.client_middleware.map(&:klass).join(", ")}" }
logger.debug { "Server Middleware: #{Sidekiq.server_middleware.map(&:klass).join(", ")}" }
logger.debug { "Client Middleware: #{@config.client_middleware.map(&:klass).join(", ")}" }
logger.debug { "Server Middleware: #{@config.server_middleware.map(&:klass).join(", ")}" }
launch(self_read)
end
@ -110,7 +115,7 @@ module Sidekiq
logger.info "Starting processing, hit Ctrl-C to stop"
end
@launcher = Sidekiq::Launcher.new(options)
@launcher = Sidekiq::Launcher.new(@config)
begin
launcher.run
@ -173,25 +178,25 @@ module Sidekiq
# Heroku sends TERM and then waits 30 seconds for process to exit.
"TERM" => ->(cli) { raise Interrupt },
"TSTP" => ->(cli) {
Sidekiq.logger.info "Received TSTP, no longer accepting new work"
cli.logger.info "Received TSTP, no longer accepting new work"
cli.launcher.quiet
},
"TTIN" => ->(cli) {
Thread.list.each do |thread|
Sidekiq.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
cli.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
if thread.backtrace
Sidekiq.logger.warn thread.backtrace.join("\n")
cli.logger.warn thread.backtrace.join("\n")
else
Sidekiq.logger.warn "<no backtrace available>"
cli.logger.warn "<no backtrace available>"
end
end
}
}
UNHANDLED_SIGNAL_HANDLER = ->(cli) { Sidekiq.logger.info "No signal handler registered, ignoring" }
UNHANDLED_SIGNAL_HANDLER = ->(cli) { cli.logger.info "No signal handler registered, ignoring" }
SIGNAL_HANDLERS.default = UNHANDLED_SIGNAL_HANDLER
def handle_signal(sig)
Sidekiq.logger.debug "Got #{sig} signal"
logger.debug "Got #{sig} signal"
SIGNAL_HANDLERS[sig].call(self)
end
@ -237,7 +242,7 @@ module Sidekiq
config_dir = if File.directory?(opts[:require].to_s)
File.join(opts[:require], "config")
else
File.join(options[:require], "config")
File.join(@config[:require], "config")
end
%w[sidekiq.yml sidekiq.yml.erb].each do |config_file|
@ -254,26 +259,22 @@ module Sidekiq
opts[:concurrency] = Integer(ENV["RAILS_MAX_THREADS"]) if opts[:concurrency].nil? && ENV["RAILS_MAX_THREADS"]
# merge with defaults
options.merge!(opts)
end
def options
Sidekiq.options
@config.merge!(opts)
end
def boot_application
ENV["RACK_ENV"] = ENV["RAILS_ENV"] = environment
if File.directory?(options[:require])
if File.directory?(@config[:require])
require "rails"
if ::Rails::VERSION::MAJOR < 6
warn "Sidekiq #{Sidekiq::VERSION} only supports Rails 6+"
end
require "sidekiq/rails"
require File.expand_path("#{options[:require]}/config/environment.rb")
options[:tag] ||= default_tag
require File.expand_path("#{@config[:require]}/config/environment.rb")
@config[:tag] ||= default_tag
else
require options[:require]
require @config[:require]
end
end
@ -290,8 +291,8 @@ module Sidekiq
end
def validate!
if !File.exist?(options[:require]) ||
(File.directory?(options[:require]) && !File.exist?("#{options[:require]}/config/application.rb"))
if !File.exist?(@config[:require]) ||
(File.directory?(@config[:require]) && !File.exist?("#{@config[:require]}/config/application.rb"))
logger.info "=================================================================="
logger.info " Please point Sidekiq to a Rails application or a Ruby file "
logger.info " to load your job classes with -r [DIR|FILE]."
@ -301,7 +302,7 @@ module Sidekiq
end
[:concurrency, :timeout].each do |opt|
raise ArgumentError, "#{opt}: #{options[opt]} is not a valid value" if options.key?(opt) && options[opt].to_i <= 0
raise ArgumentError, "#{opt}: #{@config[opt]} is not a valid value" if @config[opt].to_i <= 0
end
end
@ -375,7 +376,7 @@ module Sidekiq
end
def initialize_logger
Sidekiq.logger.level = ::Logger::DEBUG if options[:verbose]
@config.logger.level = ::Logger::DEBUG if @config[:verbose]
end
def parse_config(path)

64
lib/sidekiq/component.rb Normal file
View File

@ -0,0 +1,64 @@
module Sidekiq
##
# Sidekiq::Component assumes a config instance is available at @config
module Component # :nodoc:
attr_reader :config
def watchdog(last_words)
yield
rescue Exception => ex
handle_exception(ex, {context: last_words})
raise ex
end
def safe_thread(name, &block)
Thread.new do
Thread.current.name = name
watchdog(name, &block)
end
end
def logger
config.logger
end
def redis(&block)
config.redis(&block)
end
def tid
Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
end
def hostname
ENV["DYNO"] || Socket.gethostname
end
def process_nonce
@@process_nonce ||= SecureRandom.hex(6)
end
def identity
@@identity ||= "#{hostname}:#{::Process.pid}:#{process_nonce}"
end
def handle_exception(ex, ctx = {})
config.handle_exception(ex, ctx)
end
def fire_event(event, options = {})
reverse = options[:reverse]
reraise = options[:reraise]
arr = config[:lifecycle_events][event]
arr.reverse! if reverse
arr.each do |block|
block.call
rescue => ex
handle_exception(ex, {context: "Exception during Sidekiq lifecycle event.", event: event})
raise ex if reraise
end
arr.clear # once we've fired an event, we never fire it again
end
end
end

View File

@ -1,27 +0,0 @@
# frozen_string_literal: true
require "sidekiq"
module Sidekiq
module ExceptionHandler
class Logger
def call(ex, ctx)
Sidekiq.logger.warn(Sidekiq.dump_json(ctx)) unless ctx.empty?
Sidekiq.logger.warn("#{ex.class.name}: #{ex.message}")
Sidekiq.logger.warn(ex.backtrace.join("\n")) unless ex.backtrace.nil?
end
Sidekiq.error_handlers << Sidekiq::ExceptionHandler::Logger.new
end
def handle_exception(ex, ctx = {})
Sidekiq.error_handlers.each do |handler|
handler.call(ex, ctx)
rescue => ex
Sidekiq.logger.error "!!! ERROR HANDLER THREW AN ERROR !!!"
Sidekiq.logger.error ex
Sidekiq.logger.error ex.backtrace.join("\n") unless ex.backtrace.nil?
end
end
end
end

View File

@ -1,14 +1,16 @@
# frozen_string_literal: true
require "sidekiq"
require "sidekiq/component"
module Sidekiq
module Sidekiq # :nodoc:
class BasicFetch
include Sidekiq::Component
# We want the fetch operation to timeout every few seconds so the thread
# can check if the process is shutting down.
TIMEOUT = 2
UnitOfWork = Struct.new(:queue, :job) {
UnitOfWork = Struct.new(:queue, :job, :config) {
def acknowledge
# nothing to do
end
@ -18,17 +20,17 @@ module Sidekiq
end
def requeue
Sidekiq.redis do |conn|
config.redis do |conn|
conn.rpush(queue, job)
end
end
}
def initialize(options)
raise ArgumentError, "missing queue list" unless options[:queues]
@options = options
@strictly_ordered_queues = !!@options[:strict]
@queues = @options[:queues].map { |q| "queue:#{q}" }
def initialize(config)
raise ArgumentError, "missing queue list" unless config[:queues]
@config = config
@strictly_ordered_queues = !!@config[:strict]
@queues = @config[:queues].map { |q| "queue:#{q}" }
if @strictly_ordered_queues
@queues.uniq!
@queues << TIMEOUT
@ -44,30 +46,30 @@ module Sidekiq
return nil
end
work = Sidekiq.redis { |conn| conn.brpop(*qs) }
UnitOfWork.new(*work) if work
queue, job = redis { |conn| conn.brpop(*qs) }
UnitOfWork.new(queue, job, config) if queue
end
def bulk_requeue(inprogress, options)
return if inprogress.empty?
Sidekiq.logger.debug { "Re-queueing terminated jobs" }
logger.debug { "Re-queueing terminated jobs" }
jobs_to_requeue = {}
inprogress.each do |unit_of_work|
jobs_to_requeue[unit_of_work.queue] ||= []
jobs_to_requeue[unit_of_work.queue] << unit_of_work.job
end
Sidekiq.redis do |conn|
redis do |conn|
conn.pipelined do |pipeline|
jobs_to_requeue.each do |queue, jobs|
pipeline.rpush(queue, jobs)
end
end
end
Sidekiq.logger.info("Pushed #{inprogress.size} jobs back to Redis")
logger.info("Pushed #{inprogress.size} jobs back to Redis")
rescue => ex
Sidekiq.logger.warn("Failed to requeue #{inprogress.size} jobs: #{ex.message}")
logger.warn("Failed to requeue #{inprogress.size} jobs: #{ex.message}")
end
# Creating the Redis#brpop command takes into account any

View File

@ -66,12 +66,13 @@ module Sidekiq
class Skip < Handled; end
include Sidekiq::Util
include Sidekiq::Component
DEFAULT_MAX_RETRY_ATTEMPTS = 25
def initialize(options = {})
@max_retries = Sidekiq.options.merge(options).fetch(:max_retries, DEFAULT_MAX_RETRY_ATTEMPTS)
def initialize(options)
@config = options
@max_retries = @config[:max_retries] || DEFAULT_MAX_RETRY_ATTEMPTS
end
# The global retry handler requires only the barest of data.

View File

@ -17,13 +17,13 @@ module Sidekiq
def verify_json(item)
job_class = item["wrapped"] || item["class"]
if Sidekiq.options[:on_complex_arguments] == :raise
if Sidekiq[:on_complex_arguments] == :raise
msg = <<~EOM
Job arguments to #{job_class} must be native JSON types, see https://github.com/mperham/sidekiq/wiki/Best-Practices.
To disable this error, add `Sidekiq.strict_args!(false)` to your initializer.
EOM
raise(ArgumentError, msg) unless json_safe?(item)
elsif Sidekiq.options[:on_complex_arguments] == :warn
elsif Sidekiq[:on_complex_arguments] == :warn
Sidekiq.logger.warn <<~EOM unless json_safe?(item)
Job arguments to #{job_class} do not serialize to JSON safely. This will raise an error in
Sidekiq 7.0. See https://github.com/mperham/sidekiq/wiki/Best-Practices or raise an error today

View File

@ -3,11 +3,12 @@
require "sidekiq/manager"
require "sidekiq/fetch"
require "sidekiq/scheduled"
require "sidekiq/ring_buffer"
module Sidekiq
# The Launcher starts the Manager and Poller threads and provides the process heartbeat.
class Launcher
include Util
include Sidekiq::Component
STATS_TTL = 5 * 365 * 24 * 60 * 60 # 5 years
@ -22,11 +23,11 @@ module Sidekiq
attr_accessor :manager, :poller, :fetcher
def initialize(options)
@config = options
options[:fetch] ||= BasicFetch.new(options)
@manager = Sidekiq::Manager.new(options)
@poller = Sidekiq::Scheduled::Poller.new
@poller = Sidekiq::Scheduled::Poller.new(options)
@done = false
@options = options
end
def run
@ -45,7 +46,7 @@ module Sidekiq
# Shuts down this Sidekiq instance. Waits up to the deadline for all jobs to complete.
def stop
deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + @options[:timeout]
deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + @config[:timeout]
@done = true
@manager.quiet
@ -55,8 +56,8 @@ module Sidekiq
# Requeue everything in case there was a thread which fetched a job while the process was stopped.
# This call is a no-op in Sidekiq but necessary for Sidekiq Pro.
strategy = @options[:fetch]
strategy.bulk_requeue([], @options)
strategy = @config[:fetch]
strategy.bulk_requeue([], @config)
clear_heartbeat
end
@ -74,14 +75,14 @@ module Sidekiq
heartbeat
sleep BEAT_PAUSE
end
Sidekiq.logger.info("Heartbeat stopping...")
logger.info("Heartbeat stopping...")
end
def clear_heartbeat
# Remove record from Redis since we are shutting down.
# Note we don't stop the heartbeat thread; if the process
# doesn't actually exit, it'll reappear in the Web UI.
Sidekiq.redis do |conn|
redis do |conn|
conn.pipelined do |pipeline|
pipeline.srem("processes", identity)
pipeline.unlink("#{identity}:work")
@ -134,7 +135,7 @@ module Sidekiq
nowdate = Time.now.utc.strftime("%Y-%m-%d")
Sidekiq.redis do |conn|
redis do |conn|
conn.multi do |transaction|
transaction.incrby("stat:processed", procd)
transaction.incrby("stat:processed:#{nowdate}", procd)
@ -161,7 +162,7 @@ module Sidekiq
fails = procd = 0
kb = memory_usage(::Process.pid)
_, exists, _, _, msg = Sidekiq.redis { |conn|
_, exists, _, _, msg = redis { |conn|
conn.multi { |transaction|
transaction.sadd("processes", key)
transaction.exists?(key)
@ -199,7 +200,7 @@ module Sidekiq
def check_rtt
a = b = 0
Sidekiq.redis do |x|
redis do |x|
a = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
x.ping
b = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :microsecond)
@ -210,7 +211,7 @@ module Sidekiq
# Workable is < 10,000µs
# Log a warning if it's a disaster.
if RTT_READINGS.all? { |x| x > RTT_WARNING_LEVEL }
Sidekiq.logger.warn <<~EOM
logger.warn <<~EOM
Your Redis network connection is performing extremely poorly.
Last RTT readings were #{RTT_READINGS.buffer.inspect}, ideally these should be < 1000.
Ensure Redis is running in the same AZ or datacenter as Sidekiq.
@ -247,10 +248,10 @@ module Sidekiq
"hostname" => hostname,
"started_at" => Time.now.to_f,
"pid" => ::Process.pid,
"tag" => @options[:tag] || "",
"concurrency" => @options[:concurrency],
"queues" => @options[:queues].uniq,
"labels" => @options[:labels],
"tag" => @config[:tag] || "",
"concurrency" => @config[:concurrency],
"queues" => @config[:queues].uniq,
"labels" => @config[:labels],
"identity" => identity
}
end

View File

@ -1,6 +1,5 @@
# frozen_string_literal: true
require "sidekiq/util"
require "sidekiq/processor"
require "sidekiq/fetch"
require "set"
@ -21,29 +20,26 @@ module Sidekiq
# the shutdown process. The other tasks are performed by other threads.
#
class Manager
include Util
include Sidekiq::Component
attr_reader :workers
attr_reader :options
def initialize(options = {})
@config = options
logger.debug { options.inspect }
@options = options
@count = options[:concurrency] || 10
raise ArgumentError, "Concurrency of #{@count} is not supported" if @count < 1
@done = false
@workers = Set.new
@count.times do
@workers << Processor.new(self, options)
@workers << Processor.new(@config, &method(:processor_result))
end
@plock = Mutex.new
end
def start
@workers.each do |x|
x.start
end
@workers.each(&:start)
end
def quiet
@ -51,7 +47,7 @@ module Sidekiq
@done = true
logger.info { "Terminating quiet threads" }
@workers.each { |x| x.terminate }
@workers.each(&:terminate)
fire_event(:quiet, reverse: true)
end
@ -72,17 +68,11 @@ module Sidekiq
hard_shutdown
end
def processor_stopped(processor)
@plock.synchronize do
@workers.delete(processor)
end
end
def processor_died(processor, reason)
def processor_result(processor, reason = nil)
@plock.synchronize do
@workers.delete(processor)
unless @done
p = Processor.new(self, options)
p = Processor.new(@config, &method(:processor_result))
@workers << p
p.start
end
@ -115,8 +105,8 @@ module Sidekiq
# contract says that jobs are run AT LEAST once. Process termination
# is delayed until we're certain the jobs are back in Redis because
# it is worse to lose a job than to run it twice.
strategy = @options[:fetch]
strategy.bulk_requeue(jobs, @options)
strategy = @config[:fetch]
strategy.bulk_requeue(jobs, @config)
end
cleanup.each do |processor|
@ -129,5 +119,18 @@ module Sidekiq
deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + 3
wait_for(deadline) { @workers.empty? }
end
# hack for quicker development / testing environment #2774
PAUSE_TIME = $stdout.tty? ? 0.1 : 0.5
# Wait for the orblock to be true or the deadline passed.
def wait_for(deadline, &condblock)
remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
while remaining > PAUSE_TIME
return if condblock.call
sleep PAUSE_TIME
remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
end
end
end
end

View File

@ -1,5 +1,7 @@
# frozen_string_literal: true
require "sidekiq/middleware/modules"
module Sidekiq
# Middleware is code configured to run before/after
# a message is processed. It is patterned after Rack
@ -44,10 +46,12 @@ module Sidekiq
# This is an example of a minimal server middleware:
#
# class MyServerHook
# include Sidekiq::ServerMiddleware
# def call(job_instance, msg, queue)
# puts "Before job"
# logger.info "Before job"
# redis {|conn| conn.get("foo") } # do something in Redis
# yield
# puts "After job"
# logger.info "After job"
# end
# end
#
@ -56,10 +60,11 @@ module Sidekiq
# to Redis:
#
# class MyClientHook
# include Sidekiq::ClientMiddleware
# def call(job_class, msg, queue, redis_pool)
# puts "Before push"
# logger.info "Before push"
# result = yield
# puts "After push"
# logger.info "After push"
# result
# end
# end
@ -76,7 +81,8 @@ module Sidekiq
entries.each(&block)
end
def initialize
def initialize(config = nil)
@config = config
@entries = nil
yield self if block_given?
end
@ -91,24 +97,24 @@ module Sidekiq
def add(klass, *args)
remove(klass)
entries << Entry.new(klass, *args)
entries << Entry.new(@config, klass, *args)
end
def prepend(klass, *args)
remove(klass)
entries.insert(0, Entry.new(klass, *args))
entries.insert(0, Entry.new(@config, klass, *args))
end
def insert_before(oldklass, newklass, *args)
i = entries.index { |entry| entry.klass == newklass }
new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
new_entry = i.nil? ? Entry.new(@config, newklass, *args) : entries.delete_at(i)
i = entries.index { |entry| entry.klass == oldklass } || 0
entries.insert(i, new_entry)
end
def insert_after(oldklass, newklass, *args)
i = entries.index { |entry| entry.klass == newklass }
new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
new_entry = i.nil? ? Entry.new(@config, newklass, *args) : entries.delete_at(i)
i = entries.index { |entry| entry.klass == oldklass } || entries.count - 1
entries.insert(i + 1, new_entry)
end
@ -149,13 +155,16 @@ module Sidekiq
class Entry
attr_reader :klass
def initialize(klass, *args)
def initialize(config, klass, *args)
@config = config
@klass = klass
@args = args
end
def make_new
@klass.new(*@args)
x = @klass.new(*@args)
x.config = @config if @config && x.respond_to?(:config=)
x
end
end
end

View File

@ -15,6 +15,8 @@ module Sidekiq
#
module CurrentAttributes
class Save
include Sidekiq::ClientMiddleware
def initialize(cattr)
@klass = cattr
end
@ -31,6 +33,8 @@ module Sidekiq
end
class Load
include Sidekiq::ServerMiddleware
def initialize(cattr)
@klass = cattr
end

View File

@ -10,6 +10,7 @@ module Sidekiq::Middleware::I18n
# Get the current locale and store it in the message
# to be sent to Sidekiq.
class Client
include Sidekiq::ClientMiddleware
def call(_jobclass, job, _queue, _redis)
job["locale"] ||= I18n.locale
yield
@ -18,6 +19,7 @@ module Sidekiq::Middleware::I18n
# Pull the msg locale out and set the current thread to use it.
class Server
include Sidekiq::ServerMiddleware
def call(_jobclass, job, _queue, &block)
I18n.with_locale(job.fetch("locale", I18n.default_locale), &block)
end

View File

@ -0,0 +1,19 @@
module Sidekiq
module ServerMiddleware
attr_accessor :config
def redis_pool
config.redis_pool
end
def logger
config.logger
end
def redis(&block)
config.redis(&block)
end
end
# no difference for now
ClientMiddleware = ServerMiddleware
end

View File

@ -1,6 +1,5 @@
# frozen_string_literal: true
require "sidekiq/util"
require "sidekiq/fetch"
require "sidekiq/job_logger"
require "sidekiq/job_retry"
@ -15,29 +14,30 @@ module Sidekiq
# b. run the middleware chain
# c. call #perform
#
# A Processor can exit due to shutdown (processor_stopped)
# or due to an error during job execution (processor_died)
# A Processor can exit due to shutdown or due to
# an error during job execution.
#
# If an error occurs in the job execution, the
# Processor calls the Manager to create a new one
# to replace itself and exits.
#
class Processor
include Util
include Sidekiq::Component
attr_reader :thread
attr_reader :job
def initialize(mgr, options)
@mgr = mgr
def initialize(options, &block)
@callback = block
@down = false
@done = false
@job = nil
@thread = nil
@config = options
@strategy = options[:fetch]
@reloader = options[:reloader] || proc { |&block| block.call }
@job_logger = (options[:job_logger] || Sidekiq::JobLogger).new
@retrier = Sidekiq::JobRetry.new
@retrier = Sidekiq::JobRetry.new(options)
end
def terminate(wait = false)
@ -66,14 +66,14 @@ module Sidekiq
def run
process_one until @done
@mgr.processor_stopped(self)
@callback.call(self)
rescue Sidekiq::Shutdown
@mgr.processor_stopped(self)
@callback.call(self)
rescue Exception => ex
@mgr.processor_died(self, ex)
@callback.call(self, ex)
end
def process_one
def process_one(&block)
@job = fetch
process(@job) if @job
@job = nil
@ -160,7 +160,7 @@ module Sidekiq
ack = false
begin
dispatch(job_hash, queue, jobstr) do |inst|
Sidekiq.server_middleware.invoke(inst, job_hash, queue) do
@config.server_middleware.invoke(inst, job_hash, queue) do
execute_job(inst, job_hash["args"])
end
end

View File

@ -38,12 +38,12 @@ module Sidekiq
end
initializer "sidekiq.rails_logger" do
Sidekiq.configure_server do |_|
Sidekiq.configure_server do |config|
# This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
# it will appear in the Sidekiq console with all of the job context. See #5021 and
# https://github.com/rails/rails/blob/b5f2b550f69a99336482739000c58e4e04e033aa/railties/lib/rails/commands/server/server_command.rb#L82-L84
unless ::Rails.logger == ::Sidekiq.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(::Sidekiq.logger))
unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
end
end
end
@ -60,8 +60,8 @@ module Sidekiq
#
# None of this matters on the client-side, only within the Sidekiq process itself.
config.after_initialize do
Sidekiq.configure_server do |_|
Sidekiq.options[:reloader] = Sidekiq::Rails::Reloader.new
Sidekiq.configure_server do |config|
config[:reloader] = Sidekiq::Rails::Reloader.new
end
end
end

View File

@ -32,14 +32,14 @@ module Sidekiq
elsif Sidekiq.server?
# Give ourselves plenty of connections. pool is lazy
# so we won't create them until we need them.
Sidekiq.options[:concurrency] + 5
Sidekiq[:concurrency] + 5
elsif ENV["RAILS_MAX_THREADS"]
Integer(ENV["RAILS_MAX_THREADS"])
else
5
end
verify_sizing(size, Sidekiq.options[:concurrency]) if Sidekiq.server?
verify_sizing(size, Sidekiq[:concurrency]) if Sidekiq.server?
pool_timeout = symbolized_options[:pool_timeout] || 1
log_info(symbolized_options)

View File

@ -0,0 +1,29 @@
require "forwardable"
module Sidekiq
class RingBuffer
include Enumerable
extend Forwardable
def_delegators :@buf, :[], :each, :size
def initialize(size, default = 0)
@size = size
@buf = Array.new(size, default)
@index = 0
end
def <<(element)
@buf[@index % @size] = element
@index += 1
element
end
def buffer
@buf
end
def reset(default = 0)
@buf.fill(default)
end
end
end

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true
require "sidekiq"
require "sidekiq/util"
require "sidekiq/api"
require "sidekiq/component"
module Sidekiq
module Scheduled
@ -67,12 +67,13 @@ module Sidekiq
# just pops the job back onto its original queue so the
# workers can pick it up like any other job.
class Poller
include Util
include Sidekiq::Component
INITIAL_WAIT = 10
def initialize
@enq = (Sidekiq.options[:scheduled_enq] || Sidekiq::Scheduled::Enq).new
def initialize(options)
@config = options
@enq = (options[:scheduled_enq] || Sidekiq::Scheduled::Enq).new
@sleeper = ConnectionPool::TimedStack.new
@done = false
@thread = nil
@ -100,7 +101,7 @@ module Sidekiq
enqueue
wait
end
Sidekiq.logger.info("Scheduler exiting...")
logger.info("Scheduler exiting...")
}
end
@ -171,14 +172,14 @@ module Sidekiq
#
# We only do this if poll_interval_average is unset (the default).
def poll_interval_average
Sidekiq.options[:poll_interval_average] ||= scaled_poll_interval
@config[:poll_interval_average] ||= scaled_poll_interval
end
# Calculates an average poll interval based on the number of known Sidekiq processes.
# This minimizes a single point of failure by dispersing check-ins but without taxing
# Redis if you run many Sidekiq processes.
def scaled_poll_interval
process_count * Sidekiq.options[:average_scheduled_poll_interval]
process_count * @config[:average_scheduled_poll_interval]
end
def process_count
@ -197,7 +198,7 @@ module Sidekiq
# to give time for the heartbeat to register (if the poll interval is going to be calculated by the number
# of workers), and 5 random seconds to ensure they don't all hit Redis at the same time.
total = 0
total += INITIAL_WAIT unless Sidekiq.options[:poll_interval_average]
total += INITIAL_WAIT unless @config[:poll_interval_average]
total += (5 * rand)
@sleeper.pop(total)

View File

@ -179,7 +179,7 @@ module Sidekiq
end
def clear_for(queue, klass)
jobs_by_queue[queue].clear
jobs_by_queue[queue.to_s].clear
jobs_by_class[klass].clear
end

View File

@ -1,108 +0,0 @@
# frozen_string_literal: true
require "forwardable"
require "socket"
require "securerandom"
require "sidekiq/exception_handler"
module Sidekiq
##
# This module is part of Sidekiq core and not intended for extensions.
#
class RingBuffer
include Enumerable
extend Forwardable
def_delegators :@buf, :[], :each, :size
def initialize(size, default = 0)
@size = size
@buf = Array.new(size, default)
@index = 0
end
def <<(element)
@buf[@index % @size] = element
@index += 1
element
end
def buffer
@buf
end
def reset(default = 0)
@buf.fill(default)
end
end
module Util
include ExceptionHandler
# hack for quicker development / testing environment #2774
PAUSE_TIME = $stdout.tty? ? 0.1 : 0.5
# Wait for the orblock to be true or the deadline passed.
def wait_for(deadline, &condblock)
remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
while remaining > PAUSE_TIME
return if condblock.call
sleep PAUSE_TIME
remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
end
end
def watchdog(last_words)
yield
rescue Exception => ex
handle_exception(ex, {context: last_words})
raise ex
end
def safe_thread(name, &block)
Thread.new do
Thread.current.name = name
watchdog(name, &block)
end
end
def logger
Sidekiq.logger
end
def redis(&block)
Sidekiq.redis(&block)
end
def tid
Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
end
def hostname
ENV["DYNO"] || Socket.gethostname
end
def process_nonce
@@process_nonce ||= SecureRandom.hex(6)
end
def identity
@@identity ||= "#{hostname}:#{::Process.pid}:#{process_nonce}"
end
def fire_event(event, options = {})
reverse = options[:reverse]
reraise = options[:reraise]
arr = Sidekiq.options[:lifecycle_events][event]
arr.reverse! if reverse
arr.each do |block|
block.call
rescue => ex
handle_exception(ex, {context: "Exception during Sidekiq lifecycle event.", event: event})
raise ex if reraise
end
arr.clear
end
end
end

View File

@ -297,7 +297,7 @@ module Sidekiq
end
def environment_title_prefix
environment = Sidekiq.options[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
environment = Sidekiq[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
"[#{environment.upcase}] " unless environment == "production"
end

View File

@ -1,15 +1,15 @@
source 'https://rubygems.org'
source "https://rubygems.org"
gem 'sidekiq', :path => '..'
gem 'rails', "~> 6.1"
gem 'puma'
gem "sidekiq", path: ".."
gem "rails"
gem "puma"
gem "redis-client"
# Ubuntu required packages to install rails
# apt-get install build-essential patch ruby-dev zlib1g-dev liblzma-dev libsqlite3-dev
platforms :ruby do
gem 'sqlite3'
gem "sqlite3"
end
gem 'after_commit_everywhere'
gem "after_commit_everywhere"

View File

@ -1,6 +1,6 @@
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require_relative 'config/application'
require_relative "config/application"
Rails.application.load_tasks

View File

@ -3,42 +3,42 @@ class WorkController < ApplicationController
@count = rand(100)
puts "Adding #{@count} jobs"
@count.times do |x|
HardWorker.perform_async('bubba', 0.01, x)
HardWorker.perform_async("bubba", 0.01, x)
end
end
def email
UserMailer.delay_for(30.seconds).greetings(Time.now)
render :plain => 'enqueued'
render plain: "enqueued"
end
def bulk
Sidekiq::Client.push_bulk('class' => HardWorker,
'args' => [['bob', 1, 1], ['mike', 1, 2]])
render :plain => 'enbulked'
Sidekiq::Client.push_bulk("class" => HardWorker,
"args" => [["bob", 1, 1], ["mike", 1, 2]])
render plain: "enbulked"
end
def long
50.times do |x|
HardWorker.perform_async('bob', 15, x)
HardWorker.perform_async("bob", 15, x)
end
render :plain => 'enqueued'
render plain: "enqueued"
end
def crash
HardWorker.perform_async('crash', 1, Time.now.to_f)
render :plain => 'enqueued'
HardWorker.perform_async("crash", 1, Time.now.to_f)
render plain: "enqueued"
end
def delayed_post
p = Post.first
unless p
p = Post.create!(:title => "Title!", :body => 'Body!')
p2 = Post.create!(:title => "Other!", :body => 'Second Body!')
else
if p
p2 = Post.second
else
p = Post.create!(title: "Title!", body: "Body!")
p2 = Post.create!(title: "Other!", body: "Second Body!")
end
p.delay.long_method(p2)
render :plain => 'enqueued'
render plain: "enqueued"
end
end

View File

@ -4,6 +4,6 @@ class UserMailer < ActionMailer::Base
def greetings(now)
@now = now
@hostname = `hostname`.strip
mail(:to => 'mperham@gmail.com', :subject => 'Ahoy Matey!')
mail(to: "mperham@gmail.com", subject: "Ahoy Matey!")
end
end

View File

@ -1,6 +1,6 @@
class Post < ActiveRecord::Base
def long_method(other_post)
puts "Running long method with #{self.id} and #{other_post.id}"
puts "Running long method with #{id} and #{other_post.id}"
end
def self.testing

View File

@ -1,9 +1,9 @@
class HardWorker
include Sidekiq::Worker
sidekiq_options :backtrace => 5
sidekiq_options backtrace: 5
def perform(name, count, salt)
raise name if name == 'crash'
raise name if name == "crash"
logger.info Time.now
sleep count
end

View File

@ -1,3 +1,3 @@
#!/usr/bin/env ruby
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
load Gem.bin_path('bundler', 'bundle')
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
load Gem.bin_path("bundler", "bundle")

View File

@ -1,9 +1,9 @@
#!/usr/bin/env ruby
begin
load File.expand_path('../spring', __FILE__)
load File.expand_path("../spring", __FILE__)
rescue LoadError => e
raise unless e.message.include?('spring')
raise unless e.message.include?("spring")
end
APP_PATH = File.expand_path('../config/application', __dir__)
require_relative '../config/boot'
require 'rails/commands'
APP_PATH = File.expand_path("../config/application", __dir__)
require_relative "../config/boot"
require "rails/commands"

View File

@ -1,9 +1,9 @@
#!/usr/bin/env ruby
begin
load File.expand_path('../spring', __FILE__)
load File.expand_path("../spring", __FILE__)
rescue LoadError => e
raise unless e.message.include?('spring')
raise unless e.message.include?("spring")
end
require_relative '../config/boot'
require 'rake'
require_relative "../config/boot"
require "rake"
Rake.application.run

View File

@ -1,36 +0,0 @@
#!/usr/bin/env ruby
require 'fileutils'
include FileUtils
# path to your application root.
APP_ROOT = File.expand_path('..', __dir__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end
chdir APP_ROOT do
# This script is a starting point to setup your application.
# Add necessary setup steps to this file.
puts '== Installing dependencies =='
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
# Install JavaScript dependencies if using Yarn
# system('bin/yarn')
# puts "\n== Copying sample files =="
# unless File.exist?('config/database.yml')
# cp 'config/database.yml.sample', 'config/database.yml'
# end
puts "\n== Preparing database =="
system! 'bin/rails db:setup'
puts "\n== Removing old logs and tempfiles =="
system! 'bin/rails log:clear tmp:clear'
puts "\n== Restarting application server =="
system! 'bin/rails restart'
end

View File

@ -1,5 +1,5 @@
# This file is used by Rack-based servers to start the application.
require_relative 'config/environment'
require_relative "config/environment"
run Rails.application

View File

@ -1,18 +1,16 @@
require_relative 'boot'
require_relative "boot"
require 'rails'
%w(
require "rails"
%w[
active_record/railtie
action_controller/railtie
action_view/railtie
action_mailer/railtie
active_job/railtie
sprockets/railtie
).each do |railtie|
begin
require railtie
rescue LoadError
end
].each do |railtie|
require railtie
rescue LoadError
end
# Require the gems listed in Gemfile, including any gems

View File

@ -1,3 +1,3 @@
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
require 'bundler/setup' # Set up gems listed in the Gemfile.
require "bundler/setup" # Set up gems listed in the Gemfile.

View File

@ -1,5 +1,5 @@
# Load the Rails application.
require_relative 'application'
require_relative "application"
# Initialize the Rails application.
Rails.application.initialize!

View File

@ -14,12 +14,12 @@ Rails.application.configure do
# Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching.
if Rails.root.join('tmp', 'caching-dev.txt').exist?
if Rails.root.join("tmp", "caching-dev.txt").exist?
config.action_controller.perform_caching = true
config.cache_store = :memory_store
config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{2.days.to_i}"
"Cache-Control" => "public, max-age=#{2.days.to_i}"
}
else
config.action_controller.perform_caching = false
@ -28,7 +28,7 @@ Rails.application.configure do
end
# Store uploaded files on the local file system (see config/storage.yml for options)
#config.active_storage.service = :local
# config.active_storage.service = :local
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
@ -57,5 +57,5 @@ Rails.application.configure do
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
#config.file_watcher = ActiveSupport::EventedFileUpdateChecker
# config.file_watcher = ActiveSupport::EventedFileUpdateChecker
end

View File

@ -11,7 +11,7 @@ Rails.application.configure do
config.eager_load = true
# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
@ -20,7 +20,7 @@ Rails.application.configure do
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
# Compress JavaScripts and CSS.
config.assets.js_compressor = :uglifier
@ -39,7 +39,7 @@ Rails.application.configure do
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Store uploaded files on the local file system (see config/storage.yml for options)
#config.active_storage.service = :local
# config.active_storage.service = :local
# Mount Action Cable outside main process or domain
# config.action_cable.mount_path = nil
@ -54,7 +54,7 @@ Rails.application.configure do
config.log_level = :debug
# Prepend all log lines with the following tags.
config.log_tags = [ :request_id ]
config.log_tags = [:request_id]
# Use a different cache store in production.
# config.cache_store = :mem_cache_store
@ -84,9 +84,9 @@ Rails.application.configure do
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
if ENV["RAILS_LOG_TO_STDOUT"].present?
logger = ActiveSupport::Logger.new(STDOUT)
logger = ActiveSupport::Logger.new($stdout)
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
config.logger = ActiveSupport::TaggedLogging.new(logger)
end
# Do not dump schema after migrations.

View File

@ -15,11 +15,11 @@ Rails.application.configure do
# Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true
config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{1.hour.to_i}"
"Cache-Control" => "public, max-age=#{1.hour.to_i}"
}
# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
# Raise exceptions instead of rendering exception templates.
@ -29,7 +29,7 @@ Rails.application.configure do
config.action_controller.allow_forgery_protection = false
# Store uploaded files on the local file system in a temporary directory
#config.active_storage.service = :test
# config.active_storage.service = :test
config.action_mailer.perform_caching = false

View File

@ -4,4 +4,4 @@
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
Myapp::Application.config.secret_token = 'bdd335500c81ba1f279f9dd8198d1f334445d0193168ed73c1282502180dca27e3e102ec345e99b2acac6a63f7fe29da69c60cc9e76e8da34fb5d4989db24cd8'
Myapp::Application.config.secret_token = "bdd335500c81ba1f279f9dd8198d1f334445d0193168ed73c1282502180dca27e3e102ec345e99b2acac6a63f7fe29da69c60cc9e76e8da34fb5d4989db24cd8"

View File

@ -1,3 +1,3 @@
# Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cookie_store, key: '_myapp_session'
Rails.application.config.session_store :cookie_store, key: "_myapp_session"

View File

@ -1,19 +1,19 @@
Sidekiq.default_job_options = { queue: "something" }
Sidekiq.default_job_options = {queue: "something"}
Sidekiq.configure_client do |config|
config.redis = { :size => 2 }
config.redis = {size: 2}
end
Sidekiq.configure_server do |config|
config.on(:startup) { }
config.on(:quiet) { }
config.on(:startup) {}
config.on(:quiet) {}
config.on(:shutdown) do
#result = RubyProf.stop
# result = RubyProf.stop
## Write the results to a file
## Requires railsexpress patched MRI build
# brew install qcachegrind
#File.open("callgrind.profile", "w") do |f|
#RubyProf::CallTreePrinter.new(result).print(f, :min_percent => 1)
#end
# File.open("callgrind.profile", "w") do |f|
# RubyProf::CallTreePrinter.new(result).print(f, :min_percent => 1)
# end
end
end

View File

@ -1,11 +1,11 @@
# turns off browser asset caching so we can test CSS changes quickly
ENV['SIDEKIQ_WEB_TESTING'] = '1'
ENV["SIDEKIQ_WEB_TESTING"] = "1"
require 'sidekiq/web'
Sidekiq::Web.app_url = '/'
require "sidekiq/web"
Sidekiq::Web.app_url = "/"
Rails.application.routes.draw do
mount Sidekiq::Web => '/sidekiq'
mount Sidekiq::Web => "/sidekiq"
get "work" => "work#index"
get "work/email" => "work#email"
get "work/post" => "work#delayed_post"

View File

@ -11,12 +11,10 @@
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2012_01_23_214055) do
create_table "posts", force: :cascade do |t|
t.string "title"
t.string "body"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
end

View File

@ -1,6 +1,6 @@
#!/usr/bin/env ruby
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
APP_PATH = File.expand_path('../../config/application', __FILE__)
require File.expand_path('../../config/boot', __FILE__)
require 'rails/commands'
APP_PATH = File.expand_path("../../config/application", __FILE__)
require File.expand_path("../../config/boot", __FILE__)
require "rails/commands"

View File

@ -1,14 +1,14 @@
# Easiest way to run Sidekiq::Web.
# Run with "bundle exec rackup simple.ru"
require 'sidekiq/web'
require "sidekiq/web"
# A Web process always runs as client, no need to configure server
Sidekiq.configure_client do |config|
config.redis = { url: 'redis://localhost:6379/0', size: 1 }
config.redis = {url: "redis://localhost:6379/0", size: 1}
end
Sidekiq::Client.push('class' => "HardWorker", 'args' => [])
Sidekiq::Client.push("class" => "HardWorker", "args" => [])
# In a multi-process deployment, all Web UI instances should share
# this secret key so they can all decode the encrypted browser cookies

View File

@ -28,7 +28,7 @@ end
ENV["REDIS_URL"] ||= "redis://localhost/15"
Sidekiq.logger = ::Logger.new(STDOUT)
Sidekiq.logger = ::Logger.new($stdout)
Sidekiq.logger.level = Logger::ERROR
if ENV["SIDEKIQ_REDIS_CLIENT"]
@ -48,3 +48,20 @@ def capture_logging(lvl = Logger::INFO)
Sidekiq.logger = old
end
end
module Sidekiq
def self.reset!
@config = DEFAULTS.dup
end
end
Signal.trap("TTIN") do
Thread.list.each do |thread|
puts "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
if thread.backtrace
puts thread.backtrace.join("\n")
else
puts "<no backtrace available>"
end
end
end

View File

@ -6,23 +6,29 @@ require "sidekiq/fetch"
require "sidekiq/scheduled"
require "sidekiq/processor"
describe "Actors" do
class JoeWorker
include Sidekiq::Worker
def perform(slp)
raise "boom" if slp == "boom"
sleep(slp) if slp > 0
$count += 1
end
class JoeWorker
include Sidekiq::Job
def perform(slp)
raise "boom" if slp == "boom"
sleep(slp) if slp > 0
$count += 1
end
end
describe "Actors" do
before do
Sidekiq.reset!
Sidekiq.redis { |c| c.flushdb }
@config = Sidekiq
@config[:queues] = %w[default]
@config[:fetch] = Sidekiq::BasicFetch.new(@config)
@config[:error_handlers] << Sidekiq.method(:default_error_handler)
# @config.logger.level = Logger::DEBUG
end
describe "scheduler" do
it "can start and stop" do
f = Sidekiq::Scheduled::Poller.new
f = Sidekiq::Scheduled::Poller.new(@config)
f.start
f.terminate
end
@ -37,7 +43,7 @@ describe "Actors" do
assert_equal 1, ss.size
sleep 0.015
s = Sidekiq::Scheduled::Poller.new
s = Sidekiq::Scheduled::Poller.new(@config)
s.enqueue
assert_equal 1, q.size
assert_equal 0, ss.size
@ -48,84 +54,82 @@ describe "Actors" do
describe "processor" do
before do
$count = 0
@mutex = ::Mutex.new
@cond = ::ConditionVariable.new
@latest_error = nil
end
def result(pr, ex)
@latest_error = ex
@mutex.synchronize do
@cond.signal
end
end
def await(timeout = 0.5)
@mutex.synchronize do
yield
@cond.wait(@mutex, timeout)
end
end
it "can start and stop" do
m = Mgr.new
f = Sidekiq::Processor.new(m, m.options)
f = Sidekiq::Processor.new(@config) { |p, ex| raise "should not raise!" }
f.terminate
end
class Mgr
attr_reader :latest_error
attr_reader :mutex
attr_reader :cond
def initialize
@mutex = ::Mutex.new
@cond = ::ConditionVariable.new
end
def processor_died(inst, err)
@latest_error = err
@mutex.synchronize do
@cond.signal
end
end
def processor_stopped(inst)
@mutex.synchronize do
@cond.signal
end
end
def options
opts = {concurrency: 3, queues: ["default"]}
opts[:fetch] = Sidekiq::BasicFetch.new(opts)
opts
end
end
it "can process" do
mgr = Mgr.new
p = Sidekiq::Processor.new(mgr, mgr.options)
JoeWorker.perform_async(0)
a = $count
p.process_one
b = $count
assert_equal a + 1, b
end
it "deals with errors" do
mgr = Mgr.new
p = Sidekiq::Processor.new(mgr, mgr.options)
JoeWorker.perform_async("boom")
q = Sidekiq::Queue.new
assert_equal 0, q.size
p = Sidekiq::Processor.new(@config) do |pr, ex|
result(pr, ex)
end
JoeWorker.perform_async(0)
assert_equal 1, q.size
a = $count
mgr.mutex.synchronize do
await do
p.start
end
p.kill(true)
b = $count
assert_nil @latest_error
assert_equal a + 1, b
assert_equal 0, q.size
end
it "deals with errors" do
q = Sidekiq::Queue.new
assert_equal 0, q.size
p = Sidekiq::Processor.new(@config) do |pr, ex|
result(pr, ex)
end
jid = JoeWorker.perform_async("boom")
assert jid, jid
assert_equal 1, q.size
a = $count
await do
p.start
mgr.cond.wait(mgr.mutex)
end
b = $count
assert_equal a, b
sleep 0.001
assert_equal false, p.thread.status
p.terminate(true)
refute_nil mgr.latest_error
assert_equal RuntimeError, mgr.latest_error.class
p.kill(true)
assert @latest_error
assert_equal "boom", @latest_error.message
assert_equal RuntimeError, @latest_error.class
end
it "gracefully kills" do
mgr = Mgr.new
p = Sidekiq::Processor.new(mgr, mgr.options)
JoeWorker.perform_async(1)
q = Sidekiq::Queue.new
assert_equal 0, q.size
p = Sidekiq::Processor.new(@config) do |pr, ex|
result(pr, ex)
end
jid = JoeWorker.perform_async(1)
assert jid, jid
assert_equal 1, q.size
a = $count
@ -137,7 +141,7 @@ describe "Actors" do
b = $count
assert_equal a, b
assert_equal false, p.thread.status
refute mgr.latest_error, mgr.latest_error.to_s
refute @latest_error, @latest_error.to_s
end
end
end

View File

@ -6,17 +6,22 @@ require "sidekiq/cli"
describe Sidekiq::CLI do
describe "#parse" do
before do
Sidekiq.options = Sidekiq::DEFAULTS.dup
Sidekiq.reset!
@logger = Sidekiq.logger
@logdev = StringIO.new
Sidekiq.logger = Logger.new(@logdev)
@config = Sidekiq
end
attr_reader :config
after do
Sidekiq.logger = @logger
end
subject { Sidekiq::CLI.new }
subject do
Sidekiq::CLI.new.tap { |c| c.config = config }
end
def logdev
@logdev ||= StringIO.new
@ -28,7 +33,7 @@ describe Sidekiq::CLI do
it "accepts with -r" do
subject.parse(%w[sidekiq -r ./test/fake_env.rb])
assert_equal "./test/fake_env.rb", Sidekiq.options[:require]
assert_equal "./test/fake_env.rb", config[:require]
end
end
@ -36,7 +41,7 @@ describe Sidekiq::CLI do
it "accepts with -c" do
subject.parse(%w[sidekiq -c 60 -r ./test/fake_env.rb])
assert_equal 60, Sidekiq.options[:concurrency]
assert_equal 60, config[:concurrency]
end
describe "when concurrency is empty and RAILS_MAX_THREADS env var is set" do
@ -51,13 +56,13 @@ describe Sidekiq::CLI do
it "sets concurrency from RAILS_MAX_THREADS env var" do
subject.parse(%w[sidekiq -r ./test/fake_env.rb])
assert_equal 9, Sidekiq.options[:concurrency]
assert_equal 9, config[:concurrency]
end
it "option overrides RAILS_MAX_THREADS env var" do
subject.parse(%w[sidekiq -c 60 -r ./test/fake_env.rb])
assert_equal 60, Sidekiq.options[:concurrency]
assert_equal 60, config[:concurrency]
end
end
end
@ -67,7 +72,7 @@ describe Sidekiq::CLI do
it "discards the `strict` option specified via the config file" do
subject.parse(%w[sidekiq -C ./test/config_with_internal_options.yml])
assert_equal true, !!Sidekiq.options[:strict]
assert_equal true, !!config[:strict]
end
end
end
@ -76,20 +81,20 @@ describe Sidekiq::CLI do
it "accepts with -q" do
subject.parse(%w[sidekiq -q foo -r ./test/fake_env.rb])
assert_equal ["foo"], Sidekiq.options[:queues]
assert_equal ["foo"], config[:queues]
end
describe "when weights are not present" do
it "accepts queues without weights" do
subject.parse(%w[sidekiq -q foo -q bar -r ./test/fake_env.rb])
assert_equal ["foo", "bar"], Sidekiq.options[:queues]
assert_equal ["foo", "bar"], config[:queues]
end
it "sets strictly ordered queues" do
subject.parse(%w[sidekiq -q foo -q bar -r ./test/fake_env.rb])
assert_equal true, !!Sidekiq.options[:strict]
assert_equal true, !!config[:strict]
end
end
@ -97,26 +102,26 @@ describe Sidekiq::CLI do
it "accepts queues with weights" do
subject.parse(%w[sidekiq -q foo,3 -q bar -r ./test/fake_env.rb])
assert_equal ["foo", "foo", "foo", "bar"], Sidekiq.options[:queues]
assert_equal ["foo", "foo", "foo", "bar"], config[:queues]
end
it "does not set strictly ordered queues" do
subject.parse(%w[sidekiq -q foo,3 -q bar -r ./test/fake_env.rb])
assert_equal false, !!Sidekiq.options[:strict]
assert_equal false, !!config[:strict]
end
end
it "accepts queues with multi-word names" do
subject.parse(%w[sidekiq -q queue_one -q queue-two -r ./test/fake_env.rb])
assert_equal ["queue_one", "queue-two"], Sidekiq.options[:queues]
assert_equal ["queue_one", "queue-two"], config[:queues]
end
it "accepts queues with dots in the name" do
subject.parse(%w[sidekiq -q foo.bar -r ./test/fake_env.rb])
assert_equal ["foo.bar"], Sidekiq.options[:queues]
assert_equal ["foo.bar"], config[:queues]
end
describe "when duplicate queue names" do
@ -131,7 +136,7 @@ describe Sidekiq::CLI do
it "sets 'default' queue" do
subject.parse(%w[sidekiq -r ./test/fake_env.rb])
assert_equal ["default"], Sidekiq.options[:queues]
assert_equal ["default"], config[:queues]
end
end
@ -139,7 +144,7 @@ describe Sidekiq::CLI do
it "sets 'default' queue" do
subject.parse(%w[sidekiq -C ./test/config_empty.yml -r ./test/fake_env.rb])
assert_equal ["default"], Sidekiq.options[:queues]
assert_equal ["default"], config[:queues]
end
end
end
@ -149,7 +154,7 @@ describe Sidekiq::CLI do
it "accepts with -t" do
subject.parse(%w[sidekiq -t 30 -r ./test/fake_env.rb])
assert_equal 30, Sidekiq.options[:timeout]
assert_equal 30, config[:timeout]
end
end
@ -165,57 +170,57 @@ describe Sidekiq::CLI do
it "accepts with -C" do
subject.parse(%w[sidekiq -C ./test/config.yml])
assert_equal "./test/config.yml", Sidekiq.options[:config_file]
refute Sidekiq.options[:verbose]
assert_equal "./test/fake_env.rb", Sidekiq.options[:require]
assert_nil Sidekiq.options[:environment]
assert_equal 50, Sidekiq.options[:concurrency]
assert_equal 2, Sidekiq.options[:queues].count { |q| q == "very_often" }
assert_equal 1, Sidekiq.options[:queues].count { |q| q == "seldom" }
assert_equal "./test/config.yml", config[:config_file]
refute config[:verbose]
assert_equal "./test/fake_env.rb", config[:require]
assert_nil config[:environment]
assert_equal 50, config[:concurrency]
assert_equal 2, config[:queues].count { |q| q == "very_often" }
assert_equal 1, config[:queues].count { |q| q == "seldom" }
end
it "accepts stringy keys" do
subject.parse(%w[sidekiq -C ./test/config_string.yml])
assert_equal "./test/config_string.yml", Sidekiq.options[:config_file]
refute Sidekiq.options[:verbose]
assert_equal "./test/fake_env.rb", Sidekiq.options[:require]
assert_nil Sidekiq.options[:environment]
assert_equal 50, Sidekiq.options[:concurrency]
assert_equal 2, Sidekiq.options[:queues].count { |q| q == "very_often" }
assert_equal 1, Sidekiq.options[:queues].count { |q| q == "seldom" }
assert_equal "./test/config_string.yml", config[:config_file]
refute config[:verbose]
assert_equal "./test/fake_env.rb", config[:require]
assert_nil config[:environment]
assert_equal 50, config[:concurrency]
assert_equal 2, config[:queues].count { |q| q == "very_often" }
assert_equal 1, config[:queues].count { |q| q == "seldom" }
end
it "accepts environment specific config" do
subject.parse(%w[sidekiq -e staging -C ./test/config_environment.yml])
assert_equal "./test/config_environment.yml", Sidekiq.options[:config_file]
refute Sidekiq.options[:verbose]
assert_equal "./test/fake_env.rb", Sidekiq.options[:require]
assert_equal "staging", Sidekiq.options[:environment]
assert_equal 50, Sidekiq.options[:concurrency]
assert_equal 2, Sidekiq.options[:queues].count { |q| q == "very_often" }
assert_equal 1, Sidekiq.options[:queues].count { |q| q == "seldom" }
assert_equal "./test/config_environment.yml", config[:config_file]
refute config[:verbose]
assert_equal "./test/fake_env.rb", config[:require]
assert_equal "staging", config[:environment]
assert_equal 50, config[:concurrency]
assert_equal 2, config[:queues].count { |q| q == "very_often" }
assert_equal 1, config[:queues].count { |q| q == "seldom" }
end
it "accepts environment specific config with alias" do
subject.parse(%w[sidekiq -e staging -C ./test/config_with_alias.yml])
assert_equal "./test/config_with_alias.yml", Sidekiq.options[:config_file]
refute Sidekiq.options[:verbose]
assert_equal "./test/fake_env.rb", Sidekiq.options[:require]
assert_equal "staging", Sidekiq.options[:environment]
assert_equal 50, Sidekiq.options[:concurrency]
assert_equal 2, Sidekiq.options[:queues].count { |q| q == "very_often" }
assert_equal 1, Sidekiq.options[:queues].count { |q| q == "seldom" }
assert_equal "./test/config_with_alias.yml", config[:config_file]
refute config[:verbose]
assert_equal "./test/fake_env.rb", config[:require]
assert_equal "staging", config[:environment]
assert_equal 50, config[:concurrency]
assert_equal 2, config[:queues].count { |q| q == "very_often" }
assert_equal 1, config[:queues].count { |q| q == "seldom" }
subject.parse(%w[sidekiq -e production -C ./test/config_with_alias.yml])
assert_equal "./test/config_with_alias.yml", Sidekiq.options[:config_file]
assert Sidekiq.options[:verbose]
assert_equal "./test/fake_env.rb", Sidekiq.options[:require]
assert_equal "production", Sidekiq.options[:environment]
assert_equal 50, Sidekiq.options[:concurrency]
assert_equal 2, Sidekiq.options[:queues].count { |q| q == "very_often" }
assert_equal 1, Sidekiq.options[:queues].count { |q| q == "seldom" }
assert_equal "./test/config_with_alias.yml", config[:config_file]
assert config[:verbose]
assert_equal "./test/fake_env.rb", config[:require]
assert_equal "production", config[:environment]
assert_equal 50, config[:concurrency]
assert_equal 2, config[:queues].count { |q| q == "very_often" }
assert_equal 1, config[:queues].count { |q| q == "seldom" }
end
it "exposes ERB expected __FILE__ and __dir__" do
@ -226,8 +231,8 @@ describe Sidekiq::CLI do
subject.parse(%W[sidekiq -C #{given_path}])
assert_equal(expected_file, Sidekiq.options.fetch(:__FILE__))
assert_equal(expected_dir, Sidekiq.options.fetch(:__dir__))
assert_equal(expected_file, config.fetch(:__FILE__))
assert_equal(expected_dir, config.fetch(:__dir__))
end
end
@ -236,30 +241,30 @@ describe Sidekiq::CLI do
it "tries config/sidekiq.yml from required diretory" do
subject.parse(%w[sidekiq -r ./test/dummy])
assert_equal "./test/dummy/config/sidekiq.yml", Sidekiq.options[:config_file]
assert_equal 25, Sidekiq.options[:concurrency]
assert_equal "./test/dummy/config/sidekiq.yml", config[:config_file]
assert_equal 25, config[:concurrency]
end
end
describe "when required path is a file" do
it "tries config/sidekiq.yml from current diretory" do
Sidekiq.options[:require] = "./test/dummy" # stub current dir ./
config[:require] = "./test/dummy" # stub current dir ./
subject.parse(%w[sidekiq -r ./test/fake_env.rb])
assert_equal "./test/dummy/config/sidekiq.yml", Sidekiq.options[:config_file]
assert_equal 25, Sidekiq.options[:concurrency]
assert_equal "./test/dummy/config/sidekiq.yml", config[:config_file]
assert_equal 25, config[:concurrency]
end
end
describe "without any required path" do
it "tries config/sidekiq.yml from current diretory" do
Sidekiq.options[:require] = "./test/dummy" # stub current dir ./
config[:require] = "./test/dummy" # stub current dir ./
subject.parse(%w[sidekiq])
assert_equal "./test/dummy/config/sidekiq.yml", Sidekiq.options[:config_file]
assert_equal 25, Sidekiq.options[:concurrency]
assert_equal "./test/dummy/config/sidekiq.yml", config[:config_file]
assert_equal 25, config[:concurrency]
end
end
@ -272,13 +277,13 @@ describe Sidekiq::CLI do
-q often,7
-q seldom,3])
assert_equal "./test/config.yml", Sidekiq.options[:config_file]
refute Sidekiq.options[:verbose]
assert_equal "./test/fake_env.rb", Sidekiq.options[:require]
assert_equal "snoop", Sidekiq.options[:environment]
assert_equal 100, Sidekiq.options[:concurrency]
assert_equal 7, Sidekiq.options[:queues].count { |q| q == "often" }
assert_equal 3, Sidekiq.options[:queues].count { |q| q == "seldom" }
assert_equal "./test/config.yml", config[:config_file]
refute config[:verbose]
assert_equal "./test/fake_env.rb", config[:require]
assert_equal "snoop", config[:environment]
assert_equal 100, config[:concurrency]
assert_equal 7, config[:queues].count { |q| q == "often" }
assert_equal 3, config[:queues].count { |q| q == "seldom" }
end
describe "when the config file specifies queues with weights" do
@ -288,7 +293,7 @@ describe Sidekiq::CLI do
-r ./test/fake_env.rb
-q foo -q bar])
assert_equal true, !!Sidekiq.options[:strict]
assert_equal true, !!config[:strict]
end
end
@ -297,7 +302,7 @@ describe Sidekiq::CLI do
subject.parse(%w[sidekiq -C ./test/config.yml
-r ./test/fake_env.rb])
assert_equal false, !!Sidekiq.options[:strict]
assert_equal false, !!config[:strict]
end
end
@ -307,7 +312,7 @@ describe Sidekiq::CLI do
-r ./test/fake_env.rb
-q foo,2 -q bar,3])
assert_equal false, !!Sidekiq.options[:strict]
assert_equal false, !!config[:strict]
end
end
end
@ -319,7 +324,7 @@ describe Sidekiq::CLI do
-r ./test/fake_env.rb
-q foo -q bar])
assert_equal true, !!Sidekiq.options[:strict]
assert_equal true, !!config[:strict]
end
end
@ -328,7 +333,7 @@ describe Sidekiq::CLI do
subject.parse(%w[sidekiq -C ./test/config_queues_without_weights.yml
-r ./test/fake_env.rb])
assert_equal true, !!Sidekiq.options[:strict]
assert_equal true, !!config[:strict]
end
end
@ -338,7 +343,7 @@ describe Sidekiq::CLI do
-r ./test/fake_env.rb
-q foo,2 -q bar,3])
assert_equal false, !!Sidekiq.options[:strict]
assert_equal false, !!config[:strict]
end
end
end
@ -350,7 +355,7 @@ describe Sidekiq::CLI do
-r ./test/fake_env.rb
-q foo -q bar])
assert_equal true, !!Sidekiq.options[:strict]
assert_equal true, !!config[:strict]
end
end
@ -359,7 +364,7 @@ describe Sidekiq::CLI do
subject.parse(%w[sidekiq -C ./test/config_empty.yml
-r ./test/fake_env.rb])
assert_equal true, !!Sidekiq.options[:strict]
assert_equal true, !!config[:strict]
end
end
@ -369,7 +374,7 @@ describe Sidekiq::CLI do
-r ./test/fake_env.rb
-q foo,2 -q bar,3])
assert_equal false, !!Sidekiq.options[:strict]
assert_equal false, !!config[:strict]
end
end
end
@ -380,8 +385,8 @@ describe Sidekiq::CLI do
it "tries config/sidekiq.yml" do
subject.parse(%w[sidekiq -r ./test/dummy])
assert_equal "sidekiq.yml", File.basename(Sidekiq.options[:config_file])
assert_equal 25, Sidekiq.options[:concurrency]
assert_equal "sidekiq.yml", File.basename(config[:config_file])
assert_equal 25, config[:concurrency]
end
end
end
@ -451,14 +456,15 @@ describe Sidekiq::CLI do
describe "#run" do
before do
Sidekiq.options[:concurrency] = 2
Sidekiq.options[:require] = "./test/fake_env.rb"
subject.config = Sidekiq
subject.config[:concurrency] = 2
subject.config[:require] = "./test/fake_env.rb"
end
describe "require workers" do
describe "when path is a rails directory" do
before do
Sidekiq.options[:require] = "./test/dummy"
subject.config[:require] = "./test/dummy"
subject.environment = "test"
end
@ -476,7 +482,7 @@ describe Sidekiq::CLI do
subject.run
end
assert_equal "dummy", Sidekiq.options[:tag]
assert_equal "dummy", subject.config[:tag]
end
end
@ -556,11 +562,12 @@ describe Sidekiq::CLI do
it "quiets with a corresponding event" do
quiet = false
Sidekiq.on(:quiet) do
subject.config = Sidekiq
subject.config.on(:quiet) do
quiet = true
end
subject.launcher = Sidekiq::Launcher.new(Sidekiq.options)
subject.launcher = Sidekiq::Launcher.new(subject.config)
subject.handle_signal("TSTP")
assert_match(/Got TSTP signal/, logdev.string)

View File

@ -132,13 +132,13 @@ describe Sidekiq::Client do
end
describe "argument checking" do
before do
Sidekiq.strict_args!(false)
end
before do
Sidekiq.strict_args!(false)
end
after do
Sidekiq.strict_args!(:raise)
end
after do
Sidekiq.strict_args!(:raise)
end
class InterestingWorker
include Sidekiq::Worker

View File

@ -1,17 +1,19 @@
# frozen_string_literal: true
require_relative "./helper"
require "sidekiq/web/csrf_protection"
class TestCsrf < Minitest::Test
describe "Csrf" do
def session
@session ||= {}
end
def env(method = :get, form_hash = {}, rack_session = session)
imp = StringIO.new("")
imp = StringIO.new
{
"REQUEST_METHOD" => method.to_s.upcase,
"rack.session" => rack_session,
"rack.logger" => ::Logger.new(@logio ||= StringIO.new("")),
"rack.logger" => ::Logger.new(@logio ||= StringIO.new),
"rack.input" => imp,
"rack.request.form_input" => imp,
"rack.request.form_hash" => form_hash
@ -22,7 +24,7 @@ class TestCsrf < Minitest::Test
Sidekiq::Web::CsrfProtection.new(block).call(env)
end
def test_get
it "get" do
ok = [200, {}, ["OK"]]
first = 1
second = 1
@ -46,7 +48,7 @@ class TestCsrf < Minitest::Test
refute_equal first, second
end
def test_bad_post
it "bad post" do
result = call(env(:post)) do
raise "Shouldnt be called"
end
@ -58,7 +60,7 @@ class TestCsrf < Minitest::Test
assert_match(/attack prevented/, @logio.string)
end
def test_good_and_bad_posts
it "succeeds with good token" do
# Make a GET to set up the session with a good token
goodtoken = call(env) do |envy|
envy[:csrf_token]
@ -72,7 +74,9 @@ class TestCsrf < Minitest::Test
refute_nil result
assert_equal 200, result[0]
assert_equal ["OK"], result[2]
end
it "fails with bad token" do
# Make a POST with a known bad token
result = call(env(:post, "authenticity_token" => "N0QRBD34tU61d7fi+0ZaF/35JLW/9K+8kk8dc1TZoK/0pTl7GIHap5gy7BWGsoKlzbMLRp1yaDpCDFwTJtxWAg==")) do
raise "shouldnt be called"
@ -82,7 +86,7 @@ class TestCsrf < Minitest::Test
assert_equal ["Forbidden"], result[2]
end
def test_empty_session_post
it "empty session post" do
# Make a GET to set up the session with a good token
goodtoken = call(env) do |envy|
envy[:csrf_token]
@ -98,7 +102,8 @@ class TestCsrf < Minitest::Test
assert_equal ["Forbidden"], result[2]
end
def test_empty_csrf_session_post
it "empty csrf session post" do
# Make a GET to set up the session with a good token
goodtoken = call(env) do |envy|
envy[:csrf_token]
end

View File

@ -1,5 +1,8 @@
# frozen_string_literal: true
require_relative "./helper"
require "sidekiq/middleware/current_attributes"
require "sidekiq/fetch"
module Myapp
class Current < ActiveSupport::CurrentAttributes
@ -7,8 +10,8 @@ module Myapp
end
end
class TestCurrentAttributes < Minitest::Test
def test_save
describe "Current attributes" do
it "saves" do
cm = Sidekiq::CurrentAttributes::Save.new(Myapp::Current)
job = {}
with_context(:user_id, 123) do
@ -18,7 +21,7 @@ class TestCurrentAttributes < Minitest::Test
end
end
def test_load
it "loads" do
cm = Sidekiq::CurrentAttributes::Load.new(Myapp::Current)
job = {"cattr" => {"user_id" => 123}}
@ -29,7 +32,7 @@ class TestCurrentAttributes < Minitest::Test
# the Rails reloader is responsible for reseting Current after every unit of work
end
def test_persist
it "persists" do
Sidekiq::CurrentAttributes.persist(Myapp::Current)
job_hash = {}
with_context(:user_id, 16) do
@ -38,15 +41,15 @@ class TestCurrentAttributes < Minitest::Test
end
end
assert_nil Myapp::Current.user_id
Sidekiq.server_middleware.invoke(nil, job_hash, nil) do
assert_equal 16, job_hash["cattr"][:user_id]
assert_equal 16, Myapp::Current.user_id
end
assert_nil Myapp::Current.user_id
ensure
Sidekiq.client_middleware.clear
Sidekiq.server_middleware.clear
# assert_nil Myapp::Current.user_id
# Sidekiq.server_middleware.invoke(nil, job_hash, nil) do
# assert_equal 16, job_hash["cattr"][:user_id]
# assert_equal 16, Myapp::Current.user_id
# end
# assert_nil Myapp::Current.user_id
# ensure
# Sidekiq.client_middleware.clear
# Sidekiq.server_middleware.clear
end
private

View File

@ -1,15 +1,20 @@
# frozen_string_literal: true
require_relative "helper"
require "sidekiq/exception_handler"
require "sidekiq/component"
require "stringio"
require "logger"
ExceptionHandlerTestException = Class.new(StandardError)
TEST_EXCEPTION = ExceptionHandlerTestException.new("Something didn't work!")
class Component
include Sidekiq::ExceptionHandler
class Thing
include Sidekiq::Component
attr_reader :config
def initialize(config)
@config = config
end
def invoke_exception(args)
raise TEST_EXCEPTION
@ -18,25 +23,23 @@ class Component
end
end
describe Sidekiq::ExceptionHandler do
describe Sidekiq::Component do
describe "with mock logger" do
before do
@old_logger = Sidekiq.logger
@str_logger = StringIO.new
Sidekiq.logger = Logger.new(@str_logger)
@config = Sidekiq
@config[:error_handlers] << Sidekiq.method(:default_error_handler)
end
after do
Sidekiq.logger = @old_logger
@config[:error_handlers].clear
end
it "logs the exception to Sidekiq.logger" do
Component.new.invoke_exception(a: 1)
@str_logger.rewind
log = @str_logger.readlines
assert_match(/"a":1/, log[0], "didn't include the context")
assert_match(/Something didn't work!/, log[1], "didn't include the exception message")
assert_match(/test\/test_exception_handler.rb/, log[2], "didn't include the backtrace")
output = capture_logging do
Thing.new(@config).invoke_exception(a: 1)
end
assert_match(/"a":1/, output, "didn't include the context")
assert_match(/Something didn't work!/, output, "didn't include the exception message")
assert_match(/test\/test_exception_handler.rb/, output, "didn't include the backtrace")
end
describe "when the exception does not have a backtrace" do
@ -44,12 +47,7 @@ describe Sidekiq::ExceptionHandler do
exception = ExceptionHandlerTestException.new
assert_nil exception.backtrace
begin
Component.new.handle_exception exception
pass
rescue
flunk "failed handling a nil backtrace"
end
Thing.new(@config).handle_exception exception
end
end
end

View File

@ -6,19 +6,21 @@ require "sidekiq/api"
describe Sidekiq::BasicFetch do
before do
@prev_redis = Sidekiq.instance_variable_get(:@redis) || {}
Sidekiq.redis do |conn|
conn.flushdb
conn.rpush("queue:basic", "msg")
end
Sidekiq.reset!
@config = Sidekiq
end
after do
Sidekiq.redis = @prev_redis
def fetcher(options)
@config.merge!(options)
Sidekiq::BasicFetch.new(@config)
end
it "retrieves" do
fetch = Sidekiq::BasicFetch.new(queues: ["basic", "bar"])
fetch = fetcher(queues: ["basic", "bar"])
uow = fetch.retrieve_work
refute_nil uow
assert_equal "basic", uow.queue_name
@ -31,7 +33,7 @@ describe Sidekiq::BasicFetch do
end
it "retrieves with strict setting" do
fetch = Sidekiq::BasicFetch.new(queues: ["basic", "bar", "bar"], strict: true)
fetch = fetcher(queues: ["basic", "bar", "bar"], strict: true)
cmd = fetch.queues_cmd
assert_equal cmd, ["queue:basic", "queue:bar", Sidekiq::BasicFetch::TIMEOUT]
end
@ -47,7 +49,7 @@ describe Sidekiq::BasicFetch do
assert_equal 2, q1.size
assert_equal 1, q2.size
fetch = Sidekiq::BasicFetch.new(queues: ["foo", "bar"])
fetch = fetcher(queues: ["foo", "bar"])
works = 3.times.map { fetch.retrieve_work }
assert_equal 0, q1.size
assert_equal 0, q2.size
@ -58,7 +60,7 @@ describe Sidekiq::BasicFetch do
end
it "sleeps when no queues are active" do
fetch = Sidekiq::BasicFetch.new(queues: [])
fetch = fetcher(queues: [])
mock = Minitest::Mock.new
mock.expect(:call, nil, [Sidekiq::BasicFetch::TIMEOUT])
fetch.stub(:sleep, mock) { assert_nil fetch.retrieve_work }

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative "helper"
describe Sidekiq::Job do

View File

@ -3,8 +3,8 @@
require_relative "helper"
require "sidekiq/job_logger"
class TestJobLogger < Minitest::Test
def setup
describe "Job logger" do
before do
@old = Sidekiq.logger
@output = StringIO.new
@logger = Sidekiq::Logger.new(@output, level: :info)
@ -14,13 +14,13 @@ class TestJobLogger < Minitest::Test
Thread.current[:sidekiq_tid] = nil
end
def teardown
after do
Thread.current[:sidekiq_context] = nil
Thread.current[:sidekiq_tid] = nil
Sidekiq.logger = @old
end
def test_pretty_output
it "tests pretty output" do
jl = Sidekiq::JobLogger.new(@logger)
# pretty
@ -41,7 +41,7 @@ class TestJobLogger < Minitest::Test
assert_match(/#{Time.now.utc.to_date}.+Z pid=#{$$} tid=#{p.tid} .+INFO: done/, b)
end
def test_json_output
it "tests json output" do
# json
@logger.formatter = Sidekiq::Logger::Formatters::JSON.new
jl = Sidekiq::JobLogger.new(@logger)
@ -60,7 +60,7 @@ class TestJobLogger < Minitest::Test
assert_equal(["bid", "class", "jid", "tags"], keys)
end
def test_custom_log_level
it "tests custom log level" do
jl = Sidekiq::JobLogger.new(@logger)
job = {"class" => "FooWorker", "log_level" => "debug"}
@ -79,7 +79,7 @@ class TestJobLogger < Minitest::Test
assert_match(/INFO: done/, c)
end
def test_custom_log_level_uses_default_log_level_for_invalid_value
it "tests custom log level uses default log level for invalid value" do
jl = Sidekiq::JobLogger.new(@logger)
job = {"class" => "FooWorker", "log_level" => "non_existent"}
@ -94,7 +94,7 @@ class TestJobLogger < Minitest::Test
assert_match(/WARN: Invalid log level/, log_level_warning)
end
def test_custom_logger_with_non_numeric_levels
it "tests custom logger with non numeric levels" do
logger_class = Class.new(Logger) do
def level
:nonsense

View File

@ -4,9 +4,16 @@ require_relative "helper"
require "sidekiq/launcher"
describe Sidekiq::Launcher do
subject { Sidekiq::Launcher.new(options) }
subject do
Sidekiq::Launcher.new(@config)
end
before do
Sidekiq.redis { |c| c.flushdb }
Sidekiq.reset!
@config = Sidekiq
@config[:tag] = "myapp"
@config[:concurrency] = 3
end
def new_manager(opts)
@ -23,8 +30,8 @@ describe Sidekiq::Launcher do
describe "heartbeat" do
before do
@mgr = new_manager(options)
@launcher = Sidekiq::Launcher.new(options)
@mgr = new_manager(@config)
@launcher = Sidekiq::Launcher.new(@config)
@launcher.manager = @mgr
@id = @launcher.identity
@ -157,8 +164,4 @@ describe Sidekiq::Launcher do
end
end
end
def options
{concurrency: 3, queues: ["default"], tag: "myapp"}
end
end

View File

@ -3,8 +3,8 @@
require_relative "helper"
require "sidekiq/logger"
class TestLogger < Minitest::Test
def setup
describe "logger" do
before do
@output = StringIO.new
@logger = Sidekiq::Logger.new(@output)
@ -13,30 +13,30 @@ class TestLogger < Minitest::Test
Thread.current[:sidekiq_tid] = nil
end
def teardown
after do
Sidekiq.log_formatter = nil
Thread.current[:sidekiq_context] = nil
Thread.current[:sidekiq_tid] = nil
end
def test_default_log_formatter
it "tests default logger format" do
assert_kind_of Sidekiq::Logger::Formatters::Pretty, Sidekiq::Logger.new(@output).formatter
end
def test_heroku_log_formatter
it "tests heroku logger formatter" do
ENV["DYNO"] = "dyno identifier"
assert_kind_of Sidekiq::Logger::Formatters::WithoutTimestamp, Sidekiq::Logger.new(@output).formatter
ensure
ENV["DYNO"] = nil
end
def test_json_log_formatter
it "tests json logger formatter" do
Sidekiq.log_formatter = Sidekiq::Logger::Formatters::JSON.new
assert_kind_of Sidekiq::Logger::Formatters::JSON, Sidekiq::Logger.new(@output).formatter
end
def test_with_context
it "tests with context" do
subject = Sidekiq::Context
assert_equal({}, subject.current)
@ -47,9 +47,9 @@ class TestLogger < Minitest::Test
assert_equal({}, subject.current)
end
def test_with_overlapping_context
it "tests with overlapping context" do
subject = Sidekiq::Context
subject.current.merge!({foo: "bar"})
subject.current[:foo] = "bar"
assert_equal({foo: "bar"}, subject.current)
subject.with(foo: "bingo") do
@ -59,7 +59,7 @@ class TestLogger < Minitest::Test
assert_equal({foo: "bar"}, subject.current)
end
def test_nested_contexts
it "tests nested contexts" do
subject = Sidekiq::Context
assert_equal({}, subject.current)
@ -76,7 +76,7 @@ class TestLogger < Minitest::Test
assert_equal({}, subject.current)
end
def test_formatted_output
it "tests formatted output" do
@logger.info("hello world")
assert_match(/INFO: hello world/, @output.string)
reset(@output)
@ -96,7 +96,7 @@ class TestLogger < Minitest::Test
end
end
def test_json_output_is_parsable
it "makes sure json output is parseable" do
@logger.formatter = Sidekiq::Logger::Formatters::JSON.new
@logger.debug("boom")
@ -118,7 +118,7 @@ class TestLogger < Minitest::Test
assert_equal "INFO", hash["lvl"]
end
def test_forwards_logger_kwargs
it "tests forwards logger kwards" do
assert_silent do
logger = Sidekiq::Logger.new("/dev/null", level: Logger::INFO)
@ -126,7 +126,7 @@ class TestLogger < Minitest::Test
end
end
def test_log_level_query_methods
it "tests log level query methods" do
logger = Sidekiq::Logger.new("/dev/null", level: Logger::INFO)
refute_predicate logger, :debug?

View File

@ -6,42 +6,36 @@ require "sidekiq/manager"
describe Sidekiq::Manager do
before do
Sidekiq.redis { |c| c.flushdb }
Sidekiq.reset!
@config = Sidekiq
@config[:fetch] = Sidekiq::BasicFetch.new(@config)
end
def new_manager(opts)
Sidekiq::Manager.new(opts.merge(fetch: Sidekiq::BasicFetch.new(opts)))
def new_manager
Sidekiq::Manager.new(@config)
end
it "creates N processor instances" do
mgr = new_manager(options)
assert_equal options[:concurrency], mgr.workers.size
mgr = new_manager
assert_equal @config[:concurrency], mgr.workers.size
end
it "shuts down the system" do
mgr = new_manager(options)
mgr = new_manager
mgr.stop(::Process.clock_gettime(::Process::CLOCK_MONOTONIC))
end
it "throws away dead processors" do
mgr = new_manager(options)
mgr = new_manager
init_size = mgr.workers.size
processor = mgr.workers.first
begin
mgr.processor_died(processor, "ignored")
mgr.processor_result(processor, "ignored")
assert_equal init_size, mgr.workers.size
refute mgr.workers.include?(processor)
ensure
mgr.workers.each { |p| p.terminate(true) }
mgr.quiet
end
end
it "does not support invalid concurrency" do
assert_raises(ArgumentError) { new_manager(concurrency: 0) }
assert_raises(ArgumentError) { new_manager(concurrency: -1) }
end
def options
{concurrency: 3, queues: ["default"]}
end
end

View File

@ -77,17 +77,15 @@ describe Sidekiq::Middleware do
it "executes middleware in the proper order" do
msg = Sidekiq.dump_json({"class" => CustomWorker.to_s, "args" => [$recorder]})
Sidekiq.server_middleware do |chain|
@config = Sidekiq
@config.server_middleware do |chain|
# should only add once, second should replace the first
2.times { |i| chain.add CustomMiddleware, i.to_s, $recorder }
chain.insert_before CustomMiddleware, AnotherCustomMiddleware, "2", $recorder
chain.insert_after AnotherCustomMiddleware, YetAnotherCustomMiddleware, "3", $recorder
end
boss = Minitest::Mock.new
opts = {queues: ["default"]}
processor = Sidekiq::Processor.new(boss, opts)
boss.expect(:processor_done, nil, [processor])
processor = Sidekiq::Processor.new(@config) { |pr, ex| }
processor.process(Sidekiq::BasicFetch::UnitOfWork.new("queue:default", msg))
assert_equal %w[2 before 3 before 1 before work_performed 1 after 3 after 2 after], $recorder.flatten
end
@ -167,4 +165,28 @@ describe Sidekiq::Middleware do
I18n.available_locales = nil
end
end
class FooC
include Sidekiq::ClientMiddleware
def initialize(*args)
@args = args
end
def call(w, j, q, rp)
redis { |c| c.incr(self.class.name) }
logger.info { |c| [self.class.name, @args].inspect }
yield
end
end
describe "configuration" do
it "gets an object which provides redis and logging" do
cfg = Sidekiq
chain = Sidekiq::Middleware::Chain.new(cfg)
chain.add FooC, foo: "bar"
final_action = nil
chain.invoke(nil, nil, nil, nil) { final_action = true }
assert final_action
end
end
end

View File

@ -11,10 +11,9 @@ describe Sidekiq::Processor do
before do
$invokes = 0
@mgr = Minitest::Mock.new
opts = {queues: ["default"]}
opts[:fetch] = Sidekiq::BasicFetch.new(opts)
@processor = ::Sidekiq::Processor.new(@mgr, opts)
@config = Sidekiq
@config[:fetch] = Sidekiq::BasicFetch.new(@config)
@processor = ::Sidekiq::Processor.new(@config) { |*args| }
end
class MockWorker
@ -60,7 +59,6 @@ describe Sidekiq::Processor do
it "does not modify original arguments" do
msg = {"class" => MockWorker.to_s, "args" => [["myarg"]]}
msgstr = Sidekiq.dump_json(msg)
@mgr.expect(:processor_done, nil, [@processor])
@processor.process(work(msgstr))
assert_equal [["myarg"]], msg["args"]
end
@ -208,7 +206,6 @@ describe Sidekiq::Processor do
it "acks the job" do
work.expect(:acknowledge, nil)
@mgr.expect(:processor_done, nil, [@processor])
@processor.process(work)
end
end
@ -229,7 +226,6 @@ describe Sidekiq::Processor do
describe "everything goes well" do
it "acks the job" do
work.expect(:acknowledge, nil)
@mgr.expect(:processor_done, nil, [@processor])
@processor.process(work)
end
end
@ -306,7 +302,6 @@ describe Sidekiq::Processor do
def successful_job
msg = Sidekiq.dump_json({"class" => MockWorker.to_s, "args" => ["myarg"]})
@mgr.expect(:processor_done, nil, [@processor])
@processor.process(work(msg))
end
@ -328,8 +323,7 @@ describe Sidekiq::Processor do
before do
opts = {queues: ["default"], job_logger: CustomJobLogger}
@mgr = Minitest::Mock.new
@processor = ::Sidekiq::Processor.new(@mgr, opts)
@processor = ::Sidekiq::Processor.new(opts) { |pr, ex| }
end
end
end
@ -341,7 +335,6 @@ describe Sidekiq::Processor do
def successful_job
msg = Sidekiq.dump_json({"class" => MockWorker.to_s, "args" => ["myarg"]})
@mgr.expect(:processor_done, nil, [@processor])
@processor.process(work(msg))
end
@ -354,9 +347,10 @@ describe Sidekiq::Processor do
describe "custom job logger class" do
before do
opts = {queues: ["default"], job_logger: CustomJobLogger}
opts = Sidekiq
opts[:job_logger] = CustomJobLogger
opts[:fetch] = Sidekiq::BasicFetch.new(opts)
@processor = ::Sidekiq::Processor.new(nil, opts)
@processor = ::Sidekiq::Processor.new(opts) { |pr, ex| }
end
it "is called instead default Sidekiq::JobLogger" do

View File

@ -6,7 +6,7 @@ require "sidekiq/cli"
describe Sidekiq::RedisConnection do
describe "create" do
before do
Sidekiq.options = Sidekiq::DEFAULTS.dup
Sidekiq.reset!
@old = ENV["REDIS_URL"]
ENV["REDIS_URL"] = "redis://localhost/15"
end
@ -60,14 +60,15 @@ describe Sidekiq::RedisConnection do
it "defaults server pool sizes based on concurrency with padding" do
_expected_padding = 5
prev_concurrency = Sidekiq.options[:concurrency]
Sidekiq.options[:concurrency] = 6
config = Sidekiq
prev_concurrency = config[:concurrency]
config[:concurrency] = 6
pool = server_connection
assert_equal 11, pool.instance_eval { @size }
assert_equal 11, pool.instance_eval { @available.length }
ensure
Sidekiq.options[:concurrency] = prev_concurrency
config[:concurrency] = prev_concurrency
end
it "defaults client pool sizes to 5" do

View File

@ -5,28 +5,31 @@ require "sidekiq/scheduled"
require "sidekiq/job_retry"
require "sidekiq/api"
class SomeWorker
include Sidekiq::Worker
end
class BadErrorMessage < StandardError
def message
raise "Ahhh, this isn't supposed to happen"
end
end
describe Sidekiq::JobRetry do
before do
Sidekiq.redis { |c| c.flushdb }
@config = Sidekiq
@config[:max_retries] = 25
@config[:error_handlers] << Sidekiq.method(:default_error_handler)
end
describe "middleware" do
class SomeWorker
include Sidekiq::Worker
end
class BadErrorMessage < StandardError
def message
raise "Ahhh, this isn't supposed to happen"
end
end
before do
Sidekiq.redis { |c| c.flushdb }
end
def worker
@worker ||= SomeWorker.new
end
def handler(options = {})
@handler ||= Sidekiq::JobRetry.new(options)
def handler
@handler ||= Sidekiq::JobRetry.new(@config)
end
def jobstr(options = {})
@ -105,7 +108,8 @@ describe Sidekiq::JobRetry do
1.upto(max_retries + 1) do |i|
assert_raises RuntimeError do
job = i > 1 ? jobstr("retry_count" => i - 2) : jobstr
handler(max_retries: max_retries).local(worker, job, "default") do
@config[:max_retries] = max_retries
handler.local(worker, job, "default") do
raise "kerblammo!"
end
end
@ -119,7 +123,7 @@ describe Sidekiq::JobRetry do
c = nil
assert_raises RuntimeError do
handler.local(worker, jobstr("backtrace" => true), "default") do
c = caller(0); raise "kerblammo!"
(c = caller(0)) && raise("kerblammo!")
end
end
@ -132,13 +136,13 @@ describe Sidekiq::JobRetry do
c = nil
assert_raises RuntimeError do
handler.local(worker, jobstr("backtrace" => 3), "default") do
c = caller(0)[0...3]; raise "kerblammo!"
c = caller(0)[0...3]
raise "kerblammo!"
end
end
job = Sidekiq::RetrySet.new.first
assert job.error_backtrace
assert_equal c, job.error_backtrace
assert_equal 3, c.size
end
@ -221,6 +225,7 @@ describe Sidekiq::JobRetry do
raise "kerblammo!"
end
end
assert job, "No job found in retry set"
assert_equal "default", job["queue"]
assert_equal "kerblammo!", job["error_message"]
assert_equal "RuntimeError", job["error_class"]
@ -248,17 +253,6 @@ describe Sidekiq::JobRetry do
end
describe "custom retry delay" do
before do
@old_logger = Sidekiq.logger
@tmp_log_path = "/tmp/sidekiq-retries.log"
Sidekiq.logger = Logger.new(@tmp_log_path)
end
after do
Sidekiq.logger = @old_logger
File.unlink @tmp_log_path if File.exist?(@tmp_log_path)
end
class CustomWorkerWithoutException
include Sidekiq::Worker
@ -315,9 +309,11 @@ describe Sidekiq::JobRetry do
end
it "falls back to the default retry on exception" do
refute_equal 4, handler.__send__(:delay_for, ErrorWorker, 2, StandardError.new)
output = capture_logging do
refute_equal 4, handler.__send__(:delay_for, ErrorWorker, 2, StandardError.new)
end
assert_match(/Failure scheduling retry using the defined `sidekiq_retry_in`/,
File.read(@tmp_log_path), "Log entry missing for sidekiq_retry_in")
output, "Log entry missing for sidekiq_retry_in")
end
end
@ -332,7 +328,7 @@ describe Sidekiq::JobRetry do
end
it "does not recurse infinitely checking if it's a shutdown" do
assert(!Sidekiq::JobRetry.new.send(
assert(!Sidekiq::JobRetry.new(@config).send(
:exception_caused_by_shutdown?, @error
))
end
@ -357,7 +353,7 @@ describe Sidekiq::JobRetry do
end
it "does not recurse infinitely checking if it's a shutdown" do
assert(!Sidekiq::JobRetry.new.send(
assert(!Sidekiq::JobRetry.new(@config).send(
:exception_caused_by_shutdown?, @error
))
end

View File

@ -1,30 +1,30 @@
require_relative "helper"
require "sidekiq/job_retry"
class NewWorker
include Sidekiq::Worker
sidekiq_class_attribute :exhausted_called, :exhausted_job, :exhausted_exception
sidekiq_retries_exhausted do |job, e|
self.exhausted_called = true
self.exhausted_job = job
self.exhausted_exception = e
end
end
class OldWorker
include Sidekiq::Worker
sidekiq_class_attribute :exhausted_called, :exhausted_job, :exhausted_exception
sidekiq_retries_exhausted do |job|
self.exhausted_called = true
self.exhausted_job = job
end
end
describe "sidekiq_retries_exhausted" do
class NewWorker
include Sidekiq::Worker
sidekiq_class_attribute :exhausted_called, :exhausted_job, :exhausted_exception
sidekiq_retries_exhausted do |job, e|
self.exhausted_called = true
self.exhausted_job = job
self.exhausted_exception = e
end
end
class OldWorker
include Sidekiq::Worker
sidekiq_class_attribute :exhausted_called, :exhausted_job, :exhausted_exception
sidekiq_retries_exhausted do |job|
self.exhausted_called = true
self.exhausted_job = job
end
end
def cleanup
[NewWorker, OldWorker].each do |worker_class|
worker_class.exhausted_called = nil
@ -34,6 +34,7 @@ describe "sidekiq_retries_exhausted" do
end
before do
@config = Sidekiq
cleanup
end
@ -49,8 +50,8 @@ describe "sidekiq_retries_exhausted" do
@old_worker ||= OldWorker.new
end
def handler(options = {})
@handler ||= Sidekiq::JobRetry.new(options)
def handler
@handler ||= Sidekiq::JobRetry.new(@config)
end
def job(options = {})

View File

@ -20,9 +20,10 @@ describe Sidekiq::Scheduled do
@future_2 = {"class" => ScheduledWorker.name, "args" => [4], "queue" => "queue_5"}
@future_3 = {"class" => ScheduledWorker.name, "args" => [5], "queue" => "queue_6"}
@config = Sidekiq
@retry = Sidekiq::RetrySet.new
@scheduled = Sidekiq::ScheduledSet.new
@poller = Sidekiq::Scheduled::Poller.new
@poller = Sidekiq::Scheduled::Poller.new(@config)
end
class MyStopper
@ -105,11 +106,11 @@ describe Sidekiq::Scheduled do
end
def with_sidekiq_option(name, value)
_original, Sidekiq.options[name] = Sidekiq.options[name], value
original, Sidekiq[name] = Sidekiq[name], value
begin
yield
ensure
Sidekiq.options[name] = _original
Sidekiq[name] = original
end
end

View File

@ -1,9 +1,12 @@
# frozen_string_literal: true
require_relative "helper"
require "sidekiq/cli"
describe Sidekiq do
before do
@config = Sidekiq
end
describe "json processing" do
it "handles json" do
assert_equal({"foo" => "bar"}, Sidekiq.load_json("{\"foo\":\"bar\"}"))
@ -14,39 +17,40 @@ describe Sidekiq do
describe "redis connection" do
it "returns error without creating a connection if block is not given" do
assert_raises(ArgumentError) do
Sidekiq.redis
@config.redis
end
end
end
describe 'lifecycle events' do
it 'handles invalid input' do
Sidekiq.options[:lifecycle_events][:startup].clear
describe "lifecycle events" do
it "handles invalid input" do
config = @config
config[:lifecycle_events][:startup].clear
e = assert_raises ArgumentError do
Sidekiq.on(:startp)
config.on(:startp)
end
assert_match(/Invalid event name/, e.message)
e = assert_raises ArgumentError do
Sidekiq.on("startup")
config.on("startup")
end
assert_match(/Symbols only/, e.message)
Sidekiq.on(:startup) do
config.on(:startup) do
1 + 1
end
assert_equal 2, Sidekiq.options[:lifecycle_events][:startup].first.call
assert_equal 2, config[:lifecycle_events][:startup].first.call
end
end
describe "default_job_options" do
it "stringifies keys" do
@old_options = Sidekiq.default_job_options
@old_options = @config.default_job_options
begin
Sidekiq.default_job_options = {queue: "cat"}
assert_equal "cat", Sidekiq.default_job_options["queue"]
@config.default_job_options = {queue: "cat"}
assert_equal "cat", @config.default_job_options["queue"]
ensure
Sidekiq.default_job_options = @old_options
@config.default_job_options = @old_options
end
end
end
@ -54,13 +58,12 @@ describe Sidekiq do
describe "error handling" do
it "deals with user-specified error handlers which raise errors" do
output = capture_logging do
Sidekiq.error_handlers << proc { |x, hash|
@config.error_handlers << proc { |x, hash|
raise "boom"
}
cli = Sidekiq::CLI.new
cli.handle_exception(RuntimeError.new("hello"))
@config.handle_exception(RuntimeError.new("hello"))
ensure
Sidekiq.error_handlers.pop
@config.error_handlers.pop
end
assert_includes output, "boom"
assert_includes output, "ERROR"
@ -70,7 +73,7 @@ describe Sidekiq do
describe "redis connection" do
it "does not continually retry" do
assert_raises Sidekiq::RedisConnection.adapter::CommandError do
Sidekiq.redis do |c|
@config.redis do |c|
raise Sidekiq::RedisConnection.adapter::CommandError, "READONLY You can't write against a replica."
end
end
@ -78,7 +81,7 @@ describe Sidekiq do
it "reconnects if connection is flagged as readonly" do
counts = []
Sidekiq.redis do |c|
@config.redis do |c|
counts << c.info["total_connections_received"].to_i
raise Sidekiq::RedisConnection.adapter::CommandError, "READONLY You can't write against a replica." if counts.size == 1
end
@ -88,7 +91,7 @@ describe Sidekiq do
it "reconnects if instance state changed" do
counts = []
Sidekiq.redis do |c|
@config.redis do |c|
counts << c.info["total_connections_received"].to_i
raise Sidekiq::RedisConnection.adapter::CommandError, "UNBLOCKED force unblock from blocking operation, instance state changed (master -> replica?)" if counts.size == 1
end
@ -99,7 +102,7 @@ describe Sidekiq do
describe "redis info" do
it "calls the INFO command which returns at least redis_version" do
output = Sidekiq.redis_info
output = @config.redis_info
assert_includes output.keys, "redis_version"
end
end

View File

@ -1,9 +1,11 @@
# frozen_string_literal: true
require_relative "helper"
require "sidekiq/sd_notify"
require "sidekiq/systemd"
class TestSystemd < Minitest::Test
def setup
describe "Systemd" do
before do
::Dir::Tmpname.create("sidekiq_socket") do |sockaddr|
@sockaddr = sockaddr
@socket = Socket.new(:UNIX, :DGRAM, 0)
@ -13,8 +15,8 @@ class TestSystemd < Minitest::Test
end
end
def teardown
@socket.close if @socket
after do
@socket&.close
File.unlink(@sockaddr) if @sockaddr
@socket = nil
@sockaddr = nil
@ -24,7 +26,7 @@ class TestSystemd < Minitest::Test
@socket.recvfrom(10)[0]
end
def test_notify
it "notifies" do
count = Sidekiq::SdNotify.ready
assert_equal(socket_message, "READY=1")
assert_equal(ENV["NOTIFY_SOCKET"], @sockaddr)

View File

@ -51,7 +51,7 @@ describe "Sidekiq::Testing.fake" do
assert_in_delta 10.seconds.from_now.to_f, DirectWorker.jobs.last["at"], 0.1
end
it 'stubs the enqueue call' do
it "stubs the enqueue call" do
assert_equal 0, EnqueuedWorker.jobs.size
assert Sidekiq::Client.enqueue(EnqueuedWorker, 1, 2)
assert_equal 1, EnqueuedWorker.jobs.size
@ -212,6 +212,18 @@ describe "Sidekiq::Testing.fake" do
assert_equal 1, SecondWorker.count
end
it "clears the jobs of workers having their queue name defined as a symbol" do
assert_equal Symbol, AltQueueWorker.sidekiq_options["queue"].class
AltQueueWorker.perform_async
assert_equal 1, AltQueueWorker.jobs.size
assert_equal 1, Sidekiq::Queues[AltQueueWorker.sidekiq_options["queue"].to_s].size
AltQueueWorker.clear
assert_equal 0, AltQueueWorker.jobs.size
assert_equal 0, Sidekiq::Queues[AltQueueWorker.sidekiq_options["queue"].to_s].size
end
it "drains jobs across all workers even when workers create new jobs" do
Sidekiq::Worker.jobs.clear
FirstWorker.count = 0

View File

@ -39,7 +39,7 @@ describe "Sidekiq::Testing.inline" do
end
end
it 'stubs the enqueue call when in testing mode' do
it "stubs the enqueue call when in testing mode" do
assert Sidekiq::Client.enqueue(InlineWorker, true)
assert_raises InlineError do

View File

@ -1,26 +0,0 @@
# frozen_string_literal: true
require_relative "helper"
require "sidekiq/util"
class TestUtil < Minitest::Test
class Helpers
include Sidekiq::Util
end
def test_event_firing
before_handlers = Sidekiq.options[:lifecycle_events][:startup]
begin
Sidekiq.options[:lifecycle_events][:startup] = [proc { raise "boom" }]
h = Helpers.new
h.fire_event(:startup)
Sidekiq.options[:lifecycle_events][:startup] = [proc { raise "boom" }]
assert_raises RuntimeError do
h.fire_event(:startup, reraise: true)
end
ensure
Sidekiq.options[:lifecycle_events][:startup] = before_handlers
end
end
end

View File

@ -2,7 +2,6 @@
require_relative "helper"
require "sidekiq/web"
require "sidekiq/util"
require "rack/test"
describe Sidekiq::Web do
@ -429,7 +428,7 @@ describe Sidekiq::Web do
it "escape job args and error messages" do
# on /retries page
params = add_xss_retry
add_xss_retry
get "/retries"
assert_equal 200, last_response.status
assert_match(/FailWorker/, last_response.body)
@ -514,8 +513,6 @@ describe Sidekiq::Web do
end
describe "stats" do
include Sidekiq::Util
before do
Sidekiq.redis do |conn|
conn.set("stat:processed", 5)
@ -569,8 +566,6 @@ describe Sidekiq::Web do
end
describe "stats/queues" do
include Sidekiq::Util
before do
Sidekiq.redis do |conn|
conn.set("stat:processed", 5)
@ -710,6 +705,10 @@ describe Sidekiq::Web do
[msg, score]
end
def hostname
ENV["DYNO"] || Socket.gethostname
end
def add_worker
key = "#{hostname}:#{$$}"
msg = "{\"queue\":\"default\",\"payload\":{\"retry\":true,\"queue\":\"default\",\"timeout\":20,\"backtrace\":5,\"class\":\"HardWorker\",\"args\":[\"bob\",10,5],\"jid\":\"2b5ad2b016f5e063a1c62872\"},\"run_at\":1361208995}"

View File

@ -3,7 +3,7 @@
require_relative "helper"
require "sidekiq/web"
class TestWebHelpers < Minitest::Test
describe "Web helpers" do
class Helpers
include Sidekiq::WebHelpers
@ -33,7 +33,7 @@ class TestWebHelpers < Minitest::Test
end
end
def test_locale_determination
it "tests locale determination" do
obj = Helpers.new
assert_equal "en", obj.locale
@ -89,7 +89,7 @@ class TestWebHelpers < Minitest::Test
assert_equal "en", obj.locale
end
def test_available_locales
it "tests available locales" do
obj = Helpers.new
expected = %w[
ar cs da de el en es fa fr he hi it ja
@ -99,7 +99,7 @@ class TestWebHelpers < Minitest::Test
assert_equal expected, obj.available_locales.sort
end
def test_display_illegal_args
it "tests displaying of illegal args" do
o = Helpers.new
s = o.display_args([1, 2, 3])
assert_equal "1, 2, 3", s
@ -111,12 +111,12 @@ class TestWebHelpers < Minitest::Test
assert_equal "Invalid job payload, args is nil", s
end
def test_to_query_string_escapes_bad_query_input
it "query string escapes bad query input" do
obj = Helpers.new
assert_equal "page=B%3CH", obj.to_query_string("page" => "B<H")
end
def test_qparams_string_escapes_bad_query_input
it "qparams string escapes bad query input" do
obj = Helpers.new
obj.instance_eval do
def params