Commit 283dde3e authored by Reuben Pereira's avatar Reuben Pereira

Merge branch '329430-upgrade-sidekiq-6' into 'master'

Upgrade to Sidekiq 6

See merge request gitlab-org/gitlab!69655
parents 7b11adac 72cc45b1
...@@ -195,10 +195,10 @@ gem 'state_machines-activerecord', '~> 0.8.0' ...@@ -195,10 +195,10 @@ gem 'state_machines-activerecord', '~> 0.8.0'
gem 'acts-as-taggable-on', '~> 7.0' gem 'acts-as-taggable-on', '~> 7.0'
# Background jobs # Background jobs
gem 'sidekiq', '~> 5.2.7' gem 'sidekiq', '~> 6.2.2'
gem 'sidekiq-cron', '~> 1.0' gem 'sidekiq-cron', '~> 1.0'
gem 'redis-namespace', '~> 1.8.1' gem 'redis-namespace', '~> 1.8.1'
gem 'gitlab-sidekiq-fetcher', '0.5.6', require: 'sidekiq-reliable-fetch' gem 'gitlab-sidekiq-fetcher', '0.8.0', require: 'sidekiq-reliable-fetch'
# Cron Parser # Cron Parser
gem 'fugit', '~> 1.2.1' gem 'fugit', '~> 1.2.1'
...@@ -229,7 +229,7 @@ gem 'js_regex', '~> 3.7' ...@@ -229,7 +229,7 @@ gem 'js_regex', '~> 3.7'
gem 'device_detector' gem 'device_detector'
# Redis # Redis
gem 'redis', '~> 4.1.4' gem 'redis', '~> 4.4.0'
gem 'connection_pool', '~> 2.0' gem 'connection_pool', '~> 2.0'
# Redis session store # Redis session store
......
...@@ -490,8 +490,8 @@ GEM ...@@ -490,8 +490,8 @@ GEM
addressable (~> 2.7) addressable (~> 2.7)
omniauth (~> 1.9) omniauth (~> 1.9)
openid_connect (~> 1.2) openid_connect (~> 1.2)
gitlab-sidekiq-fetcher (0.5.6) gitlab-sidekiq-fetcher (0.8.0)
sidekiq (~> 5) sidekiq (~> 6.1)
gitlab-styles (6.3.0) gitlab-styles (6.3.0)
rubocop (~> 0.91, >= 0.91.1) rubocop (~> 0.91, >= 0.91.1)
rubocop-gitlab-security (~> 0.1.1) rubocop-gitlab-security (~> 0.1.1)
...@@ -959,8 +959,6 @@ GEM ...@@ -959,8 +959,6 @@ GEM
httpclient httpclient
json-jwt (>= 1.11.0) json-jwt (>= 1.11.0)
rack (>= 2.1.0) rack (>= 2.1.0)
rack-protection (2.0.5)
rack
rack-proxy (0.6.0) rack-proxy (0.6.0)
rack rack
rack-test (1.1.0) rack-test (1.1.0)
...@@ -1017,7 +1015,7 @@ GEM ...@@ -1017,7 +1015,7 @@ GEM
recaptcha (4.13.1) recaptcha (4.13.1)
json json
recursive-open-struct (1.1.3) recursive-open-struct (1.1.3)
redis (4.1.4) redis (4.4.0)
redis-actionpack (5.2.0) redis-actionpack (5.2.0)
actionpack (>= 5, < 7) actionpack (>= 5, < 7)
redis-rack (>= 2.1.0, < 3) redis-rack (>= 2.1.0, < 3)
...@@ -1172,11 +1170,10 @@ GEM ...@@ -1172,11 +1170,10 @@ GEM
shellany (0.0.1) shellany (0.0.1)
shoulda-matchers (4.0.1) shoulda-matchers (4.0.1)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
sidekiq (5.2.9) sidekiq (6.2.2)
connection_pool (~> 2.2, >= 2.2.2) connection_pool (>= 2.2.2)
rack (~> 2.0) rack (~> 2.0)
rack-protection (>= 1.5.0) redis (>= 4.2.0)
redis (>= 3.3.5, < 4.2)
sidekiq-cron (1.0.4) sidekiq-cron (1.0.4)
fugit (~> 1.1) fugit (~> 1.1)
sidekiq (>= 4.2.1) sidekiq (>= 4.2.1)
...@@ -1473,7 +1470,7 @@ DEPENDENCIES ...@@ -1473,7 +1470,7 @@ DEPENDENCIES
gitlab-markup (~> 1.7.1) gitlab-markup (~> 1.7.1)
gitlab-net-dns (~> 0.9.1) gitlab-net-dns (~> 0.9.1)
gitlab-omniauth-openid-connect (~> 0.8.0) gitlab-omniauth-openid-connect (~> 0.8.0)
gitlab-sidekiq-fetcher (= 0.5.6) gitlab-sidekiq-fetcher (= 0.8.0)
gitlab-styles (~> 6.3.0) gitlab-styles (~> 6.3.0)
gitlab_chronic_duration (~> 0.10.6.2) gitlab_chronic_duration (~> 0.10.6.2)
gitlab_omniauth-ldap (~> 2.1.1) gitlab_omniauth-ldap (~> 2.1.1)
...@@ -1585,7 +1582,7 @@ DEPENDENCIES ...@@ -1585,7 +1582,7 @@ DEPENDENCIES
rdoc (~> 6.3.2) rdoc (~> 6.3.2)
re2 (~> 1.2.0) re2 (~> 1.2.0)
recaptcha (~> 4.11) recaptcha (~> 4.11)
redis (~> 4.1.4) redis (~> 4.4.0)
redis-actionpack (~> 5.2.0) redis-actionpack (~> 5.2.0)
redis-namespace (~> 1.8.1) redis-namespace (~> 1.8.1)
request_store (~> 1.5) request_store (~> 1.5)
...@@ -1614,7 +1611,7 @@ DEPENDENCIES ...@@ -1614,7 +1611,7 @@ DEPENDENCIES
sentry-raven (~> 3.1) sentry-raven (~> 3.1)
settingslogic (~> 2.0.9) settingslogic (~> 2.0.9)
shoulda-matchers (~> 4.0.1) shoulda-matchers (~> 4.0.1)
sidekiq (~> 5.2.7) sidekiq (~> 6.2.2)
sidekiq-cron (~> 1.0) sidekiq-cron (~> 1.0)
simple_po_parser (~> 1.1.2) simple_po_parser (~> 1.1.2)
simplecov (~> 0.18.5) simplecov (~> 0.18.5)
......
# frozen_string_literal: true # frozen_string_literal: true
# We set the instance variable directly to suppress warnings.
# We cannot switch to the new behavior until we change all existing `redis.exists` calls to `redis.exists?`.
# Some gems also need to be updated
# https://gitlab.com/gitlab-org/gitlab/-/issues/340602
Redis.instance_variable_set(:@exists_returns_integer, false)
Redis::Client.prepend(Gitlab::Instrumentation::RedisInterceptor) Redis::Client.prepend(Gitlab::Instrumentation::RedisInterceptor)
# Make sure we initialize a Redis connection pool before multi-threaded # Make sure we initialize a Redis connection pool before multi-threaded
......
# frozen_string_literal: true # frozen_string_literal: true
class ActiveJob::QueueAdapters::SidekiqAdapter Rails.application.config.after_initialize do
# With Sidekiq 6, we can do something like: ActionMailer::MailDeliveryJob.sidekiq_options retry: 3
# class ActionMailer::MailDeliveryJob
# sidekiq_options retry: 3
# end
#
# See https://gitlab.com/gitlab-org/gitlab/-/issues/329430
raise "Update this monkey patch: #{__FILE__}" unless Sidekiq::VERSION == '5.2.9'
def enqueue(job) #:nodoc:
# Sidekiq::Client does not support symbols as keys
job.provider_job_id = Sidekiq::Client.push \
"class" => JobWrapper,
"wrapped" => job.class.to_s,
"queue" => job.queue_name,
"args" => [job.serialize],
"retry" => retry_for(job)
end
def enqueue_at(job, timestamp) #:nodoc:
job.provider_job_id = Sidekiq::Client.push \
"class" => JobWrapper,
"wrapped" => job.class.to_s,
"queue" => job.queue_name,
"args" => [job.serialize],
"at" => timestamp,
"retry" => retry_for(job)
end
private
def retry_for(job)
if job.queue_name == 'mailers'
3
else
true
end
end
end end
...@@ -27,8 +27,13 @@ use_sidekiq_daemon_memory_killer = ENV.fetch("SIDEKIQ_DAEMON_MEMORY_KILLER", 1). ...@@ -27,8 +27,13 @@ use_sidekiq_daemon_memory_killer = ENV.fetch("SIDEKIQ_DAEMON_MEMORY_KILLER", 1).
use_sidekiq_legacy_memory_killer = !use_sidekiq_daemon_memory_killer use_sidekiq_legacy_memory_killer = !use_sidekiq_daemon_memory_killer
Sidekiq.configure_server do |config| Sidekiq.configure_server do |config|
config.options[:strict] = false
config.options[:queues] = Gitlab::SidekiqConfig.expand_queues(config.options[:queues])
Sidekiq.logger.info "Listening on queues #{config.options[:queues].uniq.sort}"
if enable_json_logs if enable_json_logs
Sidekiq.logger.formatter = Gitlab::SidekiqLogging::JSONFormatter.new config.log_formatter = Gitlab::SidekiqLogging::JSONFormatter.new
config.options[:job_logger] = Gitlab::SidekiqLogging::StructuredLogger config.options[:job_logger] = Gitlab::SidekiqLogging::StructuredLogger
# Remove the default-provided handler. The exception is logged inside # Remove the default-provided handler. The exception is logged inside
......
...@@ -8,10 +8,6 @@ ...@@ -8,10 +8,6 @@
require 'sidekiq/web' require 'sidekiq/web'
# Disable the Sidekiq Rack session since GitLab already has its own session store.
# CSRF protection still works (https://github.com/mperham/sidekiq/commit/315504e766c4fd88a29b7772169060afc4c40329).
Sidekiq::Web.set :sessions, false
if Rails.env.development? if Rails.env.development?
Sidekiq.default_worker_options[:backtrace] = true Sidekiq.default_worker_options[:backtrace] = true
end end
...@@ -3,8 +3,13 @@ ...@@ -3,8 +3,13 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Mirror do RSpec.describe Gitlab::Mirror do
before do around do |example|
Sidekiq::Logging.logger = nil original_logger = Sidekiq.logger
Sidekiq.logger = nil
example.run
Sidekiq.logger = original_logger
end end
describe '#configure_cron_job!' do describe '#configure_cron_job!' do
......
...@@ -45,7 +45,7 @@ RSpec.describe VulnerabilityExports::ExportWorker, type: :worker do ...@@ -45,7 +45,7 @@ RSpec.describe VulnerabilityExports::ExportWorker, type: :worker do
let(:default_job_payload) { { 'class' => described_class.name, 'args' => [vulnerability_export.id] } } let(:default_job_payload) { { 'class' => described_class.name, 'args' => [vulnerability_export.id] } }
subject(:run_job) do subject(:run_job) do
sidekiq_retry_handler.local(worker, job_payload, 'default') do sidekiq_retry_handler.local(worker, job_payload.to_json, 'default') do
raise 'Foo' raise 'Foo'
end end
end end
......
...@@ -5,7 +5,7 @@ require 'active_record/log_subscriber' ...@@ -5,7 +5,7 @@ require 'active_record/log_subscriber'
module Gitlab module Gitlab
module SidekiqLogging module SidekiqLogging
class StructuredLogger class StructuredLogger < Sidekiq::JobLogger
include LogsJobs include LogsJobs
def call(job, queue) def call(job, queue)
......
...@@ -13,7 +13,7 @@ module Gitlab ...@@ -13,7 +13,7 @@ module Gitlab
def call(worker_class, job, queue, _redis_pool) def call(worker_class, job, queue, _redis_pool)
# worker_class can either be the string or class of the worker being enqueued. # worker_class can either be the string or class of the worker being enqueued.
worker_class = worker_class.safe_constantize if worker_class.respond_to?(:safe_constantize) worker_class = worker_class.to_s.safe_constantize
labels = create_labels(worker_class, queue, job) labels = create_labels(worker_class, queue, job)
labels[:scheduling] = job.key?('at') ? 'delayed' : 'immediate' labels[:scheduling] = job.key?('at') ? 'delayed' : 'immediate'
......
...@@ -3,25 +3,21 @@ ...@@ -3,25 +3,21 @@
module Gitlab module Gitlab
module SidekiqVersioning module SidekiqVersioning
def self.install! def self.install!
Sidekiq::Manager.prepend SidekiqVersioning::Manager
# The Sidekiq client API always adds the queue to the Sidekiq queue # The Sidekiq client API always adds the queue to the Sidekiq queue
# list, but mail_room and gitlab-shell do not. This is only necessary # list, but mail_room and gitlab-shell do not. This is only necessary
# for monitoring. # for monitoring.
begin queues = SidekiqConfig.worker_queues
queues = SidekiqConfig.worker_queues
if queues.any? if queues.any?
Sidekiq.redis do |conn| Sidekiq.redis do |conn|
conn.pipelined do conn.pipelined do
queues.each do |queue| queues.each do |queue|
conn.sadd('queues', queue) conn.sadd('queues', queue)
end
end end
end end
end end
rescue ::Redis::BaseError, SocketError, Errno::ENOENT, Errno::EADDRNOTAVAIL, Errno::EAFNOSUPPORT, Errno::ECONNRESET, Errno::ECONNREFUSED
end end
rescue ::Redis::BaseError, SocketError, Errno::ENOENT, Errno::EADDRNOTAVAIL, Errno::EAFNOSUPPORT, Errno::ECONNRESET, Errno::ECONNREFUSED
end end
end end
end end
# frozen_string_literal: true
module Gitlab
module SidekiqVersioning
module Manager
def initialize(options = {})
options[:strict] = false
options[:queues] = SidekiqConfig.expand_queues(options[:queues])
Sidekiq.logger.info "Listening on queues #{options[:queues].uniq.sort}"
super
end
end
end
end
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware, :clean_gitlab_redis_queues do
let(:middleware) { described_class.new } let(:middleware) { described_class.new }
let(:load_balancer) { Gitlab::Database::LoadBalancing.proxy.load_balancer } let(:load_balancer) { Gitlab::Database::LoadBalancing.proxy.load_balancer }
...@@ -157,18 +157,15 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do ...@@ -157,18 +157,15 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do
process_job(job) process_job(job)
end.to raise_error(Sidekiq::JobRetry::Skip) end.to raise_error(Sidekiq::JobRetry::Skip)
expect(job['error_class']).to eq('Gitlab::Database::LoadBalancing::SidekiqServerMiddleware::JobReplicaNotUpToDate') job_for_retry = Sidekiq::RetrySet.new.first
expect(job_for_retry['error_class']).to eq('Gitlab::Database::LoadBalancing::SidekiqServerMiddleware::JobReplicaNotUpToDate')
end end
include_examples 'load balancing strategy', 'retry' include_examples 'load balancing strategy', 'retry'
end end
context 'when job is retried' do context 'when job is retried' do
before do let(:job) { { "retry" => 3, "job_id" => "a180b47c-3fd6-41b8-81e9-34da61c3400e", 'database_replica_location' => '0/D525E3A8', 'retry_count' => 0 } }
expect do
process_job(job)
end.to raise_error(Sidekiq::JobRetry::Skip)
end
context 'and replica still lagging behind' do context 'and replica still lagging behind' do
include_examples 'stick to the primary', 'primary' include_examples 'stick to the primary', 'primary'
...@@ -199,7 +196,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do ...@@ -199,7 +196,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do
end end
def process_job(job) def process_job(job)
Sidekiq::JobRetry.new.local(worker_class, job, 'default') do Sidekiq::JobRetry.new.local(worker_class, job.to_json, 'default') do
worker_class.process_job(job) worker_class.process_job(job)
end end
end end
......
...@@ -45,7 +45,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::MemoryKiller do ...@@ -45,7 +45,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::MemoryKiller do
expect(subject).to receive(:sleep).with(30).ordered expect(subject).to receive(:sleep).with(30).ordered
expect(Process).to receive(:kill).with('SIGTERM', pid).ordered expect(Process).to receive(:kill).with('SIGTERM', pid).ordered
expect(subject).to receive(:sleep).with(10).ordered expect(subject).to receive(:sleep).with(Sidekiq.options[:timeout] + 2).ordered
expect(Process).to receive(:kill).with('SIGKILL', pid).ordered expect(Process).to receive(:kill).with('SIGKILL', pid).ordered
expect(Sidekiq.logger) expect(Sidekiq.logger)
......
...@@ -65,7 +65,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do ...@@ -65,7 +65,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
expect(item).to include('queue' => 'post_receive', 'args' => [i]) expect(item).to include('queue' => 'post_receive', 'args' => [i])
end end
expect(score).to eq(i.succ.hours.from_now.to_i) expect(score).to be_within(schedule_jitter).of(i.succ.hours.from_now.to_i)
end end
end end
end end
...@@ -84,7 +84,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do ...@@ -84,7 +84,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
expect(item).to include('queue' => 'another_queue', 'args' => [i]) expect(item).to include('queue' => 'another_queue', 'args' => [i])
end end
expect(score).to eq(i.succ.hours.from_now.to_i) expect(score).to be_within(schedule_jitter).of(i.succ.hours.from_now.to_i)
end end
end end
end end
...@@ -98,7 +98,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do ...@@ -98,7 +98,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
set_after.each.with_index do |(item, score), i| set_after.each.with_index do |(item, score), i|
expect(item).to include('queue' => 'new_queue', 'args' => [i]) expect(item).to include('queue' => 'new_queue', 'args' => [i])
expect(score).to eq(i.succ.hours.from_now.to_i) expect(score).to be_within(schedule_jitter).of(i.succ.hours.from_now.to_i)
end end
end end
end end
...@@ -173,6 +173,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do ...@@ -173,6 +173,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
context 'scheduled jobs' do context 'scheduled jobs' do
let(:set_name) { 'schedule' } let(:set_name) { 'schedule' }
let(:schedule_jitter) { 0 }
def create_jobs(include_post_receive: true) def create_jobs(include_post_receive: true)
AuthorizedProjectsWorker.perform_in(1.hour, 0) AuthorizedProjectsWorker.perform_in(1.hour, 0)
...@@ -186,12 +187,14 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do ...@@ -186,12 +187,14 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
context 'retried jobs' do context 'retried jobs' do
let(:set_name) { 'retry' } let(:set_name) { 'retry' }
# Account for Sidekiq retry jitter
# https://github.com/mperham/sidekiq/blob/3575ccb44c688dd08bfbfd937696260b12c622fb/lib/sidekiq/job_retry.rb#L217
let(:schedule_jitter) { 10 }
# Try to mimic as closely as possible what Sidekiq will actually # Try to mimic as closely as possible what Sidekiq will actually
# do to retry a job. # do to retry a job.
def retry_in(klass, time, args) def retry_in(klass, time, args)
# In Sidekiq 6, this argument will become a JSON string message = { 'class' => klass.name, 'args' => [args], 'retry' => true }.to_json
message = { 'class' => klass, 'args' => [args], 'retry' => true }
allow(klass).to receive(:sidekiq_retry_in_block).and_return(proc { time }) allow(klass).to receive(:sidekiq_retry_in_block).and_return(proc { time })
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::SidekiqVersioning::Manager do
before do
Sidekiq::Manager.prepend described_class
end
describe '#initialize' do
it 'listens on all expanded queues' do
manager = Sidekiq::Manager.new(queues: %w[post_receive repository_fork cronjob unknown])
queues = manager.options[:queues]
expect(queues).to include('post_receive')
expect(queues).to include('repository_fork')
expect(queues).to include('cronjob')
expect(queues).to include('cronjob:import_stuck_project_import_jobs')
expect(queues).to include('cronjob:jira_import_stuck_jira_import_jobs')
expect(queues).to include('cronjob:stuck_merge_jobs')
expect(queues).to include('unknown')
end
end
end
...@@ -29,12 +29,6 @@ RSpec.describe Gitlab::SidekiqVersioning, :redis do ...@@ -29,12 +29,6 @@ RSpec.describe Gitlab::SidekiqVersioning, :redis do
end end
describe '.install!' do describe '.install!' do
it 'prepends SidekiqVersioning::Manager into Sidekiq::Manager' do
described_class.install!
expect(Sidekiq::Manager).to include(Gitlab::SidekiqVersioning::Manager)
end
it 'registers all versionless and versioned queues with Redis' do it 'registers all versionless and versioned queues with Redis' do
described_class.install! described_class.install!
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment