Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
InitEditCount
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 2
72
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2/**
3 * Init the user_editcount database field based on the number of rows in the
4 * revision table.
5 *
6 * @license GPL-2.0-or-later
7 * @file
8 * @ingroup Maintenance
9 */
10
11// @codeCoverageIgnoreStart
12require_once __DIR__ . '/Maintenance.php';
13// @codeCoverageIgnoreEnd
14
15use MediaWiki\Maintenance\Maintenance;
16use MediaWiki\WikiMap\WikiMap;
17use Wikimedia\Rdbms\IDatabase;
18use Wikimedia\Rdbms\RawSQLValue;
19
20class InitEditCount extends Maintenance {
21    public function __construct() {
22        parent::__construct();
23        $this->addOption( 'quick', 'Force the update to be done in a single query' );
24        $this->addOption( 'background', 'Force replication-friendly mode; may be inefficient but avoids'
25        . 'locking tables or lagging replica DBs with large updates; calculates counts on a replica DB'
26        . 'if possible. Background mode will be automatically used if multiple servers are listed in the'
27        . 'load balancer, usually indicating a replication environment.' );
28        $this->addDescription( 'Batch-recalculate user_editcount fields from the revision table' );
29    }
30
31    public function execute() {
32        $dbw = $this->getPrimaryDB();
33
34        // Autodetect mode...
35        if ( $this->hasOption( 'background' ) ) {
36            $backgroundMode = true;
37        } elseif ( $this->hasOption( 'quick' ) ) {
38            $backgroundMode = false;
39        } else {
40            $lb = $this->getServiceContainer()->getDBLoadBalancer();
41            $backgroundMode = $lb->hasReplicaServers();
42        }
43
44        if ( $backgroundMode ) {
45            $this->output( "Using replication-friendly background mode...\n" );
46
47            $dbr = $this->getReplicaDB();
48            $chunkSize = 100;
49            $lastUser = $dbr->newSelectQueryBuilder()
50                ->select( 'MAX(user_id)' )
51                ->from( 'user' )
52                ->caller( __METHOD__ )->fetchField();
53
54            $start = microtime( true );
55            $migrated = 0;
56            for ( $min = 0; $min <= $lastUser; $min += $chunkSize ) {
57                $max = $min + $chunkSize;
58
59                $result = $dbr->newSelectQueryBuilder()
60                    ->select( [ 'user_id', 'user_editcount' => 'COUNT(rev_actor)' ] )
61                    ->from( 'user' )
62                    ->join( 'actor', 'actor_rev_user', 'user_id = actor_rev_user.actor_user' )
63                    ->leftJoin( 'revision', 'rev', 'actor_rev_user.actor_id = rev.rev_actor' )
64                    ->where( $dbr->expr( 'user_id', '>', $min )->and( 'user_id', '<=', $max ) )
65                    ->groupBy( 'user_id' )
66                    ->caller( __METHOD__ )->fetchResultSet();
67
68                foreach ( $result as $row ) {
69                    $dbw->newUpdateQueryBuilder()
70                        ->update( 'user' )
71                        ->set( [ 'user_editcount' => $row->user_editcount ] )
72                        ->where( [ 'user_id' => $row->user_id ] )
73                        ->caller( __METHOD__ )->execute();
74                    ++$migrated;
75                }
76
77                $delta = microtime( true ) - $start;
78                $rate = ( $delta == 0.0 ) ? 0.0 : $migrated / $delta;
79                $this->output( sprintf( "%s %d (%0.1f%%) done in %0.1f secs (%0.3f accounts/sec).\n",
80                    WikiMap::getCurrentWikiDbDomain()->getId(),
81                    $migrated,
82                    min( $max, $lastUser ) / $lastUser * 100.0,
83                    $delta,
84                    $rate ) );
85
86                $this->waitForReplication();
87            }
88        } else {
89            $this->output( "Using single-query mode...\n" );
90
91            $subquery = $dbw->newSelectQueryBuilder()
92                ->select( 'COUNT(*)' )
93                ->from( 'revision' )
94                ->join( 'actor', 'actor_rev_user', 'actor_rev_user.actor_id = rev_actor' )
95                ->where( 'user_id = actor_rev_user.actor_user' )
96                ->caller( __METHOD__ )->getSQL();
97
98            $dbw->newUpdateQueryBuilder()
99                ->table( 'user' )
100                ->set( [ 'user_editcount' => new RawSQLValue( "($subquery)" ) ] )
101                ->where( IDatabase::ALL_ROWS )
102                ->caller( __METHOD__ )
103                ->execute();
104        }
105
106        $this->output( "Done!\n" );
107    }
108}
109
110// @codeCoverageIgnoreStart
111$maintClass = InitEditCount::class;
112require_once RUN_MAINTENANCE_IF_MAIN;
113// @codeCoverageIgnoreEnd