Addshore has uploaded a new change for review.
https://gerrit.wikimedia.org/r/277350
Change subject: Introduce ClearUserWatchlistJob
......................................................................
Introduce ClearUserWatchlistJob
Change-Id: Icea573a10078ea3f09dc2e4e9fdc737bf639935d
---
M autoload.php
M includes/DefaultSettings.php
M includes/WatchedItemStore.php
A includes/jobqueue/jobs/ClearUserWatchlistJob.php
A tests/phpunit/includes/jobqueue/jobs/ClearUserWatchlistJobTest.php
5 files changed, 189 insertions(+), 0 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core
refs/changes/50/277350/1
diff --git a/autoload.php b/autoload.php
index e74df0a..183ed08 100644
--- a/autoload.php
+++ b/autoload.php
@@ -236,6 +236,7 @@
'CleanupPreferences' => __DIR__ . '/maintenance/cleanupPreferences.php',
'CleanupRemovedModules' => __DIR__ .
'/maintenance/cleanupRemovedModules.php',
'CleanupSpam' => __DIR__ . '/maintenance/cleanupSpam.php',
+ 'ClearUserWatchlistJob' => __DIR__ .
'/includes/jobqueue/jobs/ClearUserWatchlistJob.php',
'ClearInterwikiCache' => __DIR__ .
'/maintenance/clearInterwikiCache.php',
'CliInstaller' => __DIR__ . '/includes/installer/CliInstaller.php',
'CloneDatabase' => __DIR__ . '/includes/db/CloneDatabase.php',
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index c04602c..1071fbc 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -6930,6 +6930,7 @@
'refreshLinksDynamic' => 'RefreshLinksJob',
'activityUpdateJob' => 'ActivityUpdateJob',
'categoryMembershipChange' => 'CategoryMembershipChangeJob',
+ 'clearUserWatchlist' => 'ClearUserWatchlistJob',
'cdnPurge' => 'CdnPurgeJob',
'enqueue' => 'EnqueueJob', // local queue for multi-DC setups
'null' => 'NullJob'
diff --git a/includes/WatchedItemStore.php b/includes/WatchedItemStore.php
index 806db5e..0bbc8e1 100644
--- a/includes/WatchedItemStore.php
+++ b/includes/WatchedItemStore.php
@@ -185,6 +185,15 @@
}
/**
+ * Queues a job that will clear the users watchlist using the Job Queue
+ *
+ * @param User $user
+ */
+ public function clearUserWatchedItemsUsingJobQueue( User $user ) {
+ JobQueueGroup::singleton()->push(
ClearUserWatchlistJob::newForUser( $user ) );
+ }
+
+ /**
* Count the number of individual items that are watched by the user.
* If a subject and corresponding talk page are watched this will
return 2.
*
diff --git a/includes/jobqueue/jobs/ClearUserWatchlistJob.php
b/includes/jobqueue/jobs/ClearUserWatchlistJob.php
new file mode 100644
index 0000000..39810a5
--- /dev/null
+++ b/includes/jobqueue/jobs/ClearUserWatchlistJob.php
@@ -0,0 +1,117 @@
+<?php
+
+/**
+ * Job to clear a users watchlist in batches.
+ *
+ * @author Addshore
+ *
+ * @ingroup JobQueue
+ * @since 1.27
+ */
+class ClearUserWatchlistJob extends Job {
+
+ /**
+ * @var LoadBalancer
+ */
+ private $loadBalancer;
+
+ public static function newForUser( User $user ) {
+ return new self( null, [ 'userId' => $user->getId() ] );
+ }
+
+ public function __construct( Title $title = null, array $params ) {
+ if ( !array_key_exists( 'batchSize', $params ) ) {
+ $params['batchSize'] = 1000;
+ }
+
+ parent::__construct(
+ 'clearUserWatchlist',
+ SpecialPage::getTitleFor( 'EditWatchlist', 'clear' ),
+ $params
+ );
+
+ $this->removeDuplicates = true;
+ $this->loadBalancer = wfGetLB();
+ }
+
+ public function run() {
+ $userId = $this->params['userId'];
+
+ $dbw = $this->loadBalancer->getConnection( DB_WRITE, [
'watchlist' ] );
+
+ // Use a named lock so that jobs for this page see each others'
changes
+ $lockKey = "ClearUserWatchlistJob:$userId";
+ $scopedLock = $dbw->getScopedLockAndFlush( $lockKey,
__METHOD__, 10 );
+ if ( !$scopedLock ) {
+ $this->setLastError( "Could not acquire lock
'$lockKey'" );
+ return false;
+ }
+
+ $dbr = $this->loadBalancer->getConnection( DB_SLAVE, [
'watchlist' ] );
+
+ if ( !$this->loadBalancer->safeWaitForMasterPos( $dbr ) ) {
+ $this->setLastError( "Timed out while waiting for slave
to catch up" );
+ return false;
+ }
+
+ $result = $dbr->select(
+ 'watchlist',
+ [ 'wl_namespace', 'wl_title' ],
+ [ 'wl_user' => $userId, ],
+ __METHOD__,
+ [
+ 'ORDER BY' => 'wl_namespace, wl_title ASC',
+ 'LIMIT' => $this->params['batchSize'],
+ ]
+ );
+
+ $this->loadBalancer->reuseConnection( $dbr );
+
+ if ( $result === false ) {
+ $this->setLastError( "Failed to select watchlist
entries" );
+ return false;
+ }
+
+ if ( $result->numRows() == 0 ) {
+ return true;
+ }
+
+ $dbw = $this->loadBalancer->getConnection( DB_MASTER, [
'watchlist' ] );
+
+ $deleteConds = [];
+ foreach ( $result as $row ) {
+ $deleteConds[] = $dbw->makeList(
+ [
+ 'wl_namespace' => $row->wl_namespace,
+ 'wl_title' => $row->wl_title,
+ 'wl_user' => $userId,
+ ],
+ LIST_AND
+ );
+ }
+
+ $result = $dbw->delete(
+ 'watchlist',
+ $dbw->makeList( $deleteConds, LIST_OR ),
+ __METHOD__
+ );
+
+ $this->loadBalancer->reuseConnection( $dbw );
+
+ if ( $result === false ) {
+ $this->setLastError( "Failed to delete watchlist
entries" );
+ return false;
+ }
+
+ JobQueueGroup::singleton()->push( new self( $this->getTitle(),
$this->getParams() ) );
+
+ return true;
+ }
+
+ public function getDeduplicationInfo() {
+ return [
+ 'type' => $this->getType(),
+ 'userId' => $this->params['userId'],
+ ];
+ }
+}
diff --git a/tests/phpunit/includes/jobqueue/jobs/ClearUserWatchlistJobTest.php
b/tests/phpunit/includes/jobqueue/jobs/ClearUserWatchlistJobTest.php
new file mode 100644
index 0000000..d332a23
--- /dev/null
+++ b/tests/phpunit/includes/jobqueue/jobs/ClearUserWatchlistJobTest.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @covers ClearUserWatchlistJob
+ *
+ * @group JobQueue
+ * @group Database
+ *
+ * @licence GNU GPL v2+
+ * @author Addshore
+ */
+class ClearUserWatchlistJobTest extends MediaWikiTestCase {
+
+ public function setUp() {
+ parent::setUp();
+ self::$users['ClearUserWatchlistJobTestUser']
+ = new TestUser( 'ClearUserWatchlistJobTestUser' );
+ JobQueueGroup::destroySingletons();
+ }
+
+ private function getUser() {
+ return self::$users['ClearUserWatchlistJobTestUser']->getUser();
+ }
+
+ private function runJobs( $jobLimit = 9999 ) {
+ $runJobs = new RunJobs;
+ $runJobs->loadParamsAndArgs( null, [ 'quiet' => true, 'maxjobs'
=> $jobLimit ] );
+ $runJobs->execute();
+ }
+
+ public function testRun() {
+ $user = $this->getUser();
+ $watchedItemStore = WatchedItemStore::getDefaultInstance();
+ $this->runJobs(); // Ensure the queue is empty
+
+ $watchedItemStore->addWatch( $user, new TitleValue( 0, 'A' ) );
+ $watchedItemStore->addWatch( $user, new TitleValue( 1, 'A' ) );
+ $watchedItemStore->addWatch( $user, new TitleValue( 0, 'B' ) );
+ $watchedItemStore->addWatch( $user, new TitleValue( 1, 'B' ) );
+
+ JobQueueGroup::singleton()->push(
+ new ClearUserWatchlistJob(
+ null,
+ [ 'userId' => $user->getId(), 'batchSize' => 2 ]
+ )
+ );
+
+ $this->assertEquals( 1,
JobQueueGroup::singleton()->getQueueSizes()['clearUserWatchlist'] );
+ $this->assertEquals( 4, $watchedItemStore->countWatchedItems(
$user ) );
+ $this->runJobs( 1 );
+ $this->assertEquals( 1,
JobQueueGroup::singleton()->getQueueSizes()['clearUserWatchlist'] );
+ $this->assertEquals( 2, $watchedItemStore->countWatchedItems(
$user ) );
+ $this->runJobs( 1 );
+ $this->assertEquals( 1,
JobQueueGroup::singleton()->getQueueSizes()['clearUserWatchlist'] );
+ $this->assertEquals( 0, $watchedItemStore->countWatchedItems(
$user ) );
+ $this->runJobs( 1 );
+ $this->assertEquals( 0,
JobQueueGroup::singleton()->getQueueSizes()['clearUserWatchlist'] );
+
+ }
+
+}
\ No newline at end of file
--
To view, visit https://gerrit.wikimedia.org/r/277350
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Icea573a10078ea3f09dc2e4e9fdc737bf639935d
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Addshore <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits