Commit 1c7344ba authored by Dmitry Gruzd's avatar Dmitry Gruzd

Merge branch...

Merge branch 'qmnguyen0711/1006-db-multi-request-stickiness-doesn-t-stick-as-expected' into 'master'

Fix the bug that DB multi-request stickiness doesn't stick as expected

See merge request gitlab-org/gitlab!60246
parents 3e333282 6897c76e
---
title: Fix the bug that DB multi-request stickiness doesn't stick as expected
merge_request: 60246
author:
type: fixed
...@@ -22,7 +22,8 @@ module Gitlab ...@@ -22,7 +22,8 @@ module Gitlab
Sticking.unstick_or_continue_sticking(namespace, id) Sticking.unstick_or_continue_sticking(namespace, id)
env[STICK_OBJECT] = [namespace, id] env[STICK_OBJECT] ||= Set.new
env[STICK_OBJECT] << [namespace, id]
end end
def initialize(app) def initialize(app)
...@@ -50,18 +51,18 @@ module Gitlab ...@@ -50,18 +51,18 @@ module Gitlab
# Typically this code will only be reachable for Rails requests as # Typically this code will only be reachable for Rails requests as
# Grape data is not yet available at this point. # Grape data is not yet available at this point.
def unstick_or_continue_sticking(env) def unstick_or_continue_sticking(env)
namespace, id = sticking_namespace_and_id(env) namespaces_and_ids = sticking_namespaces_and_ids(env)
if namespace && id namespaces_and_ids.each do |namespace, id|
Sticking.unstick_or_continue_sticking(namespace, id) Sticking.unstick_or_continue_sticking(namespace, id)
end end
end end
# Determine if we need to stick after handling a request. # Determine if we need to stick after handling a request.
def stick_if_necessary(env) def stick_if_necessary(env)
namespace, id = sticking_namespace_and_id(env) namespaces_and_ids = sticking_namespaces_and_ids(env)
if namespace && id namespaces_and_ids.each do |namespace, id|
Sticking.stick_if_necessary(namespace, id) Sticking.stick_if_necessary(namespace, id)
end end
end end
...@@ -80,13 +81,13 @@ module Gitlab ...@@ -80,13 +81,13 @@ module Gitlab
# #
# For Rails requests this uses warden, but Grape and others have to # For Rails requests this uses warden, but Grape and others have to
# manually set the right environment variable. # manually set the right environment variable.
def sticking_namespace_and_id(env) def sticking_namespaces_and_ids(env)
warden = env['warden'] warden = env['warden']
if warden && warden.user if warden && warden.user
[:user, warden.user.id] [[:user, warden.user.id]]
elsif env[STICK_OBJECT] elsif env[STICK_OBJECT].present?
env[STICK_OBJECT] env[STICK_OBJECT].to_a
else else
[] []
end end
......
...@@ -5,16 +5,27 @@ require 'spec_helper' ...@@ -5,16 +5,27 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do
let(:app) { double(:app) } let(:app) { double(:app) }
let(:middleware) { described_class.new(app) } let(:middleware) { described_class.new(app) }
let(:warden_user) { double(:warden, user: double(:user, id: 42)) }
let(:single_sticking_object) { Set.new([[:user, 42]]) }
let(:multiple_sticking_objects) do
Set.new([
[:user, 42],
[:runner, '123456789'],
[:runner, '1234']
])
end
after do after do
Gitlab::Database::LoadBalancing::Session.clear_session Gitlab::Database::LoadBalancing::Session.clear_session
end end
describe '.stick_or_unstick' do describe '.stick_or_unstick' do
it 'sticks or unsticks and updates the Rack environment' do before do
allow(Gitlab::Database::LoadBalancing).to receive(:enable?) allow(Gitlab::Database::LoadBalancing).to receive(:enable?)
.and_return(true) .and_return(true)
end
it 'sticks or unsticks a single object and updates the Rack environment' do
expect(Gitlab::Database::LoadBalancing::Sticking) expect(Gitlab::Database::LoadBalancing::Sticking)
.to receive(:unstick_or_continue_sticking) .to receive(:unstick_or_continue_sticking)
.with(:user, 42) .with(:user, 42)
...@@ -23,7 +34,29 @@ RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do ...@@ -23,7 +34,29 @@ RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do
described_class.stick_or_unstick(env, :user, 42) described_class.stick_or_unstick(env, :user, 42)
expect(env[described_class::STICK_OBJECT]).to eq([:user, 42]) expect(env[described_class::STICK_OBJECT].to_a).to eq([[:user, 42]])
end
it 'sticks or unsticks multiple objects and updates the Rack environment' do
expect(Gitlab::Database::LoadBalancing::Sticking)
.to receive(:unstick_or_continue_sticking)
.with(:user, 42)
.ordered
expect(Gitlab::Database::LoadBalancing::Sticking)
.to receive(:unstick_or_continue_sticking)
.with(:runner, '123456789')
.ordered
env = {}
described_class.stick_or_unstick(env, :user, 42)
described_class.stick_or_unstick(env, :runner, '123456789')
expect(env[described_class::STICK_OBJECT].to_a).to eq([
[:user, 42],
[:runner, '123456789']
])
end end
end end
...@@ -50,12 +83,43 @@ RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do ...@@ -50,12 +83,43 @@ RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do
middleware.unstick_or_continue_sticking({}) middleware.unstick_or_continue_sticking({})
end end
it 'sticks to the primary if a sticking namespace and identifier were found' do it 'sticks to the primary if a warden user is found' do
env = { described_class::STICK_OBJECT => [:user, 42] } env = { 'warden' => warden_user }
expect(Gitlab::Database::LoadBalancing::Sticking)
.to receive(:unstick_or_continue_sticking)
.with(:user, 42)
middleware.unstick_or_continue_sticking(env)
end
it 'sticks to the primary if a sticking namespace and identifier is found' do
env = { described_class::STICK_OBJECT => single_sticking_object }
expect(Gitlab::Database::LoadBalancing::Sticking)
.to receive(:unstick_or_continue_sticking)
.with(:user, 42)
middleware.unstick_or_continue_sticking(env)
end
it 'sticks to the primary if multiple sticking namespaces and identifiers were found' do
env = { described_class::STICK_OBJECT => multiple_sticking_objects }
expect(Gitlab::Database::LoadBalancing::Sticking) expect(Gitlab::Database::LoadBalancing::Sticking)
.to receive(:unstick_or_continue_sticking) .to receive(:unstick_or_continue_sticking)
.with(:user, 42) .with(:user, 42)
.ordered
expect(Gitlab::Database::LoadBalancing::Sticking)
.to receive(:unstick_or_continue_sticking)
.with(:runner, '123456789')
.ordered
expect(Gitlab::Database::LoadBalancing::Sticking)
.to receive(:unstick_or_continue_sticking)
.with(:runner, '1234')
.ordered
middleware.unstick_or_continue_sticking(env) middleware.unstick_or_continue_sticking(env)
end end
...@@ -69,8 +133,18 @@ RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do ...@@ -69,8 +133,18 @@ RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do
middleware.stick_if_necessary({}) middleware.stick_if_necessary({})
end end
it 'sticks to the primary if necessary' do it 'sticks to the primary if a warden user is found' do
env = { described_class::STICK_OBJECT => [:user, 42] } env = { 'warden' => warden_user }
expect(Gitlab::Database::LoadBalancing::Sticking)
.to receive(:stick_if_necessary)
.with(:user, 42)
middleware.stick_if_necessary(env)
end
it 'sticks to the primary if a a single sticking object is found' do
env = { described_class::STICK_OBJECT => single_sticking_object }
expect(Gitlab::Database::LoadBalancing::Sticking) expect(Gitlab::Database::LoadBalancing::Sticking)
.to receive(:stick_if_necessary) .to receive(:stick_if_necessary)
...@@ -78,6 +152,27 @@ RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do ...@@ -78,6 +152,27 @@ RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do
middleware.stick_if_necessary(env) middleware.stick_if_necessary(env)
end end
it 'sticks to the primary if multiple sticking namespaces and identifiers were found' do
env = { described_class::STICK_OBJECT => multiple_sticking_objects }
expect(Gitlab::Database::LoadBalancing::Sticking)
.to receive(:stick_if_necessary)
.with(:user, 42)
.ordered
expect(Gitlab::Database::LoadBalancing::Sticking)
.to receive(:stick_if_necessary)
.with(:runner, '123456789')
.ordered
expect(Gitlab::Database::LoadBalancing::Sticking)
.to receive(:stick_if_necessary)
.with(:runner, '1234')
.ordered
middleware.stick_if_necessary(env)
end
end end
describe '#clear' do describe '#clear' do
...@@ -111,35 +206,37 @@ RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do ...@@ -111,35 +206,37 @@ RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do
end end
end end
describe '#sticking_namespace_and_id' do describe '#sticking_namespaces_and_ids' do
context 'using a Warden request' do context 'using a Warden request' do
it 'returns the warden user if present' do it 'returns the warden user if present' do
user = double(:user, id: 42) env = { 'warden' => warden_user }
warden = double(:warden, user: user)
env = { 'warden' => warden }
expect(middleware.sticking_namespace_and_id(env)).to eq([:user, 42]) expect(middleware.sticking_namespaces_and_ids(env)).to eq([[:user, 42]])
end end
it 'returns an empty Array if no user was present' do it 'returns an empty Array if no user was present' do
warden = double(:warden, user: nil) warden = double(:warden, user: nil)
env = { 'warden' => warden } env = { 'warden' => warden }
expect(middleware.sticking_namespace_and_id(env)).to eq([]) expect(middleware.sticking_namespaces_and_ids(env)).to eq([])
end end
end end
context 'using a request with a manually set sticking object' do context 'using a request with a manually set sticking object' do
it 'returns the sticking object' do it 'returns the sticking object' do
env = { described_class::STICK_OBJECT => [:user, 42] } env = { described_class::STICK_OBJECT => multiple_sticking_objects }
expect(middleware.sticking_namespace_and_id(env)).to eq([:user, 42]) expect(middleware.sticking_namespaces_and_ids(env)).to eq([
[:user, 42],
[:runner, '123456789'],
[:runner, '1234']
])
end end
end end
context 'using a regular request' do context 'using a regular request' do
it 'returns an empty Array' do it 'returns an empty Array' do
expect(middleware.sticking_namespace_and_id({})).to eq([]) expect(middleware.sticking_namespaces_and_ids({})).to eq([])
end end
end end
end end
......
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