2016-08-20 17:26:01 +00:00
|
|
|
<?php
|
2017-02-24 16:17:16 +00:00
|
|
|
|
|
|
|
|
use Wikimedia\Rdbms\DBError;
|
|
|
|
|
|
2016-08-20 17:26:01 +00:00
|
|
|
/**
|
|
|
|
|
* PostgreSQL version of DBLockManager that supports shared locks.
|
|
|
|
|
* All locks are non-blocking, which avoids deadlocks.
|
|
|
|
|
*
|
|
|
|
|
* @ingroup LockManager
|
|
|
|
|
*/
|
|
|
|
|
class PostgreSqlLockManager extends DBLockManager {
|
|
|
|
|
/** @var array Mapping of lock types to the type actually used */
|
|
|
|
|
protected $lockTypeMap = [
|
|
|
|
|
self::LOCK_SH => self::LOCK_SH,
|
|
|
|
|
self::LOCK_UW => self::LOCK_SH,
|
|
|
|
|
self::LOCK_EX => self::LOCK_EX
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
|
2016-09-18 04:42:56 +00:00
|
|
|
$status = StatusValue::newGood();
|
2019-01-09 16:24:36 +00:00
|
|
|
if ( $paths === [] ) {
|
2016-08-20 17:26:01 +00:00
|
|
|
return $status; // nothing to lock
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$db = $this->getConnection( $lockSrv ); // checked in isServerUp()
|
|
|
|
|
$bigints = array_unique( array_map(
|
2021-02-10 22:31:02 +00:00
|
|
|
static function ( $key ) {
|
2016-08-20 17:26:01 +00:00
|
|
|
return Wikimedia\base_convert( substr( $key, 0, 15 ), 16, 10 );
|
|
|
|
|
},
|
|
|
|
|
array_map( [ $this, 'sha1Base16Absolute' ], $paths )
|
|
|
|
|
) );
|
|
|
|
|
|
|
|
|
|
// Try to acquire all the locks...
|
|
|
|
|
$fields = [];
|
|
|
|
|
foreach ( $bigints as $bigint ) {
|
|
|
|
|
$fields[] = ( $type == self::LOCK_SH )
|
|
|
|
|
? "pg_try_advisory_lock_shared({$db->addQuotes( $bigint )}) AS K$bigint"
|
|
|
|
|
: "pg_try_advisory_lock({$db->addQuotes( $bigint )}) AS K$bigint";
|
|
|
|
|
}
|
|
|
|
|
$res = $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
|
|
|
|
|
$row = $res->fetchRow();
|
|
|
|
|
|
|
|
|
|
if ( in_array( 'f', $row ) ) {
|
|
|
|
|
// Release any acquired locks if some could not be acquired...
|
|
|
|
|
$fields = [];
|
|
|
|
|
foreach ( $row as $kbigint => $ok ) {
|
|
|
|
|
if ( $ok === 't' ) { // locked
|
|
|
|
|
$bigint = substr( $kbigint, 1 ); // strip off the "K"
|
|
|
|
|
$fields[] = ( $type == self::LOCK_SH )
|
|
|
|
|
? "pg_advisory_unlock_shared({$db->addQuotes( $bigint )})"
|
|
|
|
|
: "pg_advisory_unlock({$db->addQuotes( $bigint )})";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( count( $fields ) ) {
|
|
|
|
|
$db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
|
|
|
|
|
}
|
2021-11-01 02:40:54 +00:00
|
|
|
$status->fatal( 'lockmanager-fail-conflict' );
|
2016-08-20 17:26:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @see QuorumLockManager::releaseAllLocks()
|
2016-09-16 22:55:40 +00:00
|
|
|
* @return StatusValue
|
2016-08-20 17:26:01 +00:00
|
|
|
*/
|
|
|
|
|
protected function releaseAllLocks() {
|
2016-09-18 04:42:56 +00:00
|
|
|
$status = StatusValue::newGood();
|
2016-08-20 17:26:01 +00:00
|
|
|
|
|
|
|
|
foreach ( $this->conns as $lockDb => $db ) {
|
|
|
|
|
try {
|
|
|
|
|
$db->query( "SELECT pg_advisory_unlock_all()", __METHOD__ );
|
|
|
|
|
} catch ( DBError $e ) {
|
|
|
|
|
$status->fatal( 'lockmanager-fail-db-release', $lockDb );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $status;
|
|
|
|
|
}
|
|
|
|
|
}
|