wfProfileOut( __METHOD__ . '-getProps' );
}
+ # 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.
if ( $timestamp === false ) {
- // Use FOR UPDATE in case lock()/unlock() did not start the transaction
- $ltimestamp = $dbw->selectField( 'image', 'img_timestamp',
- array( 'img_name' => $this->getName() ), __METHOD__, array( 'FOR UPDATE' ) );
- $ltime = $ltimestamp ? wfTimestamp( TS_UNIX, $ltimestamp ) : false;
- $ctime = time();
- // Avoid a timestamp that is not newer than the last version
- if ( $ctime > $ltime ) {
- $timestamp = $dbw->timestamp( $ctime );
- } else {
- sleep( 1 ); // fast enough uploads will go in to the future otherwise
- $timestamp = $dbw->timestamp( $ltime + 1 );
- }
+ $timestamp = $dbw->timestamp();
+ $allowTimeKludge = true;
+ } else {
+ $allowTimeKludge = false;
}
$props['description'] = $comment;
'IGNORE'
);
if ( $dbw->affectedRows() == 0 ) {
+ if ( $allowTimeKludge ) {
+ # Use FOR UPDATE to ignore any transaction snapshotting
+ $ltimestamp = $dbw->selectField( 'image', 'img_timestamp',
+ array( 'img_name' => $this->getName() ), __METHOD__, array( 'FOR UPDATE' ) );
+ $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
+ if ( $lUnixtime && wfTimestamp( TS_UNIX, $timestamp ) <= $lUnixtime ) {
+ sleep( 1 ); // fast enough re-uploads would go far in the future otherwise
+ $timestamp = $dbw->timestamp( $lUnixtime + 1 );
+ $this->timestamp = wfTimestamp( TS_MW, $timestamp ); // DB -> TS_MW
+ }
+ }
+
# (bug 34993) Note: $oldver can be empty here, if the previous
# version of the file was broken. Allow registration of the new
# version to continue anyway, because that's better than having
/**
* Start a transaction and lock the image for update
* Increments a reference counter if the lock is already held
- * @throws MWException
- * @return bool True if the image exists, false otherwise
+ * @throws MWException Throws an error if the lock was not acquired
+ * @return bool success
*/
function lock() {
$dbw = $this->repo->getMasterDB();
}
$this->locked++;
// Bug 54736: use simple lock to handle when the file does not exist.
- // SELECT FOR UPDATE only locks records not the gaps where there are none.
- $cache = wfGetMainCache();
- $key = $this->getCacheKey();
- if ( !$cache->lock( $key, 5 ) ) {
+ // SELECT FOR UPDATE prevents changes, not other SELECTs with FOR UPDATE.
+ // Also, that would cause contention on INSERT of similarly named rows.
+ $backend = $this->getRepo()->getBackend();
+ $lockPaths = array( $this->getPath() ); // represents all versions of the file
+ $status = $backend->lockFiles( $lockPaths, LockManager::LOCK_EX, 5 );
+ if ( !$status->isGood() ) {
throw new MWException( "Could not acquire lock for '{$this->getName()}.'" );
}
- $dbw->onTransactionIdle( function () use ( $cache, $key ) {
- $cache->unlock( $key ); // release on commit
+ $dbw->onTransactionIdle( function () use ( $backend, $lockPaths ) {
+ $backend->unlockFiles( $lockPaths, LockManager::LOCK_EX ); // release on commit
} );
}
$this->markVolatile(); // file may change soon
- return $dbw->selectField( 'image', '1',
- array( 'img_name' => $this->getName() ), __METHOD__, array( 'FOR UPDATE' ) );
+ return true;
}
/**
return $this->file->repo->newGood();
}
- $exists = $this->file->lock();
+ $this->file->lock();
+
$dbw = $this->file->repo->getMasterDB();
$status = $this->file->repo->newGood();
+ $exists = (bool)$dbw->selectField( 'image', '1',
+ array( 'img_name' => $this->file->getName() ), __METHOD__, array( 'FOR UPDATE' ) );
+
// Fetch all or selected archived revisions for the file,
// sorted from the most recent to the oldest.
$conditions = array( 'fa_name' => $this->file->getName() );
$result = $this->db->select( 'oldimage',
array( 'oi_archive_name', 'oi_deleted' ),
array( 'oi_name' => $this->oldName ),
- __METHOD__
+ __METHOD__,
+ array( 'FOR UPDATE' ) // ignore snapshot
);
foreach ( $result as $row ) {