Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
75.38% |
49 / 65 |
|
40.00% |
2 / 5 |
CRAP | |
0.00% |
0 / 1 |
CentralAuthEditCounter | |
75.38% |
49 / 65 |
|
40.00% |
2 / 5 |
18.36 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getCount | |
69.05% |
29 / 42 |
|
0.00% |
0 / 1 |
7.07 | |||
getCountFromWikis | |
60.00% |
3 / 5 |
|
0.00% |
0 / 1 |
3.58 | |||
getCountFromDB | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
increment | |
88.89% |
8 / 9 |
|
0.00% |
0 / 1 |
3.01 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\CentralAuth; |
4 | |
5 | use MediaWiki\Extension\CentralAuth\User\CentralAuthUser; |
6 | use Wikimedia\ObjectCache\WANObjectCache; |
7 | use Wikimedia\Rdbms\IReadableDatabase; |
8 | use Wikimedia\Rdbms\RawSQLValue; |
9 | |
10 | class CentralAuthEditCounter { |
11 | |
12 | private CentralAuthDatabaseManager $databaseManager; |
13 | private WANObjectCache $wanCache; |
14 | |
15 | public function __construct( |
16 | CentralAuthDatabaseManager $databaseManager, |
17 | WANObjectCache $wanCache |
18 | ) { |
19 | $this->databaseManager = $databaseManager; |
20 | $this->wanCache = $wanCache; |
21 | } |
22 | |
23 | /** |
24 | * Get the global edit count for a user |
25 | * |
26 | * @param CentralAuthUser $centralUser |
27 | * @return int |
28 | */ |
29 | public function getCount( CentralAuthUser $centralUser ) { |
30 | $userId = $centralUser->getId(); |
31 | $dbr = $this->databaseManager->getCentralReplicaDB(); |
32 | $count = $this->getCountFromDB( $dbr, $userId ); |
33 | if ( $count !== false ) { |
34 | return $count; |
35 | } |
36 | |
37 | if ( $this->databaseManager->isReadOnly() ) { |
38 | // Don't try DB_PRIMARY since that will throw an exception |
39 | return $this->wanCache->getWithSetCallback( |
40 | $this->wanCache->makeGlobalKey( 'centralauth-editcount', $centralUser->getId() ), |
41 | 5 * $this->wanCache::TTL_MINUTE, |
42 | function () use ( $centralUser ) { |
43 | return $this->getCountFromWikis( $centralUser ); |
44 | } |
45 | ); |
46 | } |
47 | |
48 | $dbw = $this->databaseManager->getCentralPrimaryDB(); |
49 | $count = $this->getCountFromDB( $dbw, $userId ); |
50 | if ( $count !== false ) { |
51 | return $count; |
52 | } |
53 | |
54 | $dbw->startAtomic( __METHOD__ ); |
55 | // Lock the row |
56 | $dbw->newInsertQueryBuilder() |
57 | ->insertInto( 'global_edit_count' ) |
58 | ->ignore() |
59 | ->row( [ |
60 | 'gec_user' => $userId, |
61 | 'gec_count' => 0 |
62 | ] ) |
63 | ->caller( __METHOD__ ) |
64 | ->execute(); |
65 | if ( !$dbw->affectedRows() ) { |
66 | // Try one more time after the lock wait |
67 | $dbw->endAtomic( __METHOD__ ); |
68 | $count = $this->getCountFromDB( $dbw, $userId ); |
69 | if ( $count !== false ) { |
70 | return $count; |
71 | } |
72 | $dbw->startAtomic( __METHOD__ ); |
73 | } |
74 | $count = $this->getCountFromWikis( $centralUser ); |
75 | |
76 | $dbw->newUpdateQueryBuilder() |
77 | ->update( 'global_edit_count' ) |
78 | ->set( [ 'gec_count' => $count ] ) |
79 | ->where( [ 'gec_user' => $userId ] ) |
80 | ->caller( __METHOD__ ) |
81 | ->execute(); |
82 | $dbw->endAtomic( __METHOD__ ); |
83 | return $count; |
84 | } |
85 | |
86 | /** |
87 | * Get the count by adding the user_editcount value across all attached wikis |
88 | * |
89 | * @param CentralAuthUser $centralUser |
90 | * @return int |
91 | */ |
92 | public function getCountFromWikis( CentralAuthUser $centralUser ) { |
93 | $count = 0; |
94 | foreach ( $centralUser->queryAttached() as $acc ) { |
95 | if ( isset( $acc['editCount'] ) ) { |
96 | $count += (int)$acc['editCount']; |
97 | } |
98 | } |
99 | return $count; |
100 | } |
101 | |
102 | /** |
103 | * @param IReadableDatabase $dbr |
104 | * @param int $userId |
105 | * @return int|false |
106 | */ |
107 | private function getCountFromDB( IReadableDatabase $dbr, int $userId ) { |
108 | $count = $dbr->newSelectQueryBuilder() |
109 | ->select( 'gec_count' ) |
110 | ->from( 'global_edit_count' ) |
111 | ->where( [ 'gec_user' => $userId ] ) |
112 | ->caller( __METHOD__ ) |
113 | ->fetchField(); |
114 | return is_string( $count ) ? (int)$count : $count; |
115 | } |
116 | |
117 | /** |
118 | * Increment a global edit count |
119 | * |
120 | * @param CentralAuthUser $centralUser |
121 | * @param int $increment |
122 | */ |
123 | public function increment( CentralAuthUser $centralUser, $increment ) { |
124 | if ( !$increment || $this->databaseManager->isReadOnly() ) { |
125 | return; |
126 | } |
127 | $dbw = $this->databaseManager->getCentralPrimaryDB(); |
128 | $dbw->newUpdateQueryBuilder() |
129 | ->update( 'global_edit_count' ) |
130 | ->set( [ 'gec_count' => new RawSQLValue( 'gec_count + ' . (int)$increment ) ] ) |
131 | ->where( [ 'gec_user' => $centralUser->getId() ] ) |
132 | ->caller( __METHOD__ ) |
133 | ->execute(); |
134 | // No need to populate when affectedRows() = 0, we can just wait for |
135 | // getCount() to be called. |
136 | } |
137 | } |