Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
DeleteExpiredUserImpactData
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 3
30
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
6
 getTimestampFromRelativeDate
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace GrowthExperiments\Maintenance;
4
5use DateTime;
6use Exception;
7use GrowthExperiments\GrowthExperimentsServices;
8use GrowthExperiments\UserImpact\DatabaseUserImpactStore;
9use Maintenance;
10use MediaWiki\MediaWikiServices;
11use Wikimedia\Rdbms\SelectQueryBuilder;
12use Wikimedia\Timestamp\ConvertibleTimestamp;
13
14$IP = getenv( 'MW_INSTALL_PATH' );
15if ( $IP === false ) {
16    $IP = __DIR__ . '/../../..';
17}
18require_once "$IP/maintenance/Maintenance.php";
19
20class DeleteExpiredUserImpactData extends Maintenance {
21
22    public function __construct() {
23        parent::__construct();
24        $this->requireExtension( 'GrowthExperiments' );
25        $this->addDescription( 'Delete unused old data from the growthexperiments_user_impact table.' );
26        $this->addOption( 'expiry', 'A relative timestring fragment passed to DateTime, such as "30days".',
27            false, true );
28        $this->setBatchSize( 100 );
29    }
30
31    /** @inheritDoc */
32    public function execute() {
33        $services = MediaWikiServices::getInstance();
34        $growthServices = GrowthExperimentsServices::wrap( $services );
35        $dbw = $growthServices->getLoadBalancer()->getConnection( DB_PRIMARY );
36
37        $expiry = $this->getOption( 'expiry', '30days' );
38        $expiryTimestamp = $this->getTimestampFromRelativeDate( $expiry );
39
40        $queryBuilder = $dbw->newSelectQueryBuilder()
41            ->select( 'geui_user_id' )
42            ->from( DatabaseUserImpactStore::TABLE_NAME )
43            ->where( $dbw->expr( 'geui_timestamp', '<', $dbw->timestamp( $expiryTimestamp ) ) )
44            ->orderBy( 'geui_timestamp', SelectQueryBuilder::SORT_ASC )
45            ->limit( $this->getBatchSize() )
46            ->caller( __METHOD__ );
47
48        $deletedCount = 0;
49        $idsToDelete = $queryBuilder->fetchFieldValues();
50        while ( $idsToDelete !== [] ) {
51            $dbw->newDeleteQueryBuilder()
52                ->deleteFrom( DatabaseUserImpactStore::TABLE_NAME )
53                ->where( [ 'geui_user_id' => $idsToDelete ] )
54                ->caller( __METHOD__ )
55                ->execute();
56            $deletedCount += count( $idsToDelete );
57            $this->output( '.' );
58            $this->waitForReplication();
59            $idsToDelete = $queryBuilder->fetchFieldValues();
60        }
61        $this->output( "\nDeleted $deletedCount rows\n" );
62    }
63
64    /**
65     * @param string $relativeDate A relative date string fragment that will be prefixed with a
66     *   minus sign and passed to the DateTime constructor
67     * @return string TS_MW formatted timestamp
68     */
69    private function getTimestampFromRelativeDate( string $relativeDate ): string {
70        try {
71            $timestamp = new ConvertibleTimestamp( new DateTime( 'now - ' . $relativeDate ) );
72        } catch ( Exception $e ) {
73            $this->fatalError( $e->getMessage() );
74        }
75        return $timestamp->getTimestamp( TS_MW );
76    }
77
78}
79
80$maintClass = DeleteExpiredUserImpactData::class;
81require_once RUN_MAINTENANCE_IF_MAIN;