Commit 99a60957 authored by Vitali Tatarintev's avatar Vitali Tatarintev

Merge branch 'extract_some_overrident_lb_calls' into 'master'

Database Load Balancing feature available in GitLab Free [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!62739
parents 8ffce6a5 f6232fc4
...@@ -214,6 +214,8 @@ module Ci ...@@ -214,6 +214,8 @@ module Ci
before_save :ensure_token before_save :ensure_token
before_destroy { unscoped_project } before_destroy { unscoped_project }
after_save :stick_build_if_status_changed
after_create unless: :importing? do |build| after_create unless: :importing? do |build|
run_after_commit { BuildHooksWorker.perform_async(build.id) } run_after_commit { BuildHooksWorker.perform_async(build.id) }
end end
...@@ -1082,6 +1084,13 @@ module Ci ...@@ -1082,6 +1084,13 @@ module Ci
private private
def stick_build_if_status_changed
return unless saved_change_to_status?
return unless running?
::Gitlab::Database::LoadBalancing::Sticking.stick(:build, id)
end
def status_commit_hooks def status_commit_hooks
@status_commit_hooks ||= [] @status_commit_hooks ||= []
end end
......
...@@ -338,6 +338,14 @@ module Ci ...@@ -338,6 +338,14 @@ module Ci
end end
def tick_runner_queue def tick_runner_queue
##
# We only stick a runner to primary database to be able to detect the
# replication lag in `EE::Ci::RegisterJobService#execute`. The
# intention here is not to execute `Ci::RegisterJobService#execute` on
# the primary database.
#
::Gitlab::Database::LoadBalancing::Sticking.stick(:runner, id)
SecureRandom.hex.tap do |new_update| SecureRandom.hex.tap do |new_update|
::Gitlab::Workhorse.set_key_and_notify(runner_queue_key, new_update, ::Gitlab::Workhorse.set_key_and_notify(runner_queue_key, new_update,
expire: RUNNER_QUEUE_EXPIRY_TIME, overwrite: true) expire: RUNNER_QUEUE_EXPIRY_TIME, overwrite: true)
...@@ -355,13 +363,20 @@ module Ci ...@@ -355,13 +363,20 @@ module Ci
end end
def heartbeat(values) def heartbeat(values)
values = values&.slice(:version, :revision, :platform, :architecture, :ip_address, :config) || {} ##
values[:contacted_at] = Time.current # We can safely ignore writes performed by a runner heartbeat. We do
# not want to upgrade database connection proxy to use the primary
# database after heartbeat write happens.
#
::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do
values = values&.slice(:version, :revision, :platform, :architecture, :ip_address, :config) || {}
values[:contacted_at] = Time.current
cache_attributes(values) cache_attributes(values)
# We save data without validation, it will always change due to `contacted_at` # We save data without validation, it will always change due to `contacted_at`
self.update_columns(values) if persist_cached_data? self.update_columns(values) if persist_cached_data?
end
end end
def pick_build!(build) def pick_build!(build)
......
...@@ -128,6 +128,12 @@ module Ci ...@@ -128,6 +128,12 @@ module Ci
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def retrieve_queue(queue_query_proc) def retrieve_queue(queue_query_proc)
##
# We want to reset a load balancing session to discard the side
# effects of writes that could have happened prior to this moment.
#
::Gitlab::Database::LoadBalancing::Session.clear_session
@metrics.observe_queue_time(:retrieve, @runner.runner_type) do @metrics.observe_queue_time(:retrieve, @runner.runner_type) do
queue_query_proc.call queue_query_proc.call
end end
......
...@@ -4,9 +4,10 @@ group: Database ...@@ -4,9 +4,10 @@ group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
--- ---
# Database Load Balancing **(PREMIUM SELF)** # Database Load Balancing **(FREE SELF)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1283) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.0. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1283) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.0.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60894) from GitLab Premium to GitLab Free in 14.0. See the [instructions below](#load-balancing-in-gitlab).
Distribute read-only queries among multiple database servers. Distribute read-only queries among multiple database servers.
...@@ -103,6 +104,17 @@ the following. This will balance the load between `host1.example.com` and ...@@ -103,6 +104,17 @@ the following. This will balance the load between `host1.example.com` and
1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect. 1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
### Load balancing in GitLab Free
Database load balancing was moved from GitLab Premium to GitLab Free in
14.0 with some [known limitations](https://gitlab.com/gitlab-org/gitlab/-/issues/327902).
For example, requesting new jobs for runners may fail. Hence, this feature is **not ready for production use** in GitLab Free.
To use database load balancing in GitLab Free, set the `ENABLE_LOAD_BALANCING_FOR_FOSS`
[environment
variable](https://docs.gitlab.com/omnibus/settings/environment-variables.html)
to `true`.
### Enable the load balancer for Sidekiq ### Enable the load balancer for Sidekiq
Sidekiq mostly writes to the database, which means that most of its traffic hits the Sidekiq mostly writes to the database, which means that most of its traffic hits the
......
...@@ -31,7 +31,6 @@ module EE ...@@ -31,7 +31,6 @@ module EE
has_many :security_scans, class_name: 'Security::Scan' has_many :security_scans, class_name: 'Security::Scan'
after_save :stick_build_if_status_changed
after_commit :track_ci_secrets_management_usage, on: :create after_commit :track_ci_secrets_management_usage, on: :create
delegate :service_specification, to: :runner_session, allow_nil: true delegate :service_specification, to: :runner_session, allow_nil: true
...@@ -62,13 +61,6 @@ module EE ...@@ -62,13 +61,6 @@ module EE
project.shared_runners_minutes_limit_enabled? && runner&.minutes_cost_factor(project.visibility_level)&.positive? project.shared_runners_minutes_limit_enabled? && runner&.minutes_cost_factor(project.visibility_level)&.positive?
end end
def stick_build_if_status_changed
return unless saved_change_to_status?
return unless running?
::Gitlab::Database::LoadBalancing::Sticking.stick(:build, id)
end
def log_geo_deleted_event def log_geo_deleted_event
# It is not needed to generate a Geo deleted event # It is not needed to generate a Geo deleted event
# since Legacy Artifacts are migrated to multi-build artifacts # since Legacy Artifacts are migrated to multi-build artifacts
......
...@@ -5,27 +5,6 @@ module EE ...@@ -5,27 +5,6 @@ module EE
module Runner module Runner
extend ActiveSupport::Concern extend ActiveSupport::Concern
def tick_runner_queue
##
# We only stick a runner to primary database to be able to detect the
# replication lag in `EE::Ci::RegisterJobService#execute`. The
# intention here is not to execute `Ci::RegisterJobService#execute` on
# the primary database.
#
::Gitlab::Database::LoadBalancing::Sticking.stick(:runner, id)
super
end
def heartbeat(values)
##
# We can safely ignore writes performed by a runner heartbeat. We do
# not want to upgrade database connection proxy to use the primary
# database after heartbeat write happens.
#
::Gitlab::Database::LoadBalancing::Session.without_sticky_writes { super }
end
def minutes_cost_factor(visibility_level) def minutes_cost_factor(visibility_level)
::Gitlab::Ci::Minutes::CostFactor.new(runner_matcher).for_visibility(visibility_level) ::Gitlab::Ci::Minutes::CostFactor.new(runner_matcher).for_visibility(visibility_level)
end end
......
...@@ -28,16 +28,6 @@ module EE ...@@ -28,16 +28,6 @@ module EE
end end
end end
def retrieve_queue(queue_query_proc)
##
# We want to reset a load balancing session to discard the side
# effects of writes that could have happened prior to this moment.
#
::Gitlab::Database::LoadBalancing::Session.clear_session
super
end
def builds_for_shared_runner def builds_for_shared_runner
# if disaster recovery is enabled, we disable quota # if disaster recovery is enabled, we disable quota
if ::Feature.enabled?(:ci_queueing_disaster_recovery, runner, type: :ops, default_enabled: :yaml) if ::Feature.enabled?(:ci_queueing_disaster_recovery, runner, type: :ops, default_enabled: :yaml)
......
...@@ -107,20 +107,6 @@ RSpec.describe Ci::Build do ...@@ -107,20 +107,6 @@ RSpec.describe Ci::Build do
end end
end end
describe '#stick_build_if_status_changed' do
it 'sticks the build if the status changed' do
job = create(:ci_build, :pending)
allow(Gitlab::Database::LoadBalancing).to receive(:enable?)
.and_return(true)
expect(Gitlab::Database::LoadBalancing::Sticking).to receive(:stick)
.with(:build, job.id)
job.update!(status: :running)
end
end
describe '#variables' do describe '#variables' do
subject { job.variables } subject { job.variables }
......
...@@ -3,22 +3,6 @@ ...@@ -3,22 +3,6 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe EE::Ci::Runner do RSpec.describe EE::Ci::Runner do
describe '#tick_runner_queue' do
it 'sticks the runner to the primary and calls the original method' do
runner = create(:ci_runner)
allow(Gitlab::Database::LoadBalancing).to receive(:enable?)
.and_return(true)
expect(Gitlab::Database::LoadBalancing::Sticking).to receive(:stick)
.with(:runner, runner.id)
expect(Gitlab::Workhorse).to receive(:set_key_and_notify)
runner.tick_runner_queue
end
end
describe '#minutes_cost_factor' do describe '#minutes_cost_factor' do
subject { runner.minutes_cost_factor(visibility_level) } subject { runner.minutes_cost_factor(visibility_level) }
......
...@@ -320,6 +320,20 @@ RSpec.describe Ci::Build do ...@@ -320,6 +320,20 @@ RSpec.describe Ci::Build do
end end
end end
describe '#stick_build_if_status_changed' do
it 'sticks the build if the status changed' do
job = create(:ci_build, :pending)
allow(Gitlab::Database::LoadBalancing).to receive(:enable?)
.and_return(true)
expect(Gitlab::Database::LoadBalancing::Sticking).to receive(:stick)
.with(:build, job.id)
job.update!(status: :running)
end
end
describe '#enqueue' do describe '#enqueue' do
let(:build) { create(:ci_build, :created) } let(:build) { create(:ci_build, :created) }
......
...@@ -365,6 +365,22 @@ RSpec.describe Ci::Runner do ...@@ -365,6 +365,22 @@ RSpec.describe Ci::Runner do
it { is_expected.to eq([@runner1])} it { is_expected.to eq([@runner1])}
end end
describe '#tick_runner_queue' do
it 'sticks the runner to the primary and calls the original method' do
runner = create(:ci_runner)
allow(Gitlab::Database::LoadBalancing).to receive(:enable?)
.and_return(true)
expect(Gitlab::Database::LoadBalancing::Sticking).to receive(:stick)
.with(:runner, runner.id)
expect(Gitlab::Workhorse).to receive(:set_key_and_notify)
runner.tick_runner_queue
end
end
describe '#can_pick?' do describe '#can_pick?' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
......
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