Aaron Schulz has uploaded a new change for review.
https://gerrit.wikimedia.org/r/250363
Change subject: Convert recordUpload2() to using startAtomic/endAtomic
......................................................................
Convert recordUpload2() to using startAtomic/endAtomic
* This avoids breaking transactions due to nesting
* Also improve readability a bit in this area
Change-Id: I81c41e83d14aa59930bfb99522ebcc25d8aa14f9
---
M includes/filerepo/file/LocalFile.php
1 file changed, 74 insertions(+), 75 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core
refs/changes/63/250363/1
diff --git a/includes/filerepo/file/LocalFile.php
b/includes/filerepo/file/LocalFile.php
index 390b7fe..cd64f0d 100644
--- a/includes/filerepo/file/LocalFile.php
+++ b/includes/filerepo/file/LocalFile.php
@@ -316,7 +316,7 @@
/**
* Purge the file object/metadata cache
*/
- function invalidateCache() {
+ public function invalidateCache() {
$key = $this->getCacheKey();
if ( !$key ) {
return;
@@ -1209,21 +1209,15 @@
* @param null|User $user
* @return bool
*/
- function recordUpload2( $oldver, $comment, $pageText, $props = false,
$timestamp = false,
- $user = null
+ function recordUpload2(
+ $oldver, $comment, $pageText, $props = false, $timestamp =
false, $user = null
) {
-
if ( is_null( $user ) ) {
global $wgUser;
$user = $wgUser;
}
$dbw = $this->repo->getMasterDB();
- $dbw->begin( __METHOD__ );
-
- if ( !$props ) {
- $props = $this->repo->getFileProps(
$this->getVirtualUrl() );
- }
# Imports or such might force a certain timestamp; otherwise we
generate
# it and can fudge it slightly to keep (name,timestamp) unique
on re-upload.
@@ -1234,6 +1228,7 @@
$allowTimeKludge = false;
}
+ $props = $props ?: $this->repo->getFileProps(
$this->getVirtualUrl() );
$props['description'] = $comment;
$props['user'] = $user->getId();
$props['user_text'] = $user->getName();
@@ -1243,12 +1238,11 @@
# Fail now if the file isn't there
if ( !$this->fileExists ) {
wfDebug( __METHOD__ . ": File " . $this->getRel() . "
went missing!\n" );
- $dbw->rollback( __METHOD__ );
return false;
}
- $reupload = false;
+ $dbw->startAtomic( __METHOD__ );
# Test to see if the row exists using INSERT IGNORE
# This avoids race conditions by locking the row until the
commit, and also
@@ -1273,13 +1267,18 @@
__METHOD__,
'IGNORE'
);
- if ( $dbw->affectedRows() == 0 ) {
+
+ $reupload = ( $dbw->affectedRows() == 0 );
+ if ( $reupload ) {
if ( $allowTimeKludge ) {
# Use LOCK IN SHARE MODE to ignore any
transaction snapshotting
- $ltimestamp = $dbw->selectField( 'image',
'img_timestamp',
+ $ltimestamp = $dbw->selectField(
+ 'image',
+ 'img_timestamp',
array( 'img_name' => $this->getName() ),
__METHOD__,
- array( 'LOCK IN SHARE MODE' ) );
+ array( 'LOCK IN SHARE MODE' )
+ );
$lUnixtime = $ltimestamp ? wfTimestamp(
TS_UNIX, $ltimestamp ) : false;
# Avoid a timestamp that is not newer than the
last version
# TODO: the image/oldimage tables should be
like page/revision with an ID field
@@ -1294,8 +1293,6 @@
# version of the file was broken. Allow registration of
the new
# version to continue anyway, because that's better
than having
# an image that's not fixable by user operations.
-
- $reupload = true;
# Collision, this is an update of a file
# Insert previous contents into oldimage
$dbw->insertSelect( 'oldimage', 'image',
@@ -1322,7 +1319,7 @@
# Update the current image row
$dbw->update( 'image',
- array( /* SET */
+ array(
'img_size' => $this->size,
'img_width' => intval( $this->width ),
'img_height' => intval( $this->height ),
@@ -1340,23 +1337,17 @@
array( 'img_name' => $this->getName() ),
__METHOD__
);
- } else {
- # This is a new file, so update the image count
- DeferredUpdates::addUpdate( SiteStatsUpdate::factory(
array( 'images' => 1 ) ) );
}
$descTitle = $this->getTitle();
$wikiPage = new WikiFilePage( $descTitle );
$wikiPage->setFile( $this );
- # Add the log entry
- $action = $reupload ? 'overwrite' : 'upload';
-
- $logEntry = new ManualLogEntry( 'upload', $action );
+ // Add the log entry...
+ $logEntry = new ManualLogEntry( 'upload', $reupload ?
'overwrite' : 'upload' );
$logEntry->setPerformer( $user );
$logEntry->setComment( $comment );
$logEntry->setTarget( $descTitle );
-
// Allow people using the api to associate log entries with the
upload.
// Log has a timestamp, but sometimes different from upload
timestamp.
$logEntry->setParameters(
@@ -1373,15 +1364,9 @@
// now and wait until the page exists.
$logId = $logEntry->insert();
- $exists = $descTitle->exists();
- if ( $exists ) {
- // Page exists, do RC entry now (otherwise we wait for
later).
+ if ( $descTitle->exists() ) {
+ // Page exists, do RC entry now (otherwise we wait for
later)
$logEntry->publish( $logId );
- }
-
- if ( $exists ) {
- # Create a null revision
- $latest = $descTitle->getLatestRevID();
// Use own context to get the action text in content
language
$formatter = LogFormatter::newFromEntry( $logEntry );
$formatter->setContext(
RequestContext::newExtraneousContext( $descTitle ) );
@@ -1394,60 +1379,69 @@
false,
$user
);
- if ( !is_null( $nullRevision ) ) {
+ if ( $nullRevision ) {
$nullRevision->insertOn( $dbw );
-
- Hooks::run( 'NewRevisionFromEditComplete',
array( $wikiPage, $nullRevision, $latest, $user ) );
+ Hooks::run(
+ 'NewRevisionFromEditComplete',
+ array( $wikiPage, $nullRevision,
$nullRevision->getParentId(), $user )
+ );
$wikiPage->updateRevisionOn( $dbw,
$nullRevision );
}
- }
- # Commit the transaction now, in case something goes wrong later
- # The most important thing is that files don't get lost,
especially archives
- # NOTE: once we have support for nested transactions, the
commit may be moved
- # to after $wikiPage->doEdit has been called.
- $dbw->commit( __METHOD__ );
-
- # Update memcache after the commit
- $this->invalidateCache();
-
- if ( $exists ) {
- # Invalidate the cache for the description page
- $descTitle->invalidateCache();
- $descTitle->purgeSquid();
+ $newPageContent = null;
} else {
- # New file; create the description page.
- # There's already a log entry, so don't make a second
RC entry
- # Squid and file cache for the description page are
purged by doEditContent.
- $content = ContentHandler::makeContent( $pageText,
$descTitle );
- $status = $wikiPage->doEditContent(
- $content,
- $comment,
- EDIT_NEW | EDIT_SUPPRESS_RC,
- false,
- $user
- );
-
- // Now that the page exists, make an RC entry.
- // This relies on the resetArticleID() call in
WikiPage::insertOn(),
- // which is triggered on $descTitle by doEditContent()
above.
- $logEntry->publish( $logId );
- if ( isset( $status->value['revision'] ) ) {
- $dbw->update( 'logging',
- array( 'log_page' =>
$status->value['revision']->getPage() ),
- array( 'log_id' => $logId ),
- __METHOD__
- );
- }
+ // Make the description page and RC log entry
post-commit
+ $newPageContent = ContentHandler::makeContent(
$pageText, $descTitle );
}
+
+ # Defer purges, page creation, and link updates in case they
error out.
+ # The most important thing is that files and the DB registry
stay synced.
+ $dbw->endAtomic( __METHOD__ );
# Do some cache purges after final commit so that:
# a) Changes are more likely to be seen post-purge
# b) They won't cause rollback of the log publish/update above
$that = $this;
- $dbw->onTransactionIdle( function () use ( $that, $reupload,
$descTitle ) {
+ $dbw->onTransactionIdle( function () use (
+ $that, $reupload, $wikiPage, $newPageContent, $comment,
$user, $logEntry, $logId
+ ) {
+ # Update memcache after the commit
+ $that->invalidateCache();
+
+ if ( $newPageContent ) {
+ # New file page; create the description page.
+ # There's already a log entry, so don't make a
second RC entry
+ # Squid and file cache for the description page
are purged by doEditContent.
+ $status = $wikiPage->doEditContent(
+ $newPageContent,
+ $comment,
+ EDIT_NEW | EDIT_SUPPRESS_RC,
+ false,
+ $user
+ );
+
+ // Now that the page exists, make an RC entry.
+ // This relies on the resetArticleID() call in
WikiPage::insertOn(),
+ // which is triggered on $descTitle by
doEditContent() above.
+ $logEntry->publish( $logId );
+ if ( isset( $status->value['revision'] ) ) {
+ /** @var $rev Revision */
+ $rev = $status->value['revision'];
+ $that->getRepo()->getMasterDB()->update(
+ 'logging',
+ array( 'log_page' =>
$rev->getPage() ),
+ array( 'log_id' => $logId ),
+ __METHOD__
+ );
+ }
+ } else {
+ # Existing file page: invalidate description
page cache
+ $wikiPage->getTitle()->invalidateCache();
+ $wikiPage->getTitle()->purgeSquid();
+ }
+
# Run hook for other updates (typically more cache
purging)
- Hooks::run( 'FileUpload', array( $that, $reupload,
$descTitle->exists() ) );
+ Hooks::run( 'FileUpload', array( $that, $reupload,
!$newPageContent ) );
if ( $reupload ) {
# Delete old thumbnails
@@ -1460,6 +1454,11 @@
}
} );
+ if ( !$reupload ) {
+ # This is a new file, so update the image count
+ DeferredUpdates::addUpdate( SiteStatsUpdate::factory(
array( 'images' => 1 ) ) );
+ }
+
# Invalidate cache for all pages using this file
DeferredUpdates::addUpdate( new HTMLCacheUpdate(
$this->getTitle(), 'imagelinks' ) );
--
To view, visit https://gerrit.wikimedia.org/r/250363
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I81c41e83d14aa59930bfb99522ebcc25d8aa14f9
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Aaron Schulz <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits