MediaWiki  1.32.0
RenameUserJob.php
Go to the documentation of this file.
1 <?php
2 
28 class RenameUserJob extends Job {
30  private static $actorMigratedColumns = [
31  'revision.rev_user_text',
32  'archive.ar_user_text',
33  'ipblocks.ipb_by_text',
34  'image.img_user_text',
35  'oldimage.oi_user_text',
36  'filearchive.fa_user_text',
37  'recentchanges.rc_user_text',
38  'logging.log_user_text',
39  ];
40 
41  public function __construct( Title $title, $params = [] ) {
42  parent::__construct( 'renameUser', $title, $params );
43  }
44 
45  public function run() {
46  global $wgUpdateRowsPerQuery;
47 
48  $dbw = wfGetDB( DB_MASTER );
49  $table = $this->params['table'];
50  $column = $this->params['column'];
51 
52  // Skip core tables that were migrated to the actor table, even if the
53  // field still exists in the database.
54  if ( in_array( "$table.$column", self::$actorMigratedColumns, true ) ) {
55  // We still run the job for MIGRATION_WRITE_NEW because reads might
56  // still be falling back.
58  if ( $stage >= MIGRATION_NEW ) {
59  wfDebugLog( 'Renameuser',
60  "Ignoring job {$this->toString()}, column $table.$column actor migration stage = $stage\n"
61  );
62  return true;
63  }
64  }
65 
66  // It's not worth a hook to let extensions add themselves to that list.
67  // Just check whether the table and column still exist instead.
68  if ( !$dbw->tableExists( $table, __METHOD__ ) ) {
69  wfDebugLog( 'Renameuser',
70  "Ignoring job {$this->toString()}, table $table does not exist\n"
71  );
72  return true;
73  } elseif ( !$dbw->fieldExists( $table, $column, __METHOD__ ) ) {
74  wfDebugLog( 'Renameuser',
75  "Ignoring job {$this->toString()}, column $table.$column does not exist\n"
76  );
77  return true;
78  }
79 
80  $oldname = $this->params['oldname'];
81  $newname = $this->params['newname'];
82  $count = $this->params['count'];
83  if ( isset( $this->params['userID'] ) ) {
84  $userID = $this->params['userID'];
85  $uidColumn = $this->params['uidColumn'];
86  } else {
87  $userID = null;
88  $uidColumn = null;
89  }
90  if ( isset( $this->params['timestampColumn'] ) ) {
91  $timestampColumn = $this->params['timestampColumn'];
92  $minTimestamp = $this->params['minTimestamp'];
93  $maxTimestamp = $this->params['maxTimestamp'];
94  } else {
95  $timestampColumn = null;
96  $minTimestamp = null;
97  $maxTimestamp = null;
98  }
99  $uniqueKey = isset( $this->params['uniqueKey'] ) ? $this->params['uniqueKey'] : null;
100  $keyId = isset( $this->params['keyId'] ) ? $this->params['keyId'] : null;
101  $logId = isset( $this->params['logId'] ) ? $this->params['logId'] : null;
102 
103  if ( $logId ) {
104  # Block until the transaction that inserted this job commits.
105  # The atomic section is for sanity as FOR UPDATE does not lock in auto-commit mode
106  # per http://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html.
107  $dbw->startAtomic( __METHOD__ );
108  $committed = $dbw->selectField( 'logging',
109  '1',
110  [ 'log_id' => $logId ],
111  __METHOD__,
112  [ 'FOR UPDATE' ]
113  );
114  $dbw->endAtomic( __METHOD__ );
115  # If the transaction inserting this job was rolled back, detect that
116  if ( $committed === false ) { // rollback happened?
117  throw new LogicException( 'Cannot run job if the account rename failed.' );
118  }
119  }
120 
121  # Flush any state snapshot data (and release the lock above)
122  $dbw->commit( __METHOD__, 'flush' );
123 
124  # Conditions like "*_user_text = 'x'
125  $conds = [ $column => $oldname ];
126  # If user ID given, add that to condition to avoid rename collisions
127  if ( $userID !== null ) {
128  $conds[$uidColumn] = $userID;
129  }
130  # Bound by timestamp if given
131  if ( $timestampColumn !== null ) {
132  $conds[] = "$timestampColumn >= " . $dbw->addQuotes( $minTimestamp );
133  $conds[] = "$timestampColumn <= " . $dbw->addQuotes( $maxTimestamp );
134  # Bound by unique key if given (B/C)
135  } elseif ( $uniqueKey !== null && $keyId !== null ) {
136  $conds[$uniqueKey] = $keyId;
137  } else {
138  throw new InvalidArgumentException( 'Expected ID batch or time range' );
139  }
140 
141  $affectedCount = 0;
142  # Actually update the rows for this job...
143  if ( $uniqueKey !== null ) {
144  # Select the rows to update by PRIMARY KEY
145  $ids = $dbw->selectFieldValues( $table, $uniqueKey, $conds, __METHOD__ );
146  # Update these rows by PRIMARY KEY to avoid slave lag
147  foreach ( array_chunk( $ids, $wgUpdateRowsPerQuery ) as $batch ) {
148  $dbw->commit( __METHOD__, 'flush' );
149  wfWaitForSlaves();
150 
151  $dbw->update( $table,
152  [ $column => $newname ],
153  [ $column => $oldname, $uniqueKey => $batch ],
154  __METHOD__
155  );
156  $affectedCount += $dbw->affectedRows();
157  }
158  } else {
159  # Update the chunk of rows directly
160  $dbw->update( $table,
161  [ $column => $newname ],
162  $conds,
163  __METHOD__
164  );
165  $affectedCount += $dbw->affectedRows();
166  }
167 
168  # Special case: revisions may be deleted while renaming...
169  if ( $affectedCount < $count && $table === 'revision' && $timestampColumn !== null ) {
170  # If some revisions were not renamed, they may have been deleted.
171  # Do a pass on the archive table to get these straglers...
172  $ids = $dbw->selectFieldValues(
173  'archive',
174  'ar_id',
175  [
176  'ar_user_text' => $oldname,
177  'ar_user' => $userID,
178  // No user,rev_id index, so use timestamp to bound
179  // the rows. This can use the user,timestamp index.
180  "ar_timestamp >= '$minTimestamp'",
181  "ar_timestamp <= '$maxTimestamp'"
182  ],
183  __METHOD__
184  );
185  foreach ( array_chunk( $ids, $wgUpdateRowsPerQuery ) as $batch ) {
186  $dbw->commit( __METHOD__, 'flush' );
187  wfWaitForSlaves();
188 
189  $dbw->update(
190  'archive',
191  [ 'ar_user_text' => $newname ],
192  [ 'ar_user_text' => $oldname, 'ar_id' => $batch ],
193  __METHOD__
194  );
195  }
196  }
197  # Special case: revisions may be restored while renaming...
198  if ( $affectedCount < $count && $table === 'archive' && $timestampColumn !== null ) {
199  # If some revisions were not renamed, they may have been restored.
200  # Do a pass on the revision table to get these straglers...
201  $ids = $dbw->selectFieldValues(
202  'revision',
203  'rev_id',
204  [
205  'rev_user_text' => $oldname,
206  'rev_user' => $userID,
207  // No user,rev_id index, so use timestamp to bound
208  // the rows. This can use the user,timestamp index.
209  "rev_timestamp >= '$minTimestamp'",
210  "rev_timestamp <= '$maxTimestamp'"
211  ],
212  __METHOD__
213  );
214  foreach ( array_chunk( $ids, $wgUpdateRowsPerQuery ) as $batch ) {
215  $dbw->commit( __METHOD__, 'flush' );
216  wfWaitForSlaves();
217 
218  $dbw->update(
219  'revision',
220  [ 'rev_user_text' => $newname ],
221  [ 'rev_user_text' => $oldname, 'rev_id' => $batch ],
222  __METHOD__
223  );
224  }
225  }
226 
227  return true;
228  }
229 }
RenameUserJob\__construct
__construct(Title $title, $params=[])
Definition: RenameUserJob.php:41
RenameuserSQL\getActorMigrationStage
static getActorMigrationStage()
Fetch the core actor table schema migration stage.
Definition: RenameuserSQL.php:405
RenameUserJob
Custom job to perform updates on tables in busier environments.
Definition: RenameUserJob.php:28
Job\$title
Title $title
Definition: Job.php:41
MIGRATION_NEW
const MIGRATION_NEW
Definition: Defines.php:318
Job\$params
array $params
Array of job parameters.
Definition: Job.php:35
wfWaitForSlaves
wfWaitForSlaves( $ifWritesSince=null, $wiki=false, $cluster=false, $timeout=null)
Waits for the replica DBs to catch up to the master position.
Definition: GlobalFunctions.php:2847
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1082
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
Job
Class to both describe a background job and handle jobs.
Definition: Job.php:30
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2693
$wgUpdateRowsPerQuery
$wgUpdateRowsPerQuery
Number of rows to update per query.
Definition: DefaultSettings.php:8480
DB_MASTER
const DB_MASTER
Definition: defines.php:26
RenameUserJob\run
run()
Run the job.
Definition: RenameUserJob.php:45
array
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
RenameUserJob\$actorMigratedColumns
static array $actorMigratedColumns
Core tables+columns that are being migrated to the actor table.
Definition: RenameUserJob.php:30
Title
Represents a title within MediaWiki.
Definition: Title.php:39
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
$batch
$batch
Definition: linkcache.txt:23