Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
92.68% |
76 / 82 |
|
80.00% |
4 / 5 |
CRAP | |
0.00% |
0 / 1 |
PurgeScoreCache | |
98.70% |
76 / 77 |
|
80.00% |
4 / 5 |
15 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
6 | |||
purge | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
3 | |||
purgeOld | |
94.74% |
18 / 19 |
|
0.00% |
0 / 1 |
3.00 | |||
deleteRows | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace ORES\Maintenance; |
4 | |
5 | use Maintenance; |
6 | use ORES\Services\ORESServices; |
7 | use Wikimedia\Rdbms\Database; |
8 | |
9 | require_once getenv( 'MW_INSTALL_PATH' ) !== false |
10 | ? getenv( 'MW_INSTALL_PATH' ) . '/maintenance/Maintenance.php' |
11 | : __DIR__ . '/../../../maintenance/Maintenance.php'; |
12 | |
13 | /** |
14 | * @ingroup Maintenance |
15 | */ |
16 | class PurgeScoreCache extends Maintenance { |
17 | |
18 | public function __construct() { |
19 | parent::__construct(); |
20 | |
21 | $this->requireExtension( 'ORES' ); |
22 | $this->addDescription( 'Purge out of date (or all) ORES model results' ); |
23 | |
24 | $this->addOption( 'model', 'Model name (optional)', false, true ); |
25 | $this->addOption( 'all', 'Flag to indicate that we want to clear all data, ' . |
26 | 'even those from the most recent model', false, false ); |
27 | $this->addOption( 'old', 'Flag to indicate that we only want to clear old data ' . |
28 | 'that is not in recent changes anymore.', false, false ); |
29 | $this->setBatchSize( 1000 ); |
30 | } |
31 | |
32 | public function execute() { |
33 | if ( $this->hasOption( 'model' ) ) { |
34 | $models = [ $this->getOption( 'model' ) ]; |
35 | } else { |
36 | $models = array_keys( ORESServices::getModelLookup()->getModels() ); |
37 | } |
38 | |
39 | $batchSize = $this->getBatchSize(); |
40 | $this->output( "Purging ORES scores:\n" ); |
41 | foreach ( $models as $model ) { |
42 | if ( $this->hasOption( 'old' ) ) { |
43 | $deletedRows = $this->purgeOld( $model, $batchSize ); |
44 | $description = 'old rows'; |
45 | } elseif ( $this->hasOption( 'all' ) ) { |
46 | $deletedRows = $this->purge( $model, true, $batchSize ); |
47 | $description = 'scores from all model versions'; |
48 | } else { |
49 | $deletedRows = $this->purge( $model, false, $batchSize ); |
50 | $description = 'scores from old model versions'; |
51 | } |
52 | if ( $deletedRows ) { |
53 | $this->output( " ...purging $description from '$model' model': deleted $deletedRows rows\n" ); |
54 | } else { |
55 | $this->output( " ...skipping '$model' model, no action needed\n" ); |
56 | } |
57 | } |
58 | $this->output( " done.\n" ); |
59 | } |
60 | |
61 | /** |
62 | * Delete cached scores |
63 | * |
64 | * Normally, we'll only delete scores from out-of-date model versions. |
65 | * |
66 | * @param string $model Model name. |
67 | * @param bool $isEverything When true, delete scores with the up-to-date |
68 | * model version as well. This can be used in cases where the old data is |
69 | * bad, but no new model has been released yet. |
70 | * @param int $batchSize Maximum number of records to delete per loop. |
71 | * Note that this function runs multiple batches, until all records are deleted. |
72 | * @return int The number of deleted rows |
73 | */ |
74 | private function purge( $model, $isEverything, $batchSize = 1000 ) { |
75 | $conditions = [ |
76 | 'oresm_name' => [ $model, null ], |
77 | ]; |
78 | if ( !$isEverything ) { |
79 | $conditions[] = '(oresm_is_current != 1 OR oresm_is_current IS NULL)'; |
80 | } |
81 | |
82 | $modelIds = $this->getReplicaDB()->selectFieldValues( 'ores_model', |
83 | 'oresm_id', |
84 | $conditions, |
85 | __METHOD__ |
86 | ); |
87 | if ( !$modelIds ) { |
88 | return 0; |
89 | } |
90 | |
91 | return $this->deleteRows( [ 'oresc_model' => $modelIds ], $batchSize ); |
92 | } |
93 | |
94 | /** |
95 | * Delete old cached scores. |
96 | * A score is old of the corresponding revision is not in the recentchanges table. |
97 | * @param string $model Model name. |
98 | * @param int $batchSize Maximum number of records to delete per loop. |
99 | * Note that this function runs multiple batches, until all records are deleted. |
100 | * @return int The number of deleted rows |
101 | */ |
102 | public function purgeOld( $model, $batchSize = 1000 ) { |
103 | $dbr = $this->getReplicaDB(); |
104 | $modelIds = $dbr->selectFieldValues( 'ores_model', |
105 | 'oresm_id', |
106 | [ 'oresm_name' => [ $model, null ] ], |
107 | __METHOD__ |
108 | ); |
109 | |
110 | $lowestRCRev = $dbr->selectFieldValues( 'recentchanges', |
111 | 'rc_this_oldid', |
112 | [], |
113 | __METHOD__, |
114 | [ 'LIMIT' => 1, 'ORDER BY' => 'rc_id' ] |
115 | ); |
116 | |
117 | if ( !$lowestRCRev || !$modelIds ) { |
118 | return 0; |
119 | } |
120 | |
121 | $conditions = [ |
122 | $dbr->expr( 'oresc_rev', '<', $lowestRCRev[0] ), |
123 | 'oresc_model' => $modelIds |
124 | ]; |
125 | return $this->deleteRows( $conditions, $batchSize ); |
126 | } |
127 | |
128 | /** |
129 | * Delete cached scores. Which rows to delete is given by Database::select parameters. |
130 | * @param array $conditions |
131 | * @param int $batchSize Maximum number of records to delete per loop. |
132 | * Note that this function runs multiple batches, until all records are deleted. |
133 | * @return int The number of deleted rows |
134 | * @see Database::select |
135 | */ |
136 | private function deleteRows( array $conditions, $batchSize ) { |
137 | $dbr = $this->getReplicaDB(); |
138 | $dbw = $this->getPrimaryDB(); |
139 | |
140 | $deletedRows = 0; |
141 | |
142 | do { |
143 | $ids = $dbr->selectFieldValues( 'ores_classification', |
144 | 'oresc_id', |
145 | $conditions, |
146 | __METHOD__, |
147 | [ 'LIMIT' => $batchSize ] |
148 | ); |
149 | if ( $ids ) { |
150 | $dbw->delete( 'ores_classification', |
151 | [ 'oresc_id' => $ids ], |
152 | __METHOD__ |
153 | ); |
154 | $deletedRows += $dbw->affectedRows(); |
155 | $this->waitForReplication(); |
156 | } |
157 | } while ( $ids ); |
158 | |
159 | return $deletedRows; |
160 | } |
161 | |
162 | } |
163 | |
164 | $maintClass = PurgeScoreCache::class; |
165 | require_once RUN_MAINTENANCE_IF_MAIN; |