Commit 9131bf42 authored by Sean McGivern's avatar Sean McGivern

Allow setting Sidekiq status only when opted in

This adds the `opt_in_sidekiq_status` feature flag. When this is
disabled, everything works as normal: we always set an entry in Redis
for a Sidekiq job's status.

When the flag is enabled, we will only perform the Redis SET command if
the job itself has opted in to tracking its status. It can do this on
the job class level:

    class SomeWorker
      sidekiq_options status_expiration: 1.hour
    end

Or on an individual call:

    SomeOtherWorker.with_status.perform_async

The aim here is to heavily reduce the number of SET commands we send to
the Sidekiq Redis, as most are unnecessary - most workers don't need to
have their status tracked.
parent c5d75b4c
---
name: opt_in_sidekiq_status
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77349
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343964
milestone: '14.7'
type: development
group: group::scalability
default_enabled: false
...@@ -34,6 +34,8 @@ module Gitlab ...@@ -34,6 +34,8 @@ module Gitlab
# jid - The Sidekiq job ID # jid - The Sidekiq job ID
# expire - The expiration time of the Redis key. # expire - The expiration time of the Redis key.
def self.set(jid, expire = DEFAULT_EXPIRATION) def self.set(jid, expire = DEFAULT_EXPIRATION)
return unless expire
Sidekiq.redis do |redis| Sidekiq.redis do |redis|
redis.set(key_for(jid), 1, ex: expire) redis.set(key_for(jid), 1, ex: expire)
end end
......
...@@ -4,10 +4,14 @@ module Gitlab ...@@ -4,10 +4,14 @@ module Gitlab
module SidekiqStatus module SidekiqStatus
class ClientMiddleware class ClientMiddleware
def call(_, job, _, _) def call(_, job, _, _)
status_expiration = job['status_expiration'] || Gitlab::SidekiqStatus::DEFAULT_EXPIRATION status_expiration = job['status_expiration']
value = job['status_expiration'] ? 2 : Gitlab::SidekiqStatus::DEFAULT_VALUE
unless ::Feature.enabled?(:opt_in_sidekiq_status, default_enabled: :yaml)
status_expiration ||= Gitlab::SidekiqStatus::DEFAULT_EXPIRATION
end
Gitlab::SidekiqStatus.set(job['jid'], status_expiration)
Gitlab::SidekiqStatus.set(job['jid'], status_expiration, value: value)
yield yield
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
require 'fast_spec_helper' # This can use fast_spec_helper when the feature flag stubbing is removed.
require 'spec_helper'
RSpec.describe Gitlab::SidekiqStatus::ClientMiddleware do RSpec.describe Gitlab::SidekiqStatus::ClientMiddleware, :clean_gitlab_redis_queues do
describe '#call' do describe '#call' do
context 'when the job has status_expiration set' do context 'when opt_in_sidekiq_status is disabled' do
it 'tracks the job in Redis' do before do
expect(Gitlab::SidekiqStatus).to receive(:set).with('123', 1.hour.to_i) stub_feature_flags(opt_in_sidekiq_status: false)
end
context 'when the job has status_expiration set' do
it 'tracks the job in Redis' do
expect(Gitlab::SidekiqStatus).to receive(:set).with('123', 1.hour.to_i).and_call_original
described_class.new
.call('Foo', { 'jid' => '123', 'status_expiration' => 1.hour.to_i }, double(:queue), double(:pool)) { nil }
expect(Gitlab::SidekiqStatus.num_running(['123'])).to eq(1)
end
end
context 'when the job does not have status_expiration set' do
it 'tracks the job in Redis' do
expect(Gitlab::SidekiqStatus).to receive(:set).with('123', 30.minutes.to_i).and_call_original
described_class.new
.call('Foo', { 'jid' => '123' }, double(:queue), double(:pool)) { nil }
described_class.new expect(Gitlab::SidekiqStatus.num_running(['123'])).to eq(1)
.call('Foo', { 'jid' => '123', 'status_expiration' => 1.hour.to_i }, double(:queue), double(:pool)) { nil } end
end end
end end
context 'when the job does not have status_expiration set' do context 'when opt_in_sidekiq_status is enabled' do
it 'tracks the job in Redis' do before do
expect(Gitlab::SidekiqStatus).to receive(:set).with('123', Gitlab::SidekiqStatus::DEFAULT_EXPIRATION) stub_feature_flags(opt_in_sidekiq_status: true)
end
context 'when the job has status_expiration set' do
it 'tracks the job in Redis' do
expect(Gitlab::SidekiqStatus).to receive(:set).with('123', 1.hour.to_i).and_call_original
described_class.new
.call('Foo', { 'jid' => '123', 'status_expiration' => 1.hour.to_i }, double(:queue), double(:pool)) { nil }
expect(Gitlab::SidekiqStatus.num_running(['123'])).to eq(1)
end
end
context 'when the job does not have status_expiration set' do
it 'does not track the job in Redis' do
described_class.new
.call('Foo', { 'jid' => '123' }, double(:queue), double(:pool)) { nil }
described_class.new expect(Gitlab::SidekiqStatus.num_running(['123'])).to be_zero
.call('Foo', { 'jid' => '123' }, double(:queue), double(:pool)) { nil } end
end end
end end
end end
......
...@@ -27,6 +27,16 @@ RSpec.describe Gitlab::SidekiqStatus, :clean_gitlab_redis_queues, :clean_gitlab_ ...@@ -27,6 +27,16 @@ RSpec.describe Gitlab::SidekiqStatus, :clean_gitlab_redis_queues, :clean_gitlab_
expect(redis.get(key)).to eq('1') expect(redis.get(key)).to eq('1')
end end
end end
it 'does not store anything with a nil expiry' do
described_class.set('123', nil)
key = described_class.key_for('123')
Sidekiq.redis do |redis|
expect(redis.exists(key)).to eq(false)
end
end
end end
describe '.unset' do describe '.unset' do
......
...@@ -3278,9 +3278,10 @@ RSpec.describe API::MergeRequests do ...@@ -3278,9 +3278,10 @@ RSpec.describe API::MergeRequests do
context 'when skip_ci parameter is set' do context 'when skip_ci parameter is set' do
it 'enqueues a rebase of the merge request with skip_ci flag set' do it 'enqueues a rebase of the merge request with skip_ci flag set' do
allow(RebaseWorker).to receive(:with_status).and_return(RebaseWorker) with_status = RebaseWorker.with_status
expect(RebaseWorker).to receive(:perform_async).with(merge_request.id, user.id, true).and_call_original expect(RebaseWorker).to receive(:with_status).and_return(with_status)
expect(with_status).to receive(:perform_async).with(merge_request.id, user.id, true).and_call_original
Sidekiq::Testing.fake! do Sidekiq::Testing.fake! do
expect do expect do
......
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