Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 36 |
|
0.00% |
0 / 13 |
CRAP | |
0.00% |
0 / 1 |
ReplicationReporter | |
0.00% |
0 / 36 |
|
0.00% |
0 / 13 |
462 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getTopologyRole | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getLag | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
doGetLag | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getApproximateLagStatus | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
primaryPosWait | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getReplicaPos | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPrimaryPos | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTopologyBasedReadOnlyReason | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
resetReplicationLagStatus | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getRecordedTransactionLagStatus | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
getSessionLagStatus | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
getLogContext | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | namespace Wikimedia\Rdbms\Replication; |
21 | |
22 | use Psr\Log\LoggerInterface; |
23 | use Wikimedia\ObjectCache\BagOStuff; |
24 | use Wikimedia\Rdbms\DBError; |
25 | use Wikimedia\Rdbms\DBPrimaryPos; |
26 | use Wikimedia\Rdbms\IDatabase; |
27 | |
28 | /** |
29 | * @internal |
30 | * @ingroup Database |
31 | * @since 1.40 |
32 | */ |
33 | class ReplicationReporter { |
34 | /** @var string Replication topology role of the server; one of the class ROLE_* constants */ |
35 | protected $topologyRole; |
36 | /** @var LoggerInterface */ |
37 | protected $logger; |
38 | /** @var BagOStuff */ |
39 | protected $srvCache; |
40 | /** @var array|null Replication lag estimate at the time of BEGIN for the last transaction */ |
41 | private $trxReplicaLagStatus = null; |
42 | |
43 | public function __construct( $topologyRole, $logger, $srvCache ) { |
44 | $this->topologyRole = $topologyRole; |
45 | $this->logger = $logger; |
46 | $this->srvCache = $srvCache; |
47 | } |
48 | |
49 | public function getTopologyRole() { |
50 | return $this->topologyRole; |
51 | } |
52 | |
53 | public function getLag( IDatabase $conn ) { |
54 | if ( $this->topologyRole === IDatabase::ROLE_STREAMING_MASTER ) { |
55 | return 0; // this is the primary DB |
56 | } elseif ( $this->topologyRole === IDatabase::ROLE_STATIC_CLONE ) { |
57 | return 0; // static dataset |
58 | } |
59 | |
60 | return $this->doGetLag( $conn ); |
61 | } |
62 | |
63 | /** |
64 | * Get the amount of replication lag for this database server |
65 | * |
66 | * Callers should avoid using this method while a transaction is active |
67 | * |
68 | * @see getLag() |
69 | * |
70 | * @param IDatabase $conn To make queries |
71 | * @return float|int|false Database replication lag in seconds or false on error |
72 | * @throws DBError |
73 | */ |
74 | protected function doGetLag( IDatabase $conn ) { |
75 | return 0; |
76 | } |
77 | |
78 | /** |
79 | * Get a replica DB lag estimate for this server at the start of a transaction |
80 | * |
81 | * This is a no-op unless the server is known a priori to be a replica DB |
82 | * |
83 | * @param IDatabase $conn To make queries |
84 | * @return array ('lag': seconds or false on error, 'since': UNIX timestamp of estimate) |
85 | * @since 1.27 in Database, moved to ReplicationReporter in 1.40 |
86 | */ |
87 | protected function getApproximateLagStatus( IDatabase $conn ) { |
88 | if ( $this->topologyRole === IDatabase::ROLE_STREAMING_REPLICA ) { |
89 | // Avoid exceptions as this is used internally in critical sections |
90 | try { |
91 | $lag = $this->getLag( $conn ); |
92 | } catch ( DBError $e ) { |
93 | $lag = false; |
94 | } |
95 | } else { |
96 | $lag = 0; |
97 | } |
98 | |
99 | return [ 'lag' => $lag, 'since' => microtime( true ) ]; |
100 | } |
101 | |
102 | public function primaryPosWait( IDatabase $conn, DBPrimaryPos $pos, $timeout ) { |
103 | // Real waits are implemented in the subclass. |
104 | return 0; |
105 | } |
106 | |
107 | public function getReplicaPos( IDatabase $conn ) { |
108 | // Stub |
109 | return false; |
110 | } |
111 | |
112 | public function getPrimaryPos( IDatabase $conn ) { |
113 | // Stub |
114 | return false; |
115 | } |
116 | |
117 | /** |
118 | * @return array|null Tuple of (reason string, "role") if read-only; null otherwise |
119 | */ |
120 | public function getTopologyBasedReadOnlyReason() { |
121 | if ( $this->topologyRole === IDatabase::ROLE_STREAMING_REPLICA ) { |
122 | return [ 'Server is configured as a read-only replica database.', 'role' ]; |
123 | } elseif ( $this->topologyRole === IDatabase::ROLE_STATIC_CLONE ) { |
124 | return [ 'Server is configured as a read-only static clone database.', 'role' ]; |
125 | } |
126 | |
127 | return null; |
128 | } |
129 | |
130 | public function resetReplicationLagStatus( IDatabase $conn ) { |
131 | // With REPEATABLE-READ isolation, the first SELECT establishes the read snapshot, |
132 | // so get the replication lag estimate before any transaction SELECT queries come in. |
133 | // This way, the lag estimate reflects what will actually be read. Also, if heartbeat |
134 | // tables are used, this avoids counting snapshot lag as part of replication lag. |
135 | $this->trxReplicaLagStatus = null; // clear cached value first |
136 | $this->trxReplicaLagStatus = $this->getApproximateLagStatus( $conn ); |
137 | } |
138 | |
139 | /** |
140 | * Get the replica DB lag when the current transaction started |
141 | * |
142 | * This is useful given that transactions might use point-in-time read snapshots, |
143 | * in which case the lag estimate should be recorded just before the transaction |
144 | * establishes the read snapshot (either BEGIN or the first SELECT/write query). |
145 | * |
146 | * If snapshots are not used, it is still safe to be pessimistic. |
147 | * |
148 | * This returns null if there is no transaction or the lag status was not yet recorded. |
149 | * |
150 | * @param IDatabase $conn To make queries |
151 | * @return array|null ('lag': seconds or false, 'since': UNIX timestamp of BEGIN) or null |
152 | * @since 1.27 in Database, moved to ReplicationReporter in 1.40 |
153 | */ |
154 | final protected function getRecordedTransactionLagStatus( IDatabase $conn ) { |
155 | return $conn->trxLevel() ? $this->trxReplicaLagStatus : null; |
156 | } |
157 | |
158 | public function getSessionLagStatus( IDatabase $conn ) { |
159 | return $this->getRecordedTransactionLagStatus( $conn ) ?: $this->getApproximateLagStatus( $conn ); |
160 | } |
161 | |
162 | /** |
163 | * Create a log context to pass to PSR-3 logger functions. |
164 | * |
165 | * @param IDatabase $conn To make queries |
166 | * @param array $extras Additional data to add to context |
167 | * @return array |
168 | */ |
169 | protected function getLogContext( IDatabase $conn, array $extras = [] ) { |
170 | return array_merge( |
171 | [ |
172 | 'db_server' => $conn->getServerName(), |
173 | 'db_name' => $conn->getDBname(), |
174 | // TODO: Add db_user |
175 | ], |
176 | $extras |
177 | ); |
178 | } |
179 | } |