2021-11-18 22:05:49 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module Gitlab
|
|
|
|
module Database
|
|
|
|
module LoadBalancing
|
|
|
|
# Class for setting up load balancing of a specific model.
|
|
|
|
class Setup
|
2021-12-11 22:18:48 +05:30
|
|
|
attr_reader :model, :configuration
|
2021-11-18 22:05:49 +05:30
|
|
|
|
|
|
|
def initialize(model, start_service_discovery: false)
|
|
|
|
@model = model
|
|
|
|
@configuration = Configuration.for_model(model)
|
|
|
|
@start_service_discovery = start_service_discovery
|
|
|
|
end
|
|
|
|
|
|
|
|
def setup
|
2021-12-11 22:18:48 +05:30
|
|
|
configure_connection
|
|
|
|
setup_connection_proxy
|
2021-11-18 22:05:49 +05:30
|
|
|
setup_service_discovery
|
2021-12-11 22:18:48 +05:30
|
|
|
setup_feature_flag_to_model_load_balancing
|
2021-11-18 22:05:49 +05:30
|
|
|
end
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
def configure_connection
|
2021-11-18 22:05:49 +05:30
|
|
|
db_config_object = @model.connection_db_config
|
2021-12-11 22:18:48 +05:30
|
|
|
|
|
|
|
hash = db_config_object.configuration_hash.merge(
|
|
|
|
prepared_statements: false,
|
|
|
|
pool: Gitlab::Database.default_pool_size
|
|
|
|
)
|
2021-11-18 22:05:49 +05:30
|
|
|
|
|
|
|
hash_config = ActiveRecord::DatabaseConfigurations::HashConfig.new(
|
|
|
|
db_config_object.env_name,
|
|
|
|
db_config_object.name,
|
2021-12-11 22:18:48 +05:30
|
|
|
hash
|
2021-11-18 22:05:49 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
@model.establish_connection(hash_config)
|
|
|
|
end
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
def setup_connection_proxy
|
2021-11-18 22:05:49 +05:30
|
|
|
# We just use a simple `class_attribute` here so we don't need to
|
|
|
|
# inject any modules and/or expose unnecessary methods.
|
2021-12-11 22:18:48 +05:30
|
|
|
setup_class_attribute(:load_balancer, load_balancer)
|
|
|
|
setup_class_attribute(:connection, ConnectionProxy.new(load_balancer))
|
|
|
|
setup_class_attribute(:sticking, Sticking.new(load_balancer))
|
|
|
|
end
|
|
|
|
|
|
|
|
# TODO: This is temporary code to gradually redirect traffic to use
|
|
|
|
# a dedicated DB replicas, or DB primaries (depending on configuration)
|
|
|
|
# This implements a sticky behavior for the current request if enabled.
|
|
|
|
#
|
|
|
|
# This is needed for Phase 3 and Phase 4 of application rollout
|
|
|
|
# https://gitlab.com/groups/gitlab-org/-/epics/6160#progress
|
|
|
|
#
|
|
|
|
# If `GITLAB_USE_MODEL_LOAD_BALANCING` is set, its value is preferred
|
|
|
|
# Otherwise, a `use_model_load_balancing` FF value is used
|
|
|
|
def setup_feature_flag_to_model_load_balancing
|
|
|
|
return if active_record_base?
|
2021-11-18 22:05:49 +05:30
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
@model.singleton_class.prepend(ModelLoadBalancingFeatureFlagMixin)
|
2021-11-18 22:05:49 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
def setup_service_discovery
|
|
|
|
return unless configuration.service_discovery_enabled?
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
sv = ServiceDiscovery.new(load_balancer, **configuration.service_discovery)
|
2021-11-18 22:05:49 +05:30
|
|
|
|
|
|
|
sv.perform_service_discovery
|
|
|
|
|
|
|
|
sv.start if @start_service_discovery
|
|
|
|
end
|
2021-12-11 22:18:48 +05:30
|
|
|
|
|
|
|
def load_balancer
|
|
|
|
@load_balancer ||= LoadBalancer.new(configuration)
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def setup_class_attribute(attribute, value)
|
|
|
|
@model.class_attribute(attribute)
|
|
|
|
@model.public_send("#{attribute}=", value) # rubocop:disable GitlabSecurity/PublicSend
|
|
|
|
end
|
|
|
|
|
|
|
|
def active_record_base?
|
|
|
|
@model == ActiveRecord::Base
|
|
|
|
end
|
|
|
|
|
|
|
|
module ModelLoadBalancingFeatureFlagMixin
|
|
|
|
extend ActiveSupport::Concern
|
|
|
|
|
|
|
|
def use_model_load_balancing?
|
|
|
|
# Cache environment variable and return env variable first if defined
|
|
|
|
use_model_load_balancing_env = Gitlab::Utils.to_boolean(ENV["GITLAB_USE_MODEL_LOAD_BALANCING"])
|
|
|
|
|
|
|
|
unless use_model_load_balancing_env.nil?
|
|
|
|
return use_model_load_balancing_env
|
|
|
|
end
|
|
|
|
|
|
|
|
# Check a feature flag using RequestStore (if active)
|
|
|
|
return false unless Gitlab::SafeRequestStore.active?
|
|
|
|
|
|
|
|
Gitlab::SafeRequestStore.fetch(:use_model_load_balancing) do
|
|
|
|
Feature.enabled?(:use_model_load_balancing, default_enabled: :yaml)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# rubocop:disable Database/MultipleDatabases
|
|
|
|
def connection
|
|
|
|
use_model_load_balancing? ? super : ActiveRecord::Base.connection
|
|
|
|
end
|
|
|
|
# rubocop:enable Database/MultipleDatabases
|
|
|
|
end
|
2021-11-18 22:05:49 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|