Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 123 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
PruneFRIncludeData | |
0.00% |
0 / 117 |
|
0.00% |
0 / 3 |
342 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
pruneFlaggedRevs | |
0.00% |
0 / 91 |
|
0.00% |
0 / 1 |
272 |
1 | <?php |
2 | /** |
3 | * @ingroup Maintenance |
4 | */ |
5 | |
6 | if ( getenv( 'MW_INSTALL_PATH' ) ) { |
7 | $IP = getenv( 'MW_INSTALL_PATH' ); |
8 | } else { |
9 | $IP = __DIR__ . '/../../..'; |
10 | } |
11 | |
12 | require_once "$IP/maintenance/Maintenance.php"; |
13 | |
14 | class PruneFRIncludeData extends Maintenance { |
15 | |
16 | public function __construct() { |
17 | parent::__construct(); |
18 | $this->addDescription( "This script clears template data for reviewed versions" . |
19 | "that are 1+ hour old and have 50+ newer versions in page. By default," . |
20 | "it will just output how many rows can be deleted. Use the 'prune' option " . |
21 | "to actually delete them." |
22 | ); |
23 | $this->addOption( 'prune', 'Actually do a live run' ); |
24 | $this->addOption( 'start', 'The ID of the starting rev', false, true ); |
25 | $this->addOption( 'sleep', 'Extra sleep time between each batch', false, true ); |
26 | $this->setBatchSize( 500 ); |
27 | $this->addOption( |
28 | 'rev-age', |
29 | 'Revisions older than this age will be deleted in seconds (default: 3600)', |
30 | false, |
31 | true |
32 | ); |
33 | $this->addOption( |
34 | 'rev-num', |
35 | 'Revisions must have at least this number of reviewed revisions on top (default: 50)', |
36 | false, |
37 | true |
38 | ); |
39 | $this->requireExtension( 'FlaggedRevs' ); |
40 | } |
41 | |
42 | /** |
43 | * @inheritDoc |
44 | */ |
45 | public function execute() { |
46 | $start = $this->getOption( 'start' ); |
47 | $prune = $this->getOption( 'prune' ); |
48 | $this->pruneFlaggedRevs( $start, $prune ); |
49 | } |
50 | |
51 | /** |
52 | * @param int|null $start Revision ID |
53 | * @param bool $prune |
54 | */ |
55 | private function pruneFlaggedRevs( $start = null, $prune = false ) { |
56 | if ( $prune ) { |
57 | $this->output( "Pruning old flagged revision inclusion data...\n" ); |
58 | } else { |
59 | $this->output( "Running dry-run of old flagged revision inclusion data pruning...\n" ); |
60 | } |
61 | |
62 | $dbw = wfGetDB( DB_PRIMARY ); |
63 | $dbr = wfGetDB( DB_REPLICA ); |
64 | |
65 | if ( $start === null ) { |
66 | $start = $dbr->selectField( 'flaggedpages', 'MIN(fp_page_id)', false, __METHOD__ ); |
67 | } |
68 | $end = $dbr->selectField( 'flaggedpages', 'MAX(fp_page_id)', false, __METHOD__ ); |
69 | if ( $start === null || $end === null ) { |
70 | $this->output( "...flaggedpages table seems to be empty.\n" ); |
71 | return; |
72 | } |
73 | $end += $this->mBatchSize - 1; # Do remaining chunk |
74 | $blockStart = (int)$start; |
75 | $blockEnd = (int)$start + $this->mBatchSize - 1; |
76 | |
77 | // Tally |
78 | $tDeleted = 0; |
79 | |
80 | $newerRevs = (int)$this->getOption( 'rev-num', 50 ); |
81 | $sleep = (int)$this->getOption( 'sleep', 0 ); |
82 | $cutoff = $dbr->timestamp( time() - (int)$this->getOption( 'rev-age', 3600 ) ); |
83 | |
84 | // First clean up revisions that don't exist anymore. |
85 | while ( true ) { |
86 | if ( !$prune ) { |
87 | break; |
88 | } |
89 | $sres = $dbr->selectFieldValues( |
90 | [ 'flaggedtemplates', 'revision' ], |
91 | 'ft_rev_id', |
92 | [ |
93 | 'rev_id' => null, |
94 | ], |
95 | __METHOD__, |
96 | [ 'LIMIT' => $this->mBatchSize ], |
97 | [ 'revision' => [ 'LEFT JOIN', 'rev_id = ft_rev_id' ] ] |
98 | ); |
99 | |
100 | if ( !$sres ) { |
101 | break; |
102 | } |
103 | $dbw->delete( 'flaggedtemplates', |
104 | [ 'ft_rev_id' => $sres ], |
105 | __METHOD__ |
106 | ); |
107 | $rowsCount = $dbw->affectedRows(); |
108 | $this->output( "...deleted $rowsCount rows\n" ); |
109 | $tDeleted += $rowsCount; |
110 | $this->waitForReplication(); |
111 | sleep( $sleep ); |
112 | } |
113 | |
114 | while ( $blockEnd <= $end ) { |
115 | $this->output( "...doing fp_page_id from $blockStart to $blockEnd\n" ); |
116 | $cond = "fp_page_id BETWEEN $blockStart AND $blockEnd"; |
117 | $res = $dbr->select( 'flaggedpages', 'fp_page_id', $cond, __METHOD__ ); |
118 | // Go through a chunk of flagged pages... |
119 | $revsClearIncludes = []; |
120 | foreach ( $res as $row ) { |
121 | // Get the newest X ($newerRevs) flagged revs for this page |
122 | $sres = $dbr->selectFieldValues( 'flaggedrevs', |
123 | 'fr_rev_id', |
124 | [ 'fr_page_id' => $row->fp_page_id ], |
125 | __METHOD__, |
126 | [ 'ORDER BY' => 'fr_rev_id DESC', 'LIMIT' => $newerRevs ] |
127 | ); |
128 | // See if there are older revs that can be pruned... |
129 | if ( count( $sres ) !== $newerRevs ) { |
130 | continue; |
131 | } |
132 | // Get the oldest of the top X revisions |
133 | $oldestId = (int)$sres[ $newerRevs - 1 ]; |
134 | // Get revs not in the top X that were not reviewed recently |
135 | $sres = $dbr->select( 'flaggedrevs', |
136 | 'fr_rev_id', |
137 | [ |
138 | 'fr_page_id' => $row->fp_page_id, |
139 | 'fr_rev_id < ' . $oldestId, // not in the newest X |
140 | 'fr_timestamp < ' . $dbr->addQuotes( $cutoff ) // not reviewed recently |
141 | ], |
142 | __METHOD__, |
143 | [ 'ORDER BY' => 'fr_rev_id ASC', 'LIMIT' => 5000 ] |
144 | ); |
145 | // Build an array of these rev Ids |
146 | foreach ( $sres as $srow ) { |
147 | $revsClearIncludes[] = $srow->fr_rev_id; |
148 | } |
149 | } |
150 | $blockStart += $this->mBatchSize; |
151 | $blockEnd += $this->mBatchSize; |
152 | if ( !$revsClearIncludes ) { |
153 | continue; |
154 | } elseif ( $prune ) { |
155 | foreach ( array_chunk( $revsClearIncludes, $this->mBatchSize ) as $batch ) { |
156 | $dbw->delete( 'flaggedtemplates', |
157 | [ 'ft_rev_id' => $batch ], |
158 | __METHOD__ |
159 | ); |
160 | $tDeleted += $dbw->affectedRows(); |
161 | $this->waitForReplication(); |
162 | sleep( $sleep ); |
163 | } |
164 | } else { |
165 | // Dry run: say how many includes rows would have been cleared |
166 | $tDeleted += $dbr->selectField( 'flaggedtemplates', |
167 | 'COUNT(*)', |
168 | [ 'ft_rev_id' => $revsClearIncludes ], |
169 | __METHOD__ |
170 | ); |
171 | } |
172 | } |
173 | if ( $prune ) { |
174 | $this->output( "Flagged revision inclusion prunning complete ...\n" ); |
175 | } else { |
176 | $this->output( "Flagged revision inclusion prune test complete ...\n" ); |
177 | } |
178 | $this->output( "Rows: \tflaggedtemplates:$tDeleted\n" ); |
179 | } |
180 | } |
181 | |
182 | $maintClass = PruneFRIncludeData::class; |
183 | require_once RUN_MAINTENANCE_IF_MAIN; |