MediaWiki master
pruneUnusedLinkTargetRows.php
Go to the documentation of this file.
1<?php
2
3require_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->getPrimaryDB();
30 $dbr = $this->getReplicaDB();
31 $maxLtId = (int)$dbr->newSelectQueryBuilder()
32 ->select( 'MAX(lt_id)' )
33 ->from( 'linktarget' )
34 ->caller( __METHOD__ )
35 ->fetchField();
36 // To avoid race condition of newly added linktarget rows
37 // being deleted before getting a chance to be used, let's ignore the newest ones.
38 $maxLtId = min( [ $maxLtId - 1, (int)( $maxLtId * 0.99 ) ] );
39
40 $ltCounter = (int)$this->getOption( 'start', 0 );
41
42 $this->output( "Deleting unused linktarget rows...\n" );
43 $deleted = 0;
44 $linksMigration = $this->getServiceContainer()->getLinksMigration();
45 while ( $ltCounter < $maxLtId ) {
46 $batchMaxLtId = min( $ltCounter + $this->getBatchSize(), $maxLtId ) + 1;
47 $this->output( "Checking lt_id between $ltCounter and $batchMaxLtId...\n" );
48 $queryBuilder = $dbr->newSelectQueryBuilder()
49 ->select( [ 'lt_id' ] )
50 ->from( 'linktarget' );
51 $queryBuilder->where( [
52 $dbr->expr( 'lt_id', '<', $batchMaxLtId ),
53 $dbr->expr( 'lt_id', '>', $ltCounter )
54 ] );
55 foreach ( $linksMigration::$mapping as $table => $tableData ) {
56 $queryBuilder->leftJoin( $table, null, $tableData['target_id'] . '=lt_id' );
57 $queryBuilder->andWhere( [
58 $tableData['target_id'] => null
59 ] );
60 }
61 $ltIdsToDelete = $queryBuilder->caller( __METHOD__ )->fetchFieldValues();
62 if ( !$ltIdsToDelete ) {
63 $ltCounter += $this->getBatchSize();
64 continue;
65 }
66
67 // Run against primary as well with a faster query plan, just to be safe.
68 // Also having a bit of time in between helps in cases of immediate removal and insertion of use.
69 $queryBuilder = $dbr->newSelectQueryBuilder()
70 ->select( [ 'lt_id' ] )
71 ->from( 'linktarget' )
72 ->where( [
73 'lt_id' => $ltIdsToDelete,
74 ] );
75 foreach ( $linksMigration::$mapping as $table => $tableData ) {
76 $queryBuilder->leftJoin( $table, null, $tableData['target_id'] . '=lt_id' );
77 $queryBuilder->andWhere( [
78 $tableData['target_id'] => null
79 ] );
80 }
81 $ltIdsToDelete = $queryBuilder->caller( __METHOD__ )->fetchFieldValues();
82 if ( !$ltIdsToDelete ) {
83 $ltCounter += $this->getBatchSize();
84 continue;
85 }
86
87 if ( !$this->getOption( 'dry' ) ) {
88 $dbw->newDeleteQueryBuilder()
89 ->deleteFrom( 'linktarget' )
90 ->where( [ 'lt_id' => $ltIdsToDelete ] )
91 ->caller( __METHOD__ )->execute();
92 }
93 $deleted += count( $ltIdsToDelete );
94 $ltCounter += $this->getBatchSize();
95
96 // Sleep between batches for replication to catch up
97 $this->waitForReplication();
98 $sleep = (int)$this->getOption( 'sleep', 0 );
99 if ( $sleep > 0 ) {
100 sleep( $sleep );
101 }
102 }
103
104 $this->output(
105 "Completed clean up linktarget table, "
106 . "$deleted rows deleted.\n"
107 );
108
109 return true;
110 }
111
112}
113
114$maintClass = PruneUnusedLinkTargetRows::class;
115require_once RUN_MAINTENANCE_IF_MAIN;
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
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.