MediaWiki  master
DBLockManager.php
Go to the documentation of this file.
1 <?php
27 
43 abstract 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 }
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:52
HashBagOStuff
Simple store for keeping values in an associative array for the current process.
Definition: HashBagOStuff.php:32
DBLockManager\isServerUp
isServerUp( $lockSrv)
Definition: DBLockManager.php:134
DBLockManager\cacheRecordFailure
cacheRecordFailure( $lockDb)
Log a lock request failure to the cache.
Definition: DBLockManager.php:220
DBLockManager\__construct
__construct(array $config)
Construct a new instance from configuration.
Definition: DBLockManager.php:76
DBLockManager\getLocksOnServer
getLocksOnServer( $lockSrv, array $pathsByType)
Definition: DBLockManager.php:110
BagOStuff
Class representing a cache/ephemeral data store.
Definition: BagOStuff.php:86
DBLockManager\$safeDelay
$safeDelay
Definition: DBLockManager.php:50
Wikimedia\Rdbms\DBError
Database error base class.
Definition: DBError.php:32
DBO_TRX
const DBO_TRX
Definition: defines.php:12
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
DBLockManager\cacheCheckFailures
cacheCheckFailures( $lockDb)
Checks if the DB has not recently had connection/query errors.
Definition: DBLockManager.php:208
QuorumLockManager
Version of LockManager that uses a quorum from peer servers for locks.
Definition: QuorumLockManager.php:32
QuorumLockManager\releaseAllLocks
releaseAllLocks()
Release all locks that this session is holding.
DBLockManager\getMissKey
getMissKey( $lockDb)
Get a cache key for recent query misses for a DB.
Definition: DBLockManager.php:232
DBLockManager\doGetLocksOnServer
doGetLocksOnServer( $lockSrv, array $paths, $type)
DBLockManager\initConnection
initConnection( $lockDb, IDatabase $db)
Do additional initialization for new lock DB connection @stable to override.
Definition: DBLockManager.php:198
DBLockManager\$dbServers
array[] IDatabase[] $dbServers
Map of (DB names => server config or IDatabase)
Definition: DBLockManager.php:45
DBLockManager\$conns
IDatabase[] $conns
Map Database connections (DB name => Database)
Definition: DBLockManager.php:52
DBLockManager\__destruct
__destruct()
Make sure remaining locks get cleared.
Definition: DBLockManager.php:239
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82
DBLockManager\$lockExpiry
$lockExpiry
Definition: DBLockManager.php:49
DBLockManager\$statusCache
BagOStuff $statusCache
Definition: DBLockManager.php:47
DBLockManager\getConnection
getConnection( $lockDb)
Get (or reuse) a connection to a lock DB.
Definition: DBLockManager.php:157
DBLockManager
Version of LockManager based on using named/row DB locks.
Definition: DBLockManager.php:43
DBO_DEFAULT
const DBO_DEFAULT
Definition: defines.php:13
DBLockManager\freeLocksOnServer
freeLocksOnServer( $lockSrv, array $pathsByType)
Get a connection to a lock server and release locks on $paths.Subclasses must effectively implement t...
Definition: DBLockManager.php:125
$type
$type
Definition: testCompression.php:52