MediaWiki  master
pruneUnusedLinkTargetRows.php
Go to the documentation of this file.
1 <?php
2 
3 require_once __DIR__ . '/Maintenance.php';
4 
12  public function __construct() {
13  parent::__construct();
14  $this->addDescription(
15  'Clean unused rows in linktarget table'
16  );
17  $this->addOption(
18  'sleep',
19  'Sleep time (in seconds) between every batch. Default: 0',
20  false,
21  true
22  );
23  $this->addOption( 'dry', 'Dry run', false );
24  $this->addOption( 'start', 'Start after this lt_id', false, true );
25  $this->setBatchSize( 50 );
26  }
27 
28  public function execute() {
29  $dbw = $this->getDB( DB_PRIMARY );
30  $dbr = $this->getDB( DB_REPLICA );
31  $maxLtId = (int)$dbr->newSelectQueryBuilder()
32  ->select( 'MAX(lt_id)' )
33  ->from( 'linktarget' )
34  ->fetchField();
35  // To avoid race condition of newly added linktarget rows
36  // being deleted before getting a chance to be used, let's ignore the newest ones.
37  $maxLtId = min( [ $maxLtId - 1, (int)( $maxLtId * 0.99 ) ] );
38 
39  $ltCounter = (int)$this->getOption( 'start', 0 );
40 
41  $this->output( "Deleting unused linktarget rows...\n" );
42  $deleted = 0;
43  $linksMigration = $this->getServiceContainer()->getLinksMigration();
44  while ( $ltCounter < $maxLtId ) {
45  $batchMaxLtId = min( $ltCounter + $this->getBatchSize(), $maxLtId ) + 1;
46  $this->output( "Checking lt_id between $ltCounter and $batchMaxLtId...\n" );
47  $queryBuilder = $dbr->newSelectQueryBuilder()
48  ->select( [ 'lt_id' ] )
49  ->from( 'linktarget' );
50  $queryBuilder->where( [
51  'lt_id < ' . $dbr->addQuotes( $batchMaxLtId ),
52  'lt_id > ' . $dbr->addQuotes( $ltCounter )
53  ] );
54  foreach ( $linksMigration::$mapping as $table => $tableData ) {
55  $queryBuilder->leftJoin( $table, null, $tableData['target_id'] . '=lt_id' );
56  $queryBuilder->andWhere( [
57  $tableData['target_id'] => null
58  ] );
59  }
60  $ltIdsToDelete = $queryBuilder->caller( __METHOD__ )->fetchFieldValues();
61  if ( !$ltIdsToDelete ) {
62  $ltCounter += $this->getBatchSize();
63  continue;
64  }
65 
66  // Run against primary as well with a faster query plan, just to be safe.
67  // Also having a bit of time in between helps in cases of immediate removal and insertion of use.
68  $queryBuilder = $dbr->newSelectQueryBuilder()
69  ->select( [ 'lt_id' ] )
70  ->from( 'linktarget' )
71  ->where( [
72  'lt_id' => $ltIdsToDelete,
73  ] );
74  foreach ( $linksMigration::$mapping as $table => $tableData ) {
75  $queryBuilder->leftJoin( $table, null, $tableData['target_id'] . '=lt_id' );
76  $queryBuilder->andWhere( [
77  $tableData['target_id'] => null
78  ] );
79  }
80  $ltIdsToDelete = $queryBuilder->caller( __METHOD__ )->fetchFieldValues();
81  if ( !$ltIdsToDelete ) {
82  $ltCounter += $this->getBatchSize();
83  continue;
84  }
85 
86  if ( !$this->getOption( 'dry' ) ) {
87  $dbw->delete( 'linktarget', [ 'lt_id' => $ltIdsToDelete ], __METHOD__ );
88  }
89  $deleted += count( $ltIdsToDelete );
90  $ltCounter += $this->getBatchSize();
91 
92  // Sleep between batches for replication to catch up
93  $this->waitForReplication();
94  $sleep = (int)$this->getOption( 'sleep', 0 );
95  if ( $sleep > 0 ) {
96  sleep( $sleep );
97  }
98  }
99 
100  $this->output(
101  "Completed clean up linktarget table, "
102  . "$deleted rows deleted.\n"
103  );
104 
105  return true;
106  }
107 
108 }
109 
110 $maintClass = PruneUnusedLinkTargetRows::class;
111 require_once RUN_MAINTENANCE_IF_MAIN;
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
Definition: Maintenance.php:66
getDB( $db, $groups=[], $dbDomain=false)
Returns a database to be used by current maintenance script.
output( $out, $channel=null)
Throw some output to the user.
waitForReplication()
Wait for replica DBs to catch up.
getServiceContainer()
Returns the main service container.
getBatchSize()
Returns batch size.
addDescription( $text)
Set the description text.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
getOption( $name, $default=null)
Get an option, or return the default.
setBatchSize( $s=0)
Maintenance script that cleans unused rows in linktarget table.
const DB_REPLICA
Definition: defines.php:26
const DB_PRIMARY
Definition: defines.php:28