1# frozen_string_literal: true 2 3module Gitlab 4 module Database 5 module LoadBalancing 6 # Configuration settings for a single LoadBalancer instance. 7 class Configuration 8 attr_accessor :hosts, :max_replication_difference, 9 :max_replication_lag_time, :replica_check_interval, 10 :service_discovery 11 12 # Creates a configuration object for the given ActiveRecord model. 13 def self.for_model(model) 14 cfg = model.connection_db_config.configuration_hash.deep_symbolize_keys 15 lb_cfg = cfg[:load_balancing] || {} 16 config = new(model) 17 18 if (diff = lb_cfg[:max_replication_difference]) 19 config.max_replication_difference = diff 20 end 21 22 if (lag = lb_cfg[:max_replication_lag_time]) 23 config.max_replication_lag_time = lag.to_f 24 end 25 26 if (interval = lb_cfg[:replica_check_interval]) 27 config.replica_check_interval = interval.to_f 28 end 29 30 if (hosts = lb_cfg[:hosts]) 31 config.hosts = hosts 32 end 33 34 discover = lb_cfg[:discover] || {} 35 36 # We iterate over the known/default keys so we don't end up with 37 # random keys in our configuration hash. 38 config.service_discovery.each do |key, _| 39 if (value = discover[key]) 40 config.service_discovery[key] = value 41 end 42 end 43 44 config.reuse_primary_connection! 45 46 config 47 end 48 49 def initialize(model, hosts = []) 50 @max_replication_difference = 8.megabytes 51 @max_replication_lag_time = 60.0 52 @replica_check_interval = 60.0 53 @model = model 54 @hosts = hosts 55 @service_discovery = { 56 nameserver: 'localhost', 57 port: 8600, 58 record: nil, 59 record_type: 'A', 60 interval: 60, 61 disconnect_timeout: 120, 62 use_tcp: false 63 } 64 65 # Temporary model for GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ 66 # To be removed with FF 67 @primary_model = nil 68 end 69 70 def db_config_name 71 @model.connection_db_config.name.to_sym 72 end 73 74 # With connection re-use the primary connection can be overwritten 75 # to be used from different model 76 def primary_connection_specification_name 77 (@primary_model || @model).connection_specification_name 78 end 79 80 def primary_db_config 81 (@primary_model || @model).connection_db_config 82 end 83 84 def replica_db_config 85 @model.connection_db_config 86 end 87 88 def pool_size 89 # The pool size may change when booting up GitLab, as GitLab enforces 90 # a certain number of threads. If a Configuration is memoized, this 91 # can lead to incorrect pool sizes. 92 # 93 # To support this scenario, we always attempt to read the pool size 94 # from the model's configuration. 95 @model.connection_db_config.configuration_hash[:pool] || 96 Database.default_pool_size 97 end 98 99 # Returns `true` if the use of load balancing replicas should be 100 # enabled. 101 # 102 # This is disabled for Rake tasks to ensure e.g. database migrations 103 # always produce consistent results. 104 def load_balancing_enabled? 105 return false if Gitlab::Runtime.rake? 106 107 hosts.any? || service_discovery_enabled? 108 end 109 110 # This is disabled for Rake tasks to ensure e.g. database migrations 111 # always produce consistent results. 112 def service_discovery_enabled? 113 return false if Gitlab::Runtime.rake? 114 115 service_discovery[:record].present? 116 end 117 118 # TODO: This is temporary code to allow re-use of primary connection 119 # if the two connections are pointing to the same host. This is needed 120 # to properly support transaction visibility. 121 # 122 # This behavior is required to support [Phase 3](https://gitlab.com/groups/gitlab-org/-/epics/6160#progress). 123 # This method is meant to be removed as soon as it is finished. 124 # 125 # The remapping is done as-is: 126 # export GITLAB_LOAD_BALANCING_REUSE_PRIMARY_<name-of-connection>=<new-name-of-connection> 127 # 128 # Ex.: 129 # export GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci=main 130 # 131 def reuse_primary_connection! 132 new_connection = ENV["GITLAB_LOAD_BALANCING_REUSE_PRIMARY_#{db_config_name}"] 133 return unless new_connection.present? 134 135 @primary_model = Gitlab::Database.database_base_models[new_connection.to_sym] 136 137 unless @primary_model 138 raise "Invalid value for 'GITLAB_LOAD_BALANCING_REUSE_PRIMARY_#{db_config_name}=#{new_connection}'" 139 end 140 end 141 end 142 end 143 end 144end 145