MediaWiki  master
ClearUserWatchlistJob.php
Go to the documentation of this file.
1 <?php
2 
5 
14 class ClearUserWatchlistJob extends Job implements GenericParameterJob {
20  public function __construct( array $params ) {
21  parent::__construct( 'clearUserWatchlist', $params );
22 
23  $this->removeDuplicates = true;
24  }
25 
32  public static function newForUser( UserIdentity $user, $maxWatchlistId ) {
33  return new self( [ 'userId' => $user->getId(), 'maxWatchlistId' => $maxWatchlistId ] );
34  }
35 
36  public function run() {
37  global $wgUpdateRowsPerQuery;
38  $userId = $this->params['userId'];
39  $maxWatchlistId = $this->params['maxWatchlistId'];
40  $batchSize = $wgUpdateRowsPerQuery;
41 
42  $loadBalancer = MediaWikiServices::getInstance()->getDBLoadBalancer();
43  $dbw = $loadBalancer->getConnectionRef( DB_MASTER );
44  $dbr = $loadBalancer->getConnectionRef( DB_REPLICA, [ 'watchlist' ] );
45 
46  // Wait before lock to try to reduce time waiting in the lock.
47  if ( !$loadBalancer->waitForMasterPos( $dbr ) ) {
48  $this->setLastError( 'Timed out waiting for replica to catch up before lock' );
49  return false;
50  }
51 
52  // Use a named lock so that jobs for this user see each others' changes
53  $lockKey = "{{$dbw->getDomainID()}}:ClearUserWatchlist:$userId"; // per-wiki
54  $scopedLock = $dbw->getScopedLockAndFlush( $lockKey, __METHOD__, 10 );
55  if ( !$scopedLock ) {
56  $this->setLastError( "Could not acquire lock '$lockKey'" );
57  return false;
58  }
59 
60  if ( !$loadBalancer->waitForMasterPos( $dbr ) ) {
61  $this->setLastError( 'Timed out waiting for replica to catch up within lock' );
62  return false;
63  }
64 
65  // Clear any stale REPEATABLE-READ snapshot
66  $dbr->flushSnapshot( __METHOD__ );
67 
68  $watchlistIds = $dbr->selectFieldValues(
69  'watchlist',
70  'wl_id',
71  [
72  'wl_user' => $userId,
73  'wl_id <= ' . $maxWatchlistId
74  ],
75  __METHOD__,
76  [
77  'ORDER BY' => 'wl_id ASC',
78  'LIMIT' => $batchSize,
79  ]
80  );
81 
82  if ( count( $watchlistIds ) == 0 ) {
83  return true;
84  }
85 
86  $dbw->delete( 'watchlist', [ 'wl_id' => $watchlistIds ], __METHOD__ );
87 
88  // Commit changes and remove lock before inserting next job.
89  $lbf = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
90  $lbf->commitMasterChanges( __METHOD__ );
91  unset( $scopedLock );
92 
93  if ( count( $watchlistIds ) === (int)$batchSize ) {
94  // Until we get less results than the limit, recursively push
95  // the same job again.
96  JobQueueGroup::singleton()->push( new self( $this->getParams() ) );
97  }
98 
99  return true;
100  }
101 
102  public function getDeduplicationInfo() {
103  $info = parent::getDeduplicationInfo();
104  // This job never has a namespace or title so we can't use it for deduplication
105  unset( $info['namespace'] );
106  unset( $info['title'] );
107  return $info;
108  }
109 
110 }
Job to clear a users watchlist in batches.
getParams()
Definition: Job.php:175
Class to both describe a background job and handle jobs.
Definition: Job.php:30
getDeduplicationInfo()
Subclasses may need to override this to make duplication detection work.
const DB_MASTER
Definition: defines.php:26
setLastError( $error)
Definition: Job.php:418
Interface for objects representing user identity.
$wgUpdateRowsPerQuery
Number of rows to update per query.
static singleton( $domain=false)
array $params
Array of job parameters.
Definition: Job.php:35
const DB_REPLICA
Definition: defines.php:25
static newForUser(UserIdentity $user, $maxWatchlistId)
Interface for generic jobs only uses the parameters field and are JSON serializable.