MediaWiki  master
MySqlLockManager.php
Go to the documentation of this file.
1 <?php
2 
5 
20  protected $lockTypeMap = [
21  self::LOCK_SH => self::LOCK_SH,
22  self::LOCK_UW => self::LOCK_SH,
23  self::LOCK_EX => self::LOCK_EX
24  ];
25 
26  public function __construct( array $config ) {
27  parent::__construct( $config );
28 
29  $this->session = substr( $this->session, 0, 31 ); // fit to field
30  }
31 
32  protected function initConnection( $lockDb, IDatabase $db ) {
33  # Let this transaction see lock rows from other transactions
34  $db->query( "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;", __METHOD__ );
35  # Do everything in a transaction as it all gets rolled back eventually
36  $db->startAtomic( __CLASS__ );
37  }
38 
49  protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
50  $status = StatusValue::newGood();
51 
52  $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
53 
54  $keys = []; // list of hash keys for the paths
55  $data = []; // list of rows to insert
56  $checkEXKeys = []; // list of hash keys that this has no EX lock on
57  # Build up values for INSERT clause
58  foreach ( $paths as $path ) {
59  $key = $this->sha1Base36Absolute( $path );
60  $keys[] = $key;
61  $data[] = [ 'fls_key' => $key, 'fls_session' => $this->session ];
62  if ( !isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
63  $checkEXKeys[] = $key; // this has no EX lock on $key itself
64  }
65  }
66 
67  # Block new writers (both EX and SH locks leave entries here)...
68  $db->insert( 'filelocks_shared', $data, __METHOD__, [ 'IGNORE' ] );
69  # Actually do the locking queries...
70  if ( $type == self::LOCK_SH ) { // reader locks
71  # Bail if there are any existing writers...
72  if ( count( $checkEXKeys ) ) {
73  $blocked = $db->selectField(
74  'filelocks_exclusive',
75  '1',
76  [ 'fle_key' => $checkEXKeys ],
77  __METHOD__
78  );
79  } else {
80  $blocked = false;
81  }
82  # Other prospective writers that haven't yet updated filelocks_exclusive
83  # will recheck filelocks_shared after doing so and bail due to this entry.
84  } else { // writer locks
85  $encSession = $db->addQuotes( $this->session );
86  # Bail if there are any existing writers...
87  # This may detect readers, but the safe check for them is below.
88  # Note: if two writers come at the same time, both bail :)
89  $blocked = $db->selectField(
90  'filelocks_shared',
91  '1',
92  [ 'fls_key' => $keys, "fls_session != $encSession" ],
93  __METHOD__
94  );
95  if ( !$blocked ) {
96  # Build up values for INSERT clause
97  $data = [];
98  foreach ( $keys as $key ) {
99  $data[] = [ 'fle_key' => $key ];
100  }
101  # Block new readers/writers...
102  $db->insert( 'filelocks_exclusive', $data, __METHOD__ );
103  # Bail if there are any existing readers...
104  $blocked = $db->selectField(
105  'filelocks_shared',
106  '1',
107  [ 'fls_key' => $keys, "fls_session != $encSession" ],
108  __METHOD__
109  );
110  }
111  }
112 
113  if ( $blocked ) {
114  foreach ( $paths as $path ) {
115  $status->fatal( 'lockmanager-fail-acquirelock', $path );
116  }
117  }
118 
119  return $status;
120  }
121 
126  protected function releaseAllLocks() {
127  $status = StatusValue::newGood();
128 
129  foreach ( $this->conns as $lockDb => $db ) {
130  if ( $db->trxLevel() ) { // in transaction
131  try {
132  $db->rollback( __METHOD__ ); // finish transaction and kill any rows
133  } catch ( DBError $e ) {
134  $status->fatal( 'lockmanager-fail-db-release', $lockDb );
135  }
136  }
137  }
138 
139  return $status;
140  }
141 }
MySqlLockManager\__construct
__construct(array $config)
Construct a new instance from configuration.
Definition: MySqlLockManager.php:26
LockManager\LOCK_SH
const LOCK_SH
Lock types; stronger locks have higher values.
Definition: LockManager.php:69
MySqlLockManager\releaseAllLocks
releaseAllLocks()
Definition: MySqlLockManager.php:126
Wikimedia\Rdbms\DBError
Database error base class @newable Stable to extend.
Definition: DBError.php:32
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
LockManager\sha1Base36Absolute
sha1Base36Absolute( $path)
Get the base 36 SHA-1 of a string, padded to 31 digits.
Definition: LockManager.php:180
MySqlLockManager\initConnection
initConnection( $lockDb, IDatabase $db)
Do additional initialization for new lock DB connection @stable to override.
Definition: MySqlLockManager.php:32
Wikimedia\Rdbms\IDatabase\query
query( $sql, $fname=__METHOD__, $flags=0)
Run an SQL query and return the result.
MySqlLockManager\doGetLocksOnServer
doGetLocksOnServer( $lockSrv, array $paths, $type)
Get a connection to a lock DB and acquire locks on $paths.
Definition: MySqlLockManager.php:49
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82
LockManager\$session
string $session
Random 32-char hex number.
Definition: LockManager.php:66
MySqlLockManager\$lockTypeMap
array $lockTypeMap
Mapping of lock types to the type actually used.
Definition: MySqlLockManager.php:20
MySqlLockManager
MySQL version of DBLockManager that supports shared locks.
Definition: MySqlLockManager.php:18
$path
$path
Definition: NoLocalSettings.php:25
$keys
$keys
Definition: testCompression.php:72
DBLockManager\getConnection
getConnection( $lockDb)
Get (or reuse) a connection to a lock DB.
Definition: DBLockManager.php:152
DBLockManager
Version of LockManager based on using named/row DB locks.
Definition: DBLockManager.php:43
LockManager\LOCK_EX
const LOCK_EX
Definition: LockManager.php:71
$type
$type
Definition: testCompression.php:52
Wikimedia\Rdbms\IDatabase\startAtomic
startAtomic( $fname=__METHOD__, $cancelable=self::ATOMIC_NOT_CANCELABLE)
Begin an atomic section of SQL statements.