Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 56 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
RunBatchedQuery | |
0.00% |
0 / 53 |
|
0.00% |
0 / 3 |
56 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 42 |
|
0.00% |
0 / 1 |
30 | |||
getDbType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Run a database query in batches and wait for replica DBs. This is used on large |
4 | * wikis to prevent replication lag from going through the roof when executing |
5 | * large write queries. |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | * |
12 | * This program is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | * GNU General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU General Public License along |
18 | * with this program; if not, write to the Free Software Foundation, Inc., |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
20 | * http://www.gnu.org/copyleft/gpl.html |
21 | * |
22 | * @file |
23 | * @ingroup Maintenance |
24 | */ |
25 | |
26 | require_once __DIR__ . '/Maintenance.php'; |
27 | |
28 | use Wikimedia\Rdbms\Platform\ISQLPlatform; |
29 | |
30 | /** |
31 | * Maintenance script to run a database query in batches and wait for replica DBs. |
32 | * |
33 | * @ingroup Maintenance |
34 | */ |
35 | class RunBatchedQuery extends Maintenance { |
36 | public function __construct() { |
37 | parent::__construct(); |
38 | $this->addDescription( |
39 | "Run an update query on all rows of a table. " . |
40 | "Waits for replicas at appropriate intervals." ); |
41 | $this->addOption( 'table', 'The table name', true, true ); |
42 | $this->addOption( 'set', 'The SET clause', true, true ); |
43 | $this->addOption( 'where', 'The WHERE clause', false, true ); |
44 | $this->addOption( 'key', 'A column name, the values of which are unique', true, true ); |
45 | $this->addOption( 'batch-size', 'The batch size (default 1000)', false, true ); |
46 | $this->addOption( 'db', 'The database name, or omit to use the current wiki.', false, true ); |
47 | } |
48 | |
49 | public function execute() { |
50 | $table = $this->getOption( 'table' ); |
51 | $key = $this->getOption( 'key' ); |
52 | $set = $this->getOption( 'set' ); |
53 | $where = $this->getOption( 'where', null ); |
54 | $where = $where === null ? [] : [ $where ]; |
55 | $batchSize = $this->getOption( 'batch-size', 1000 ); |
56 | |
57 | $dbName = $this->getOption( 'db', null ); |
58 | if ( $dbName === null ) { |
59 | $dbw = $this->getPrimaryDB(); |
60 | } else { |
61 | $dbw = $this->getServiceContainer()->getConnectionProvider()->getPrimaryDatabase( $dbName ); |
62 | } |
63 | |
64 | $selectConds = $where; |
65 | $prevEnd = false; |
66 | |
67 | $n = 1; |
68 | do { |
69 | $this->output( "Batch $n: " ); |
70 | $n++; |
71 | |
72 | // Note that the update conditions do not rely on the atomicity of the |
73 | // SELECT query in order to guarantee that all rows are updated. The |
74 | // results of the SELECT are merely a partitioning hint. Simultaneous |
75 | // updates merely result in the wrong number of rows being updated |
76 | // in a batch. |
77 | |
78 | $res = $dbw->newSelectQueryBuilder() |
79 | ->select( $key ) |
80 | ->from( $table ) |
81 | ->where( $selectConds ) |
82 | ->orderBy( $key ) |
83 | ->limit( $batchSize ) |
84 | ->caller( __METHOD__ ) |
85 | ->fetchResultSet(); |
86 | |
87 | if ( $res->numRows() ) { |
88 | $res->seek( $res->numRows() - 1 ); |
89 | $row = $res->fetchObject(); |
90 | $end = $dbw->addQuotes( $row->$key ); |
91 | $selectConds = array_merge( $where, [ "$key > $end" ] ); |
92 | $updateConds = array_merge( $where, [ "$key <= $end" ] ); |
93 | } else { |
94 | $updateConds = $where; |
95 | $end = false; |
96 | } |
97 | if ( $prevEnd !== false ) { |
98 | $updateConds = array_merge( [ "$key > $prevEnd" ], $updateConds ); |
99 | } |
100 | |
101 | $query = "UPDATE " . $dbw->tableName( $table ) . |
102 | " SET " . $set . |
103 | " WHERE " . $dbw->makeList( $updateConds, ISQLPlatform::LIST_AND ); |
104 | |
105 | $dbw->query( $query, __METHOD__ ); |
106 | |
107 | $prevEnd = $end; |
108 | |
109 | $affected = $dbw->affectedRows(); |
110 | $this->output( "$affected rows affected\n" ); |
111 | $this->waitForReplication(); |
112 | } while ( $res->numRows() ); |
113 | } |
114 | |
115 | public function getDbType() { |
116 | return Maintenance::DB_ADMIN; |
117 | } |
118 | } |
119 | |
120 | $maintClass = RunBatchedQuery::class; |
121 | require_once RUN_MAINTENANCE_IF_MAIN; |