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