diff options
author | xiplus <huangxuanyuxiplus@gmail.com> | 2022-05-26 22:12:41 +0800 |
---|---|---|
committer | Krinkle <krinkle@fastmail.com> | 2024-03-02 17:41:16 +0000 |
commit | f7552491c44e93dbb50c03157bc1cc4d3be231f1 (patch) | |
tree | 5d835e78a58d29362ca3c5ff48b2c29eaf211c21 | |
parent | 42528ea58f37334d2e08e510c43ccae6c87e6df5 (diff) |
RecentChange: Avoid duplicate patrol log entry after patrol conflict
Check the affected rows count to detect a patrol conflict, which is
when the database query succeeds, but it was a no-op because another
request (possibly from the same user, or someone else) marked it as
patrolled already.
We detect it using the `rc_patrolled` condition because without it,
affectedRows() would always return 1, as the previous update condition
could match either the patrolled or patrolled version of the row.
Bug: T196182
Change-Id: I2aa4f2f752a40657883e0a6714dbb175c9d2fdcb
-rw-r--r-- | includes/changes/RecentChange.php | 28 |
1 files changed, 23 insertions, 5 deletions
diff --git a/includes/changes/RecentChange.php b/includes/changes/RecentChange.php index 05d172208778..81a22d0cdcce 100644 --- a/includes/changes/RecentChange.php +++ b/includes/changes/RecentChange.php @@ -631,8 +631,16 @@ class RecentChange implements Taggable { if ( $this->getAttribute( 'rc_patrolled' ) ) { return []; } - // Actually set the 'patrolled' flag in RC - $this->reallyMarkPatrolled(); + // Attempt to set the 'patrolled' flag in RC database + $affectedRowCount = $this->reallyMarkPatrolled(); + + if ( $affectedRowCount === 0 ) { + // Query succeeded but no rows change, e.g. another request + // patrolled the same change just before us. + // Avoid duplicate log entry (T196182). + return []; + } + // Log this patrol event PatrolLog::record( $this, false, $performer->getUser(), $tags ); @@ -644,15 +652,25 @@ class RecentChange implements Taggable { /** * Mark this RecentChange patrolled, without error checking - * @return int Number of affected rows + * + * @return int Number of database rows changed, usually 1, but 0 if + * another request already patrolled it in the mean time. */ public function reallyMarkPatrolled() { $dbw = MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->getPrimaryDatabase(); $dbw->newUpdateQueryBuilder() ->update( 'recentchanges' ) ->set( [ 'rc_patrolled' => self::PRC_PATROLLED ] ) - ->where( [ 'rc_id' => $this->getAttribute( 'rc_id' ) ] ) + ->where( [ + 'rc_id' => $this->getAttribute( 'rc_id' ), + 'rc_patrolled' => self::PRC_UNPATROLLED, + ] ) ->caller( __METHOD__ )->execute(); + $affectedRowCount = $dbw->affectedRows(); + // The change was patrolled already, do nothing + if ( $affectedRowCount === 0 ) { + return 0; + } // Invalidate the page cache after the page has been patrolled // to make sure that the Patrol link isn't visible any longer! $this->getTitle()->invalidateCache(); @@ -665,7 +683,7 @@ class RecentChange implements Taggable { $revertedTagUpdateManager->approveRevertedTagForRevision( $revisionId ); } - return $dbw->affectedRows(); + return $affectedRowCount; } /** |