Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
59.09% covered (warning)
59.09%
26 / 44
33.33% covered (danger)
33.33%
3 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
LBFactorySimple
59.09% covered (warning)
59.09%
26 / 44
33.33% covered (danger)
33.33%
3 / 9
40.18
0.00% covered (danger)
0.00%
0 / 1
 __construct
70.00% covered (warning)
70.00%
7 / 10
0.00% covered (danger)
0.00%
0 / 1
5.68
 newMainLB
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getMainLB
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 newExternalLB
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getExternalLB
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getAllMainLBs
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAllExternalLBs
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 newLoadBalancer
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 getLBsForOwner
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
3.14
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6namespace Wikimedia\Rdbms;
7
8use InvalidArgumentException;
9
10/**
11 * LoadBalancer manager for sites with one "main" cluster and any number of "external" clusters
12 *
13 * @see LBFactoryMulti
14 *
15 * The class allows for large site farms to split up their data in the following ways:
16 *   - Vertically shard compact site-specific data by site (e.g. page/comment metadata)
17 *   - Vertically shard compact global data by module (e.g. account/notification data)
18 *   - Horizontally shard any bulk data by blob key (e.g. page/comment content)
19 *
20 * @ingroup Database
21 */
22class LBFactorySimple extends LBFactory {
23    /** @var LoadBalancer */
24    private $mainLB;
25    /** @var ILoadBalancerForOwner[] */
26    private $externalLBs = [];
27
28    /** @var array Configuration for the LoadMonitor to use within LoadBalancer instances */
29    private $loadMonitorConfig;
30
31    /** @var array[] Map of (server index => server config map) */
32    private $mainServers;
33    /** @var array[][] Map of (cluster => server index => server config map) */
34    private $externalServersByCluster = [];
35
36    /**
37     * @see LBFactory::__construct()
38     * @param array $conf Additional parameters include:
39     *   - servers : list of server config maps to Database::factory().
40     *      Additionally, the server maps should have a 'load' key, which is used to decide
41     *      how often clients connect to one server verses the others. A 'max lag' key should
42     *      also be set on server maps, indicating how stale the data can be before the load
43     *      balancer tries to avoid using it. The map can have 'is static' set to disable blocking
44     *      replication sync checks (intended for archive servers with unchanging data).
45     *   - externalClusters : map of cluster names to server arrays. The servers arrays have the
46     *      same format as "servers" above.
47     *   - loadMonitor: LoadMonitor::__construct() parameters with "class" field. [optional]
48     */
49    public function __construct( array $conf ) {
50        parent::__construct( $conf );
51
52        $this->mainServers = $conf['servers'] ?? [];
53        foreach ( ( $conf['externalClusters'] ?? [] ) as $cluster => $servers ) {
54            foreach ( $servers as $index => $server ) {
55                $this->externalServersByCluster[$cluster][$index] = $server;
56            }
57        }
58
59        if ( isset( $conf['loadMonitor'] ) ) {
60            $this->loadMonitorConfig = $conf['loadMonitor'];
61        } elseif ( isset( $conf['loadMonitorClass'] ) ) { // b/c
62            $this->loadMonitorConfig = [ 'class' => $conf['loadMonitorClass'] ];
63        } else {
64            $this->loadMonitorConfig = [ 'class' => LoadMonitor::class ];
65        }
66    }
67
68    /** @inheritDoc */
69    public function newMainLB( $domain = false ): ILoadBalancerForOwner {
70        return $this->newLoadBalancer(
71            self::CLUSTER_MAIN_DEFAULT,
72            $this->mainServers
73        );
74    }
75
76    /** @inheritDoc */
77    public function getMainLB( $domain = false ): ILoadBalancer {
78        $this->mainLB ??= $this->newMainLB( $domain );
79
80        return $this->mainLB;
81    }
82
83    /** @inheritDoc */
84    public function newExternalLB( $cluster ): ILoadBalancerForOwner {
85        if ( !isset( $this->externalServersByCluster[$cluster] ) ) {
86            throw new InvalidArgumentException( "Unknown cluster '$cluster'." );
87        }
88
89        return $this->newLoadBalancer(
90            $cluster,
91            $this->externalServersByCluster[$cluster]
92        );
93    }
94
95    /** @inheritDoc */
96    public function getExternalLB( $cluster ): ILoadBalancer {
97        if ( !isset( $this->externalLBs[$cluster] ) ) {
98            $this->externalLBs[$cluster] = $this->newExternalLB( $cluster );
99        }
100
101        return $this->externalLBs[$cluster];
102    }
103
104    public function getAllMainLBs(): array {
105        return [ self::CLUSTER_MAIN_DEFAULT => $this->getMainLB() ];
106    }
107
108    public function getAllExternalLBs(): array {
109        $lbs = [];
110        foreach ( $this->externalServersByCluster as $cluster => $_ ) {
111            $lbs[$cluster] = $this->getExternalLB( $cluster );
112        }
113
114        return $lbs;
115    }
116
117    private function newLoadBalancer( string $clusterName, array $servers ): ILoadBalancerForOwner {
118        $lb = new LoadBalancer( array_merge(
119            $this->baseLoadBalancerParams(),
120            [
121                'servers' => $servers,
122                'loadMonitor' => $this->loadMonitorConfig,
123                'clusterName' => $clusterName
124            ]
125        ) );
126        $this->initLoadBalancer( $lb );
127
128        return $lb;
129    }
130
131    /** @inheritDoc */
132    protected function getLBsForOwner() {
133        if ( $this->mainLB !== null ) {
134            yield $this->mainLB;
135        }
136        foreach ( $this->externalLBs as $lb ) {
137            yield $lb;
138        }
139    }
140}