1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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 ( $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;
}
}
|