MediaWiki REL1_39
DBLockManager.php
Go to the documentation of this file.
1<?php
27
43abstract class DBLockManager extends QuorumLockManager {
45 protected $dbServers; // (DB name => server config array)
47 protected $statusCache;
48
49 protected $lockExpiry; // integer number of seconds
50 protected $safeDelay; // integer number of seconds
52 protected $conns = [];
53
76 public function __construct( array $config ) {
77 parent::__construct( $config );
78
79 $this->dbServers = $config['dbServers'];
80 if ( isset( $config['dbsByBucket'] ) ) {
81 // Sanitize srvsByBucket config to prevent PHP errors
82 $this->srvsByBucket = array_filter( $config['dbsByBucket'], 'is_array' );
83 $this->srvsByBucket = array_values( $this->srvsByBucket ); // consecutive
84 } else {
85 $this->srvsByBucket = [ array_keys( $this->dbServers ) ];
86 }
87
88 if ( isset( $config['lockExpiry'] ) ) {
89 $this->lockExpiry = $config['lockExpiry'];
90 } else {
91 $met = ini_get( 'max_execution_time' );
92 $this->lockExpiry = $met ?: 60; // use some sensible amount if 0
93 }
94 $this->safeDelay = ( $this->lockExpiry <= 0 )
95 ? 60 // pick a safe-ish number to match DB timeout default
96 : $this->lockExpiry; // cover worst case
97
98 // Tracks peers that couldn't be queried recently to avoid lengthy
99 // connection timeouts. This is useless if each bucket has one peer.
100 $this->statusCache = $config['srvCache'] ?? new HashBagOStuff();
101 }
102
110 protected function getLocksOnServer( $lockSrv, array $pathsByType ) {
111 $status = StatusValue::newGood();
112 foreach ( $pathsByType as $type => $paths ) {
113 $status->merge( $this->doGetLocksOnServer( $lockSrv, $paths, $type ) );
114 }
115
116 return $status;
117 }
118
119 abstract protected function doGetLocksOnServer( $lockSrv, array $paths, $type );
120
125 protected function freeLocksOnServer( $lockSrv, array $pathsByType ) {
126 return StatusValue::newGood();
127 }
128
134 protected function isServerUp( $lockSrv ) {
135 if ( !$this->cacheCheckFailures( $lockSrv ) ) {
136 return false; // recent failure to connect
137 }
138 try {
139 $this->getConnection( $lockSrv );
140 } catch ( DBError $e ) {
141 $this->cacheRecordFailure( $lockSrv );
142
143 return false; // failed to connect
144 }
145
146 return true;
147 }
148
157 protected function getConnection( $lockDb ) {
158 if ( !isset( $this->conns[$lockDb] ) ) {
159 if ( $this->dbServers[$lockDb] instanceof IDatabase ) {
160 // Direct injected connection hande for $lockDB
161 $db = $this->dbServers[$lockDb];
162 } elseif ( is_array( $this->dbServers[$lockDb] ) ) {
163 // Parameters to construct a new database connection
164 $config = $this->dbServers[$lockDb];
165 $config['flags'] = ( $config['flags'] ?? 0 );
166 $config['flags'] &= ~( IDatabase::DBO_TRX | IDatabase::DBO_DEFAULT );
167 $db = Database::factory( $config['type'], $config );
168 if ( !$db ) {
169 throw new UnexpectedValueException( "No database connection for server called '$lockDb'." );
170 }
171 } else {
172 throw new UnexpectedValueException( "No server called '$lockDb'." );
173 }
174 # If the connection drops, try to avoid letting the DB rollback
175 # and release the locks before the file operations are finished.
176 # This won't handle the case of DB server restarts however.
177 $options = [];
178 if ( $this->lockExpiry > 0 ) {
179 $options['connTimeout'] = $this->lockExpiry;
180 }
181 $db->setSessionOptions( $options );
182 $this->initConnection( $lockDb, $db );
183
184 $this->conns[$lockDb] = $db;
185 }
186
187 return $this->conns[$lockDb];
188 }
189
198 protected function initConnection( $lockDb, IDatabase $db ) {
199 }
200
208 protected function cacheCheckFailures( $lockDb ) {
209 return ( $this->safeDelay > 0 )
210 ? !$this->statusCache->get( $this->getMissKey( $lockDb ) )
211 : true;
212 }
213
220 protected function cacheRecordFailure( $lockDb ) {
221 return ( $this->safeDelay > 0 )
222 ? $this->statusCache->set( $this->getMissKey( $lockDb ), 1, $this->safeDelay )
223 : true;
224 }
225
232 protected function getMissKey( $lockDb ) {
233 return 'dblockmanager:downservers:' . str_replace( ' ', '_', $lockDb );
234 }
235
239 public function __destruct() {
240 $this->releaseAllLocks();
241 foreach ( $this->conns as $db ) {
242 $db->close( __METHOD__ );
243 }
244 }
245}
Class representing a cache/ephemeral data store.
Definition BagOStuff.php:85
Version of LockManager based on using named/row DB locks.
__destruct()
Make sure remaining locks get cleared.
isServerUp( $lockSrv)
cacheRecordFailure( $lockDb)
Log a lock request failure to the cache.
IDatabase[] $conns
Map Database connections (DB name => Database)
freeLocksOnServer( $lockSrv, array $pathsByType)
Get a connection to a lock server and release locks on $paths.Subclasses must effectively implement t...
getMissKey( $lockDb)
Get a cache key for recent query misses for a DB.
getConnection( $lockDb)
Get (or reuse) a connection to a lock DB.
initConnection( $lockDb, IDatabase $db)
Do additional initialization for new lock DB connection.
getLocksOnServer( $lockSrv, array $pathsByType)
array[] IDatabase[] $dbServers
Map of (DB names => server config or IDatabase)
__construct(array $config)
Construct a new instance from configuration.
doGetLocksOnServer( $lockSrv, array $paths, $type)
cacheCheckFailures( $lockDb)
Checks if the DB has not recently had connection/query errors.
BagOStuff $statusCache
Simple store for keeping values in an associative array for the current process.
Version of LockManager that uses a quorum from peer servers for locks.
releaseAllLocks()
Release all locks that this session is holding.
Database error base class.
Definition DBError.php:31
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:39