Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
66.67% covered (warning)
66.67%
26 / 39
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
DatabaseLockManager
66.67% covered (warning)
66.67%
26 / 39
25.00% covered (danger)
25.00%
1 / 4
21.26
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 lockByType
72.22% covered (warning)
72.22%
13 / 18
0.00% covered (danger)
0.00%
0 / 1
6.77
 doUnlockByType
68.75% covered (warning)
68.75%
11 / 16
0.00% covered (danger)
0.00%
0 / 1
7.10
 doLockByType
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace Wikimedia\LockManager;
4
5use InvalidArgumentException;
6use LogicException;
7use StatusValue;
8use Wikimedia\Rdbms\IConnectionProvider;
9
10/**
11 * A simplified Database Lock Manager.
12 */
13class DatabaseLockManager extends LockManager {
14
15    /** @var IConnectionProvider */
16    private IConnectionProvider $dbProvider;
17
18    /**
19     * Construct a new instance from configuration.
20     * * @param array $config
21     */
22    public function __construct( array $config ) {
23        parent::__construct( $config );
24
25        // Inject the connection provider
26        $this->dbProvider = $config['dbProvider'];
27    }
28
29    /**
30     * @inheritDoc
31     */
32    public function lockByType( array $pathsByType, $timeout = 0 ) {
33        $status = StatusValue::newGood();
34        $pathsByType = $this->normalizePathsByType( $pathsByType );
35
36        foreach ( $pathsByType as $type => $keys ) {
37            if ( $type == self::LOCK_UW ) {
38                throw new InvalidArgumentException(
39                    'DatabaseLockManager does not support LOCK_UW'
40                );
41            }
42            if ( $type == self::LOCK_SH ) {
43                // LOCK_SH is no-op
44                continue;
45            }
46            foreach ( $keys as $key ) {
47                $res = $this->dbProvider->getPrimaryDatabase()->lock(
48                    $this->sha1Base36Absolute( $key ),
49                    // It's not really __METHOD__ but maybe better than nothing specially
50                    // given the fact that the locked key is a hash
51                    substr( "{$this->domain}:{$key}", 0, 15 ),
52                    $timeout
53                );
54
55                if ( !$res ) {
56                    // If one lock fails, fail the batch.
57                    $status->fatal( 'lockmanager-fail-acquirelock', $key );
58                }
59            }
60        }
61
62        return $status;
63    }
64
65    /**
66     * @inheritDoc
67     */
68    protected function doUnlockByType( array $pathsByType ) {
69        $status = StatusValue::newGood();
70        foreach ( $pathsByType as $type => $keys ) {
71            if ( $type == self::LOCK_UW ) {
72                throw new InvalidArgumentException(
73                    'DatabaseLockManager does not support LOCK_UW'
74                );
75            }
76            if ( $type == self::LOCK_SH ) {
77                // LOCK_SH is no-op
78                continue;
79            }
80            foreach ( $keys as $key ) {
81                $res = $this->dbProvider->getPrimaryDatabase()->unlock(
82                    $this->sha1Base36Absolute( $key ),
83                    // It's not really __METHOD__ but maybe better than nothing specially
84                    // given the fact that the locked key is a hash
85                    substr( "{$this->domain}:{$key}", 0, 15 )
86                );
87
88                if ( !$res ) {
89                    // If one lock fails, fail the batch.
90                    $status->fatal( 'lockmanager-fail-unlock', $key );
91                }
92            }
93        }
94
95        return $status;
96    }
97
98    /**
99     * Since we are overriding ::lockByType, this should never be reached
100     *
101     * @return never
102     */
103    protected function doLockByType( array $pathsByType ) {
104        throw new LogicException(
105            'DatabaseLockManager does not implement ::doLockByType'
106        );
107    }
108}