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