3 * MySQL version of DBLockManager that supports shared locks.
5 * All lock servers must have the innodb table defined in locking/filelocks.sql.
6 * All locks are non-blocking, which avoids deadlocks.
10 class MySqlLockManager
extends DBLockManager
{
11 /** @var array Mapping of lock types to the type actually used */
12 protected $lockTypeMap = [
13 self
::LOCK_SH
=> self
::LOCK_SH
,
14 self
::LOCK_UW
=> self
::LOCK_SH
,
15 self
::LOCK_EX
=> self
::LOCK_EX
18 protected function getLocalLB() {
19 // Use a separate connection so releaseAllLocks() doesn't rollback the main trx
20 return wfGetLBFactory()->newMainLB( $this->domain
);
23 protected function initConnection( $lockDb, IDatabase
$db ) {
24 # Let this transaction see lock rows from other transactions
25 $db->query( "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;" );
29 * Get a connection to a lock DB and acquire locks on $paths.
30 * This does not use GET_LOCK() per http://bugs.mysql.com/bug.php?id=1118.
32 * @see DBLockManager::getLocksOnServer()
33 * @param string $lockSrv
38 protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
39 $status = Status
::newGood();
41 $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
43 $keys = []; // list of hash keys for the paths
44 $data = []; // list of rows to insert
45 $checkEXKeys = []; // list of hash keys that this has no EX lock on
46 # Build up values for INSERT clause
47 foreach ( $paths as $path ) {
48 $key = $this->sha1Base36Absolute( $path );
50 $data[] = [ 'fls_key' => $key, 'fls_session' => $this->session
];
51 if ( !isset( $this->locksHeld
[$path][self
::LOCK_EX
] ) ) {
52 $checkEXKeys[] = $key;
56 # Block new writers (both EX and SH locks leave entries here)...
57 $db->insert( 'filelocks_shared', $data, __METHOD__
, [ 'IGNORE' ] );
58 # Actually do the locking queries...
59 if ( $type == self
::LOCK_SH
) { // reader locks
61 # Bail if there are any existing writers...
62 if ( count( $checkEXKeys ) ) {
63 $blocked = $db->selectField( 'filelocks_exclusive', '1',
64 [ 'fle_key' => $checkEXKeys ],
68 # Other prospective writers that haven't yet updated filelocks_exclusive
69 # will recheck filelocks_shared after doing so and bail due to this entry.
70 } else { // writer locks
71 $encSession = $db->addQuotes( $this->session
);
72 # Bail if there are any existing writers...
73 # This may detect readers, but the safe check for them is below.
74 # Note: if two writers come at the same time, both bail :)
75 $blocked = $db->selectField( 'filelocks_shared', '1',
76 [ 'fls_key' => $keys, "fls_session != $encSession" ],
80 # Build up values for INSERT clause
82 foreach ( $keys as $key ) {
83 $data[] = [ 'fle_key' => $key ];
85 # Block new readers/writers...
86 $db->insert( 'filelocks_exclusive', $data, __METHOD__
);
87 # Bail if there are any existing readers...
88 $blocked = $db->selectField( 'filelocks_shared', '1',
89 [ 'fls_key' => $keys, "fls_session != $encSession" ],
96 foreach ( $paths as $path ) {
97 $status->fatal( 'lockmanager-fail-acquirelock', $path );
105 * @see QuorumLockManager::releaseAllLocks()
108 protected function releaseAllLocks() {
109 $status = Status
::newGood();
111 foreach ( $this->conns
as $lockDb => $db ) {
112 if ( $db->trxLevel() ) { // in transaction
114 $db->rollback( __METHOD__
); // finish transaction and kill any rows
115 } catch ( DBError
$e ) {
116 $status->fatal( 'lockmanager-fail-db-release', $lockDb );