*/
use Wikimedia\Rdbms\IDatabase;
+use MediaWiki\MediaWikiServices;
/**
* Represents a "user group membership" -- a specific instance of a user belonging
}
// Purge old, expired memberships from the DB
- self::purgeExpired( $dbw );
+ JobQueueGroup::singleton()->push( new UserGroupExpiryJob() );
// Check that the values make sense
if ( $this->group === null ) {
/**
* Purge expired memberships from the user_groups table
*
- * @param IDatabase|null $dbw
+ * @return int|bool false if purging wasn't attempted (e.g. because of
+ * readonly), the number of rows purged (might be 0) otherwise
*/
- public static function purgeExpired( IDatabase $dbw = null ) {
- if ( wfReadOnly() ) {
- return;
+ public static function purgeExpired() {
+ $services = MediaWikiServices::getInstance();
+ if ( $services->getReadOnlyMode()->isReadOnly() ) {
+ return false;
}
- if ( $dbw === null ) {
- $dbw = wfGetDB( DB_MASTER );
- }
+ $lbFactory = $services->getDBLoadBalancerFactory();
+ $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
+ $dbw = $services->getDBLoadBalancer()->getConnection( DB_MASTER );
- DeferredUpdates::addUpdate( new AtomicSectionUpdate(
- $dbw,
- __METHOD__,
- function ( IDatabase $dbw, $fname ) {
- $expiryCond = [ 'ug_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ];
- $res = $dbw->select( 'user_groups', self::selectFields(), $expiryCond, $fname );
+ $lockKey = $dbw->getDomainID() . ':usergroups-prune'; // specific to this wiki
+ $scopedLock = $dbw->getScopedLockAndFlush( $lockKey, __METHOD__, 0 );
+ if ( !$scopedLock ) {
+ return false; // already running
+ }
- // save an array of users/groups to insert to user_former_groups
- $usersAndGroups = [];
+ $now = time();
+ $purgedRows = 0;
+ do {
+ $dbw->startAtomic( __METHOD__ );
+
+ $res = $dbw->select(
+ 'user_groups',
+ self::selectFields(),
+ [ 'ug_expiry < ' . $dbw->addQuotes( $dbw->timestamp( $now ) ) ],
+ __METHOD__,
+ [ 'FOR UPDATE', 'LIMIT' => 100 ]
+ );
+
+ if ( $res->numRows() > 0 ) {
+ $insertData = []; // array of users/groups to insert to user_former_groups
+ $deleteCond = []; // array for deleting the rows that are to be moved around
foreach ( $res as $row ) {
- $usersAndGroups[] = [ 'ufg_user' => $row->ug_user, 'ufg_group' => $row->ug_group ];
+ $insertData[] = [ 'ufg_user' => $row->ug_user, 'ufg_group' => $row->ug_group ];
+ $deleteCond[] = $dbw->makeList(
+ [ 'ug_user' => $row->ug_user, 'ug_group' => $row->ug_group ],
+ $dbw::LIST_AND
+ );
}
+ // Delete the rows we're about to move
+ $dbw->delete(
+ 'user_groups',
+ $dbw->makeList( $deleteCond, $dbw::LIST_OR ),
+ __METHOD__
+ );
+ // Push the groups to user_former_groups
+ $dbw->insert( 'user_former_groups', $insertData, __METHOD__, [ 'IGNORE' ] );
+ // Count how many rows were purged
+ $purgedRows += $res->numRows();
+ }
- // delete 'em all
- $dbw->delete( 'user_groups', $expiryCond, $fname );
+ $dbw->endAtomic( __METHOD__ );
- // and push the groups to user_former_groups
- $dbw->insert( 'user_former_groups', $usersAndGroups, __METHOD__, [ 'IGNORE' ] );
- }
- ) );
+ $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
+ } while ( $res->numRows() > 0 );
+ return $purgedRows;
}
/**