MediaWiki  master
DBLockManager.php
Go to the documentation of this file.
1 <?php
27 
42 abstract class DBLockManager extends QuorumLockManager {
44  protected $dbServers; // (DB name => server config array)
46  protected $statusCache;
47 
48  protected $lockExpiry; // integer number of seconds
49  protected $safeDelay; // integer number of seconds
51  protected $conns = [];
52 
73  public function __construct( array $config ) {
74  parent::__construct( $config );
75 
76  $this->dbServers = $config['dbServers'];
77  // Sanitize srvsByBucket config to prevent PHP errors
78  $this->srvsByBucket = array_filter( $config['dbsByBucket'], 'is_array' );
79  $this->srvsByBucket = array_values( $this->srvsByBucket ); // consecutive
80 
81  if ( isset( $config['lockExpiry'] ) ) {
82  $this->lockExpiry = $config['lockExpiry'];
83  } else {
84  $met = ini_get( 'max_execution_time' );
85  $this->lockExpiry = $met ?: 60; // use some sane amount if 0
86  }
87  $this->safeDelay = ( $this->lockExpiry <= 0 )
88  ? 60 // pick a safe-ish number to match DB timeout default
89  : $this->lockExpiry; // cover worst case
90 
91  // Tracks peers that couldn't be queried recently to avoid lengthy
92  // connection timeouts. This is useless if each bucket has one peer.
93  $this->statusCache = $config['srvCache'] ?? new HashBagOStuff();
94  }
95 
102  protected function getLocksOnServer( $lockSrv, array $pathsByType ) {
103  $status = StatusValue::newGood();
104  foreach ( $pathsByType as $type => $paths ) {
105  $status->merge( $this->doGetLocksOnServer( $lockSrv, $paths, $type ) );
106  }
107 
108  return $status;
109  }
110 
111  abstract protected function doGetLocksOnServer( $lockSrv, array $paths, $type );
112 
113  protected function freeLocksOnServer( $lockSrv, array $pathsByType ) {
114  return StatusValue::newGood();
115  }
116 
122  protected function isServerUp( $lockSrv ) {
123  if ( !$this->cacheCheckFailures( $lockSrv ) ) {
124  return false; // recent failure to connect
125  }
126  try {
127  $this->getConnection( $lockSrv );
128  } catch ( DBError $e ) {
129  $this->cacheRecordFailure( $lockSrv );
130 
131  return false; // failed to connect
132  }
133 
134  return true;
135  }
136 
145  protected function getConnection( $lockDb ) {
146  if ( !isset( $this->conns[$lockDb] ) ) {
147  if ( $this->dbServers[$lockDb] instanceof IDatabase ) {
148  // Direct injected connection hande for $lockDB
149  $db = $this->dbServers[$lockDb];
150  } elseif ( is_array( $this->dbServers[$lockDb] ) ) {
151  // Parameters to construct a new database connection
152  $config = $this->dbServers[$lockDb];
153  $config['flags'] = ( $config['flags'] ?? 0 );
154  $config['flags'] &= ~( IDatabase::DBO_TRX | IDatabase::DBO_DEFAULT );
155  $db = Database::factory( $config['type'], $config );
156  } else {
157  throw new UnexpectedValueException( "No server called '$lockDb'." );
158  }
159  # If the connection drops, try to avoid letting the DB rollback
160  # and release the locks before the file operations are finished.
161  # This won't handle the case of DB server restarts however.
162  $options = [];
163  if ( $this->lockExpiry > 0 ) {
164  $options['connTimeout'] = $this->lockExpiry;
165  }
166  $db->setSessionOptions( $options );
167  $this->initConnection( $lockDb, $db );
168 
169  $this->conns[$lockDb] = $db;
170  }
171 
172  return $this->conns[$lockDb];
173  }
174 
182  protected function initConnection( $lockDb, IDatabase $db ) {
183  }
184 
192  protected function cacheCheckFailures( $lockDb ) {
193  return ( $this->safeDelay > 0 )
194  ? !$this->statusCache->get( $this->getMissKey( $lockDb ) )
195  : true;
196  }
197 
204  protected function cacheRecordFailure( $lockDb ) {
205  return ( $this->safeDelay > 0 )
206  ? $this->statusCache->set( $this->getMissKey( $lockDb ), 1, $this->safeDelay )
207  : true;
208  }
209 
216  protected function getMissKey( $lockDb ) {
217  return 'dblockmanager:downservers:' . str_replace( ' ', '_', $lockDb );
218  }
219 
223  function __destruct() {
224  $this->releaseAllLocks();
225  foreach ( $this->conns as $db ) {
226  $db->close();
227  }
228  }
229 }
cacheRecordFailure( $lockDb)
Log a lock request failure to the cache.
__destruct()
Make sure remaining locks get cleared for sanity.
releaseAllLocks()
Release all locks that this session is holding.
Version of LockManager based on using named/row DB locks.
initConnection( $lockDb, IDatabase $db)
Do additional initialization for new lock DB connection.
freeLocksOnServer( $lockSrv, array $pathsByType)
doGetLocksOnServer( $lockSrv, array $paths, $type)
getLocksOnServer( $lockSrv, array $pathsByType)
isServerUp( $lockSrv)
BagOStuff $statusCache
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
const DBO_TRX
Definition: defines.php:12
cacheCheckFailures( $lockDb)
Checks if the DB has not recently had connection/query errors.
const DBO_DEFAULT
Definition: defines.php:13
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
array [] IDatabase [] $dbServers
Map of (DB names => server config or IDatabase)
__construct(array $config)
Construct a new instance from configuration.
getConnection( $lockDb)
Get (or reuse) a connection to a lock DB.
IDatabase [] $conns
Map Database connections (DB name => Database)
Version of LockManager that uses a quorum from peer servers for locks.
Database error base class.
Definition: DBError.php:30
getMissKey( $lockDb)
Get a cache key for recent query misses for a DB.