Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
84.62% |
44 / 52 |
|
50.00% |
2 / 4 |
CRAP | |
0.00% |
0 / 1 |
ClearUserWatchlistJob | |
86.27% |
44 / 51 |
|
50.00% |
2 / 4 |
10.26 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
newForUser | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
run | |
86.36% |
38 / 44 |
|
0.00% |
0 / 1 |
7.12 | |||
getDeduplicationInfo | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Watchlist; |
4 | |
5 | use GenericParameterJob; |
6 | use Job; |
7 | use MediaWiki\MainConfigNames; |
8 | use MediaWiki\MediaWikiServices; |
9 | use MediaWiki\User\UserIdentity; |
10 | |
11 | /** |
12 | * Job to clear a users watchlist in batches. |
13 | * |
14 | * @since 1.31 |
15 | * @ingroup JobQueue |
16 | * @author Addshore |
17 | */ |
18 | class ClearUserWatchlistJob extends Job implements GenericParameterJob { |
19 | /** |
20 | * @param array $params |
21 | * - userId, The ID for the user whose watchlist is being cleared. |
22 | * - maxWatchlistId, The maximum wl_id at the time the job was first created, |
23 | */ |
24 | public function __construct( array $params ) { |
25 | parent::__construct( 'clearUserWatchlist', $params ); |
26 | |
27 | $this->removeDuplicates = true; |
28 | } |
29 | |
30 | /** |
31 | * @param UserIdentity $user User to clear the watchlist for. |
32 | * @param int $maxWatchlistId The maximum wl_id at the time the job was first created. |
33 | * |
34 | * @return ClearUserWatchlistJob |
35 | */ |
36 | public static function newForUser( UserIdentity $user, $maxWatchlistId ) { |
37 | return new self( [ 'userId' => $user->getId(), 'maxWatchlistId' => $maxWatchlistId ] ); |
38 | } |
39 | |
40 | public function run() { |
41 | $updateRowsPerQuery = MediaWikiServices::getInstance()->getMainConfig()->get( |
42 | MainConfigNames::UpdateRowsPerQuery ); |
43 | $userId = $this->params['userId']; |
44 | $maxWatchlistId = $this->params['maxWatchlistId']; |
45 | $batchSize = $updateRowsPerQuery; |
46 | |
47 | $loadBalancer = MediaWikiServices::getInstance()->getDBLoadBalancer(); |
48 | $dbw = $loadBalancer->getConnection( DB_PRIMARY ); |
49 | $dbr = $loadBalancer->getConnection( DB_REPLICA ); |
50 | |
51 | // Wait before lock to try to reduce time waiting in the lock. |
52 | if ( !$loadBalancer->waitForPrimaryPos( $dbr ) ) { |
53 | $this->setLastError( 'Timed out waiting for replica to catch up before lock' ); |
54 | return false; |
55 | } |
56 | |
57 | // Use a named lock so that jobs for this user see each others' changes |
58 | $lockKey = "{{$dbw->getDomainID()}}:ClearUserWatchlist:$userId"; // per-wiki |
59 | $scopedLock = $dbw->getScopedLockAndFlush( $lockKey, __METHOD__, 10 ); |
60 | if ( !$scopedLock ) { |
61 | $this->setLastError( "Could not acquire lock '$lockKey'" ); |
62 | return false; |
63 | } |
64 | |
65 | if ( !$loadBalancer->waitForPrimaryPos( $dbr ) ) { |
66 | $this->setLastError( 'Timed out waiting for replica to catch up within lock' ); |
67 | return false; |
68 | } |
69 | |
70 | // Clear any stale REPEATABLE-READ snapshot |
71 | $dbr->flushSnapshot( __METHOD__ ); |
72 | |
73 | $watchlistIds = $dbr->newSelectQueryBuilder() |
74 | ->select( 'wl_id' ) |
75 | ->from( 'watchlist' ) |
76 | ->where( [ 'wl_user' => $userId ] ) |
77 | ->andWhere( $dbr->expr( 'wl_id', '<=', $maxWatchlistId ) ) |
78 | ->limit( $batchSize ) |
79 | ->caller( __METHOD__ )->fetchFieldValues(); |
80 | if ( count( $watchlistIds ) == 0 ) { |
81 | return true; |
82 | } |
83 | |
84 | $dbw->newDeleteQueryBuilder() |
85 | ->deleteFrom( 'watchlist' ) |
86 | ->where( [ 'wl_id' => $watchlistIds ] ) |
87 | ->caller( __METHOD__ )->execute(); |
88 | if ( MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::WatchlistExpiry ) ) { |
89 | $dbw->newDeleteQueryBuilder() |
90 | ->deleteFrom( 'watchlist_expiry' ) |
91 | ->where( [ 'we_item' => $watchlistIds ] ) |
92 | ->caller( __METHOD__ )->execute(); |
93 | } |
94 | |
95 | // Commit changes and remove lock before inserting next job. |
96 | $lbf = MediaWikiServices::getInstance()->getDBLoadBalancerFactory(); |
97 | $lbf->commitPrimaryChanges( __METHOD__ ); |
98 | unset( $scopedLock ); |
99 | |
100 | if ( count( $watchlistIds ) === (int)$batchSize ) { |
101 | // Until we get less results than the limit, recursively push |
102 | // the same job again. |
103 | MediaWikiServices::getInstance()->getJobQueueGroup()->push( new self( $this->getParams() ) ); |
104 | } |
105 | |
106 | return true; |
107 | } |
108 | |
109 | public function getDeduplicationInfo() { |
110 | $info = parent::getDeduplicationInfo(); |
111 | // This job never has a namespace or title so we can't use it for deduplication |
112 | unset( $info['namespace'] ); |
113 | unset( $info['title'] ); |
114 | return $info; |
115 | } |
116 | |
117 | } |
118 | /** @deprecated class alias since 1.43 */ |
119 | class_alias( ClearUserWatchlistJob::class, 'ClearUserWatchlistJob' ); |