ResourceLoader: Fail less hard when JSON serialization of config fails
[lhc/web/wiklou.git] / includes / deferred / UserEditCountUpdate.php
1 <?php
2 /**
3 * User edit count incrementing.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 */
22
23 use Wikimedia\Assert\Assert;
24 use MediaWiki\MediaWikiServices;
25
26 /**
27 * Handles increment the edit count for a given set of users
28 */
29 class UserEditCountUpdate implements DeferrableUpdate, MergeableUpdate {
30 /** @var array[] Map of (user ID => ('increment': int, 'instances': User[])) */
31 private $infoByUser;
32
33 /**
34 * @param User $user
35 * @param int $increment
36 */
37 public function __construct( User $user, $increment ) {
38 if ( !$user->getId() ) {
39 throw new RuntimeException( "Got user ID of zero" );
40 }
41 $this->infoByUser = [
42 $user->getId() => [ 'increment' => $increment, 'instances' => [ $user ] ]
43 ];
44 }
45
46 public function merge( MergeableUpdate $update ) {
47 /** @var UserEditCountUpdate $update */
48 Assert::parameterType( __CLASS__, $update, '$update' );
49
50 foreach ( $update->infoByUser as $userId => $info ) {
51 if ( !isset( $this->infoByUser[$userId] ) ) {
52 $this->infoByUser[$userId] = [ 'increment' => 0, 'instances' => [] ];
53 }
54 // Merge the increment amount
55 $this->infoByUser[$userId]['increment'] += $info['increment'];
56 // Merge the list of User instances to update in doUpdate()
57 foreach ( $info['instances'] as $user ) {
58 if ( !in_array( $user, $this->infoByUser[$userId]['instances'], true ) ) {
59 $this->infoByUser[$userId]['instances'][] = $user;
60 }
61 }
62 }
63 }
64
65 /**
66 * Purges the list of URLs passed to the constructor.
67 */
68 public function doUpdate() {
69 $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
70 $dbw = $lb->getConnection( DB_MASTER );
71
72 ( new AutoCommitUpdate( $dbw, __METHOD__, function () use ( $lb, $dbw ) {
73 foreach ( $this->infoByUser as $userId => $info ) {
74 $dbw->update(
75 'user',
76 [ 'user_editcount=user_editcount+' . (int)$info['increment'] ],
77 [ 'user_id' => $userId, 'user_editcount IS NOT NULL' ],
78 __METHOD__
79 );
80 /** @var User[] $affectedInstances */
81 $affectedInstances = $info['instances'];
82 // Lazy initialization check...
83 if ( $dbw->affectedRows() == 0 ) {
84 // No rows will be "affected" if user_editcount is NULL.
85 // Check if the generic "replica" connection is not the master.
86 $dbr = $lb->getConnection( DB_REPLICA );
87 if ( $dbr !== $dbw ) {
88 // This method runs after the new revisions were committed.
89 // Wait for the replica to catch up so they will all be counted.
90 $dbr->flushSnapshot( __METHOD__ );
91 $lb->safeWaitForMasterPos( $dbr );
92 }
93 $affectedInstances[0]->initEditCountInternal();
94 }
95 $newCount = (int)$dbw->selectField(
96 'user',
97 [ 'user_editcount' ],
98 [ 'user_id' => $userId ],
99 __METHOD__
100 );
101
102 // Update the edit count in the instance caches. This is mostly useful
103 // for maintenance scripts, where deferred updates might run immediately
104 // and user instances might be reused for a long time.
105 foreach ( $affectedInstances as $affectedInstance ) {
106 $affectedInstance->setEditCountInternal( $newCount );
107 }
108 // Clear the edit count in user cache too
109 $affectedInstances[0]->invalidateCache();
110 }
111 } ) )->doUpdate();
112 }
113 }