diff options
Diffstat (limited to 'www/wiki/includes/libs/lockmanager/PostgreSqlLockManager.php')
-rw-r--r-- | www/wiki/includes/libs/lockmanager/PostgreSqlLockManager.php | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/www/wiki/includes/libs/lockmanager/PostgreSqlLockManager.php b/www/wiki/includes/libs/lockmanager/PostgreSqlLockManager.php new file mode 100644 index 00000000..65c69938 --- /dev/null +++ b/www/wiki/includes/libs/lockmanager/PostgreSqlLockManager.php @@ -0,0 +1,82 @@ +<?php + +use Wikimedia\Rdbms\DBError; + +/** + * 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 ) { + $status = StatusValue::newGood(); + if ( !count( $paths ) ) { + return $status; // nothing to lock + } + + $db = $this->getConnection( $lockSrv ); // checked in isServerUp() + $bigints = array_unique( array_map( + function ( $key ) { + 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__ ); + } + foreach ( $paths as $path ) { + $status->fatal( 'lockmanager-fail-acquirelock', $path ); + } + } + + return $status; + } + + /** + * @see QuorumLockManager::releaseAllLocks() + * @return StatusValue + */ + protected function releaseAllLocks() { + $status = StatusValue::newGood(); + + 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; + } +} |