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