55 private $mainLBs = [];
57 private $externalLBs = [];
60 private $hostsByServerName;
62 private $sectionsByDB;
64 private $groupLoadsBySection;
66 private $externalLoadsByCluster;
68 private $serverTemplate;
70 private $externalTemplateOverrides;
72 private $templateOverridesBySection;
74 private $templateOverridesByCluster;
76 private $masterTemplateOverrides;
78 private $templateOverridesByServer;
80 private $readOnlyBySection;
82 private $loadMonitorConfig;
134 parent::__construct( $conf );
136 $this->hostsByServerName = $conf[
'hostsByName'] ?? [];
137 $this->sectionsByDB = $conf[
'sectionsByDB'];
138 $this->sectionsByDB += [ self::CLUSTER_MAIN_DEFAULT => self::CLUSTER_MAIN_DEFAULT ];
139 $this->groupLoadsBySection = $conf[
'groupLoadsBySection'] ?? [];
140 foreach ( ( $conf[
'sectionLoads'] ?? [] ) as $section => $loadsByServerName ) {
143 $this->externalLoadsByCluster = $conf[
'externalLoads'] ?? [];
144 $this->serverTemplate = $conf[
'serverTemplate'] ?? [];
145 $this->externalTemplateOverrides = $conf[
'externalTemplateOverrides'] ?? [];
146 $this->templateOverridesBySection = $conf[
'templateOverridesBySection'] ?? [];
147 $this->templateOverridesByCluster = $conf[
'templateOverridesByCluster'] ?? [];
148 $this->masterTemplateOverrides = $conf[
'masterTemplateOverrides'] ?? [];
149 $this->templateOverridesByServer = $conf[
'templateOverridesByServer'] ?? [];
150 $this->readOnlyBySection = $conf[
'readOnlyBySection'] ?? [];
152 if ( isset( $conf[
'loadMonitor'] ) ) {
153 $this->loadMonitorConfig = $conf[
'loadMonitor'];
154 } elseif ( isset( $conf[
'loadMonitorClass'] ) ) {
155 $this->loadMonitorConfig = [
'class' => $conf[
'loadMonitorClass'] ];
157 $this->loadMonitorConfig = [
'class' => LoadMonitor::class ];
160 foreach ( array_keys( $this->externalLoadsByCluster ) as $cluster ) {
161 if ( isset( $this->groupLoadsBySection[$cluster] ) ) {
162 throw new LogicException(
163 "External cluster '$cluster' has the same name as a main section/cluster"
171 $database = $domainInstance->getDatabase();
172 $section = $this->getSectionFromDatabase( $database );
175 throw new UnexpectedValueException(
"Section '$section' has no hosts defined." );
178 return $this->newLoadBalancer(
181 $this->serverTemplate,
182 $this->templateOverridesBySection[$section] ?? []
184 $this->groupLoadsBySection[$section],
186 is_string( $this->readOnlyReason )
187 ? $this->readOnlyReason
188 : ( $this->readOnlyBySection[$section] ?? false )
193 $domainInstance = $this->resolveDomainInstance( $domain );
194 $section = $this->getSectionFromDatabase( $domainInstance->getDatabase() );
196 if ( !isset( $this->mainLBs[$section] ) ) {
197 $this->mainLBs[$section] = $this->newMainLB( $domain );
200 return $this->mainLBs[$section];
204 if ( !isset( $this->externalLoadsByCluster[$cluster] ) ) {
205 throw new InvalidArgumentException(
"Unknown cluster '$cluster'" );
207 return $this->newLoadBalancer(
210 $this->serverTemplate,
211 $this->externalTemplateOverrides,
212 $this->templateOverridesByCluster[$cluster] ?? []
214 [ ILoadBalancer::GROUP_GENERIC => $this->externalLoadsByCluster[$cluster] ],
215 $this->readOnlyReason
220 if ( !isset( $this->externalLBs[$cluster] ) ) {
221 $this->externalLBs[$cluster] = $this->newExternalLB(
226 return $this->externalLBs[$cluster];
231 foreach ( $this->sectionsByDB as $db => $section ) {
232 if ( !isset( $lbs[$section] ) ) {
233 $lbs[$section] = $this->getMainLB( $db );
242 foreach ( $this->externalLoadsByCluster as $cluster => $unused ) {
243 $lbs[$cluster] = $this->getExternalLB( $cluster );
250 foreach ( $this->mainLBs as $lb ) {
253 foreach ( $this->externalLBs as $lb ) {
267 private function newLoadBalancer(
269 array $serverTemplate,
274 $this->baseLoadBalancerParams(),
276 'servers' => $this->makeServerConfigArrays( $serverTemplate, $groupLoads ),
277 'loadMonitor' => $this->loadMonitorConfig,
278 'readOnlyReason' => $readOnlyReason,
279 'clusterName' => $clusterName
282 $this->initLoadBalancer( $lb );
294 private function makeServerConfigArrays( array $serverTemplate, array $groupLoads ) {
296 if ( !$groupLoads[ILoadBalancer::GROUP_GENERIC] ) {
297 throw new UnexpectedValueException(
"Empty generic load array; no primary DB defined." );
299 $groupLoadsByServerName = $this->reindexGroupLoadsByServerName( $groupLoads );
301 $genericLoads = $groupLoads[ILoadBalancer::GROUP_GENERIC];
303 $genericLoads += array_fill_keys( array_keys( $groupLoadsByServerName ), 0 );
305 foreach ( $genericLoads as $serverName => $load ) {
306 $servers[] = array_merge(
308 $servers ? [] : $this->masterTemplateOverrides,
309 $this->templateOverridesByServer[$serverName] ?? [],
311 'host' => $this->hostsByServerName[$serverName] ?? $serverName,
312 'serverName' => $serverName,
314 'groupLoads' => $groupLoadsByServerName[$serverName] ?? []
328 private function reindexGroupLoadsByServerName( array $groupLoads ) {
329 $groupLoadsByServerName = [];
330 foreach ( $groupLoads as $group => $loadByServerName ) {
331 foreach ( $loadByServerName as $serverName => $load ) {
332 $groupLoadsByServerName[$serverName][$group] = $load;
336 return $groupLoadsByServerName;
343 private function getSectionFromDatabase( $database ) {
344 return $this->sectionsByDB[$database]
345 ?? $this->sectionsByDB[self::CLUSTER_MAIN_DEFAULT]
346 ?? self::CLUSTER_MAIN_DEFAULT;
354 foreach ( $this->mainLBs as $lb ) {
355 $groupLoads = $conf[
'groupLoadsBySection'][$lb->getClusterName()];
356 $groupLoads[ILoadBalancer::GROUP_GENERIC] = $conf[
'sectionLoads'][$lb->getClusterName()] ?? [];
357 $config = [
'servers' => $this->makeServerConfigArrays( $conf[
'serverTemplate'] ?? [], $groupLoads ) ];
358 $lb->reconfigure( $config );
361 foreach ( $this->externalLBs as $lb ) {
362 $groupLoads = [ ILoadBalancer::GROUP_GENERIC => $conf[
'externalLoads'][$lb->getClusterName()] ];
363 $config = [
'servers' => $this->makeServerConfigArrays( $conf[
'serverTemplate'] ?? [], $groupLoads ) ];
364 $lb->reconfigure( $config );