Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 75 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
PruneUnusedLinkTargetRows | |
0.00% |
0 / 75 |
|
0.00% |
0 / 2 |
90 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 62 |
|
0.00% |
0 / 1 |
72 |
1 | <?php |
2 | |
3 | // @codeCoverageIgnoreStart |
4 | require_once __DIR__ . '/Maintenance.php'; |
5 | // @codeCoverageIgnoreEnd |
6 | |
7 | /** |
8 | * Maintenance script that cleans unused rows in linktarget table |
9 | * |
10 | * @ingroup Maintenance |
11 | * @since 1.39 |
12 | */ |
13 | class PruneUnusedLinkTargetRows extends Maintenance { |
14 | public function __construct() { |
15 | parent::__construct(); |
16 | $this->addDescription( |
17 | 'Clean unused rows in linktarget table' |
18 | ); |
19 | $this->addOption( |
20 | 'sleep', |
21 | 'Sleep time (in seconds) between every batch. Default: 0', |
22 | false, |
23 | true |
24 | ); |
25 | $this->addOption( 'dry', 'Dry run', false ); |
26 | $this->addOption( 'start', 'Start after this lt_id', false, true ); |
27 | $this->setBatchSize( 50 ); |
28 | } |
29 | |
30 | public function execute() { |
31 | $dbw = $this->getPrimaryDB(); |
32 | $dbr = $this->getReplicaDB(); |
33 | $maxLtId = (int)$dbr->newSelectQueryBuilder() |
34 | ->select( 'MAX(lt_id)' ) |
35 | ->from( 'linktarget' ) |
36 | ->caller( __METHOD__ ) |
37 | ->fetchField(); |
38 | // To avoid race condition of newly added linktarget rows |
39 | // being deleted before getting a chance to be used, let's ignore the newest ones. |
40 | $maxLtId = min( [ $maxLtId - 1, (int)( $maxLtId * 0.99 ) ] ); |
41 | |
42 | $ltCounter = (int)$this->getOption( 'start', 0 ); |
43 | |
44 | $this->output( "Deleting unused linktarget rows...\n" ); |
45 | $deleted = 0; |
46 | $linksMigration = $this->getServiceContainer()->getLinksMigration(); |
47 | while ( $ltCounter < $maxLtId ) { |
48 | $batchMaxLtId = min( $ltCounter + $this->getBatchSize(), $maxLtId ) + 1; |
49 | $this->output( "Checking lt_id between $ltCounter and $batchMaxLtId...\n" ); |
50 | $queryBuilder = $dbr->newSelectQueryBuilder() |
51 | ->select( [ 'lt_id' ] ) |
52 | ->from( 'linktarget' ); |
53 | $queryBuilder->where( [ |
54 | $dbr->expr( 'lt_id', '<', $batchMaxLtId ), |
55 | $dbr->expr( 'lt_id', '>', $ltCounter ) |
56 | ] ); |
57 | foreach ( $linksMigration::$mapping as $table => $tableData ) { |
58 | $queryBuilder->leftJoin( $table, null, $tableData['target_id'] . '=lt_id' ); |
59 | $queryBuilder->andWhere( [ |
60 | $tableData['target_id'] => null |
61 | ] ); |
62 | } |
63 | $ltIdsToDelete = $queryBuilder->caller( __METHOD__ )->fetchFieldValues(); |
64 | if ( !$ltIdsToDelete ) { |
65 | $ltCounter += $this->getBatchSize(); |
66 | continue; |
67 | } |
68 | |
69 | // Run against primary as well with a faster query plan, just to be safe. |
70 | // Also having a bit of time in between helps in cases of immediate removal and insertion of use. |
71 | $queryBuilder = $dbr->newSelectQueryBuilder() |
72 | ->select( [ 'lt_id' ] ) |
73 | ->from( 'linktarget' ) |
74 | ->where( [ |
75 | 'lt_id' => $ltIdsToDelete, |
76 | ] ); |
77 | foreach ( $linksMigration::$mapping as $table => $tableData ) { |
78 | $queryBuilder->leftJoin( $table, null, $tableData['target_id'] . '=lt_id' ); |
79 | $queryBuilder->andWhere( [ |
80 | $tableData['target_id'] => null |
81 | ] ); |
82 | } |
83 | $ltIdsToDelete = $queryBuilder->caller( __METHOD__ )->fetchFieldValues(); |
84 | if ( !$ltIdsToDelete ) { |
85 | $ltCounter += $this->getBatchSize(); |
86 | continue; |
87 | } |
88 | |
89 | if ( !$this->getOption( 'dry' ) ) { |
90 | $dbw->newDeleteQueryBuilder() |
91 | ->deleteFrom( 'linktarget' ) |
92 | ->where( [ 'lt_id' => $ltIdsToDelete ] ) |
93 | ->caller( __METHOD__ )->execute(); |
94 | } |
95 | $deleted += count( $ltIdsToDelete ); |
96 | $ltCounter += $this->getBatchSize(); |
97 | |
98 | // Sleep between batches for replication to catch up |
99 | $this->waitForReplication(); |
100 | $sleep = (int)$this->getOption( 'sleep', 0 ); |
101 | if ( $sleep > 0 ) { |
102 | sleep( $sleep ); |
103 | } |
104 | } |
105 | |
106 | $this->output( |
107 | "Completed clean up linktarget table, " |
108 | . "$deleted rows deleted.\n" |
109 | ); |
110 | |
111 | return true; |
112 | } |
113 | |
114 | } |
115 | |
116 | // @codeCoverageIgnoreStart |
117 | $maintClass = PruneUnusedLinkTargetRows::class; |
118 | require_once RUN_MAINTENANCE_IF_MAIN; |
119 | // @codeCoverageIgnoreEnd |