1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace MediaWiki\Deferred;
use MediaWiki\MediaWikiServices;
use Wikimedia\Rdbms\DBTransactionError;
/**
* This class decouples DeferredUpdates's awareness of MediaWikiServices to ease unit testing.
*
* NOTE: As a process-level utility, it is important that MediaWikiServices::getInstance() is
* referenced explicitly each time, so as to not cache potentially stale references.
* For example after the Installer, or MediaWikiIntegrationTestCase, replace the service container.
*
* @internal For use by DeferredUpdates only
* @since 1.41
*/
class DeferredUpdatesScopeMediaWikiStack extends DeferredUpdatesScopeStack {
private function areDatabaseTransactionsActive(): bool {
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
if ( $lbFactory->hasTransactionRound()
|| !$lbFactory->isReadyForRoundOperations()
) {
return true;
}
foreach ( $lbFactory->getAllLBs() as $lb ) {
if ( $lb->hasPrimaryChanges() || $lb->explicitTrxActive() ) {
return true;
}
}
return false;
}
public function allowOpportunisticUpdates(): bool {
if ( MW_ENTRY_POINT !== 'cli' ) {
// In web req
return false;
}
// Run the updates only if they will have outer transaction scope
if ( $this->areDatabaseTransactionsActive() ) {
// transaction round is active or connection is not ready for commit()
return false;
}
return true;
}
public function queueDataUpdate( EnqueueableDataUpdate $update ): void {
$spec = $update->getAsJobSpecification();
$jobQueueGroupFactory = MediaWikiServices::getInstance()->getJobQueueGroupFactory();
$jobQueueGroupFactory->makeJobQueueGroup( $spec['domain'] )->push( $spec['job'] );
}
public function onRunUpdateStart( DeferrableUpdate $update ): void {
// Increment a counter metric
$type = get_class( $update )
. ( $update instanceof DeferrableCallback ? '_' . $update->getOrigin() : '' );
$httpMethod = MW_ENTRY_POINT === 'cli' ? 'cli' : strtolower( $_SERVER['REQUEST_METHOD'] ?? 'GET' );
$stats = MediaWikiServices::getInstance()->getStatsFactory();
$stats->getCounter( 'deferred_updates_total' )
->setLabel( 'http_method', $httpMethod )
->setLabel( 'type', $type )
->copyToStatsdAt( "deferred_updates.$httpMethod.$type" )
->increment();
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
$ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
if ( !$ticket || $lbFactory->hasTransactionRound() ) {
throw new DBTransactionError( null, "A database transaction round is pending." );
}
if ( $update instanceof DataUpdate ) {
$update->setTransactionTicket( $ticket );
}
// Designate $update::doUpdate() as the transaction round owner
$fnameTrxOwner = ( $update instanceof DeferrableCallback )
? $update->getOrigin()
: get_class( $update ) . '::doUpdate';
// Determine whether the transaction round will be explicit or implicit
$useExplicitTrxRound = !(
$update instanceof TransactionRoundAwareUpdate &&
$update->getTransactionRoundRequirement() == $update::TRX_ROUND_ABSENT
);
if ( $useExplicitTrxRound ) {
// Start a new explicit round
$lbFactory->beginPrimaryChanges( $fnameTrxOwner );
} else {
// Start a new implicit round
$lbFactory->commitPrimaryChanges( $fnameTrxOwner );
}
}
public function onRunUpdateEnd( DeferrableUpdate $update ): void {
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
$fnameTrxOwner = ( $update instanceof DeferrableCallback )
? $update->getOrigin()
: get_class( $update ) . '::doUpdate';
// Commit any pending changes from the explicit or implicit transaction round
$lbFactory->commitPrimaryChanges( $fnameTrxOwner );
}
public function onRunUpdateFailed( DeferrableUpdate $update ): void {
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
$lbFactory->rollbackPrimaryChanges( __METHOD__ );
}
}
/** @deprecated class alias since 1.42 */
class_alias( DeferredUpdatesScopeMediaWikiStack::class, 'DeferredUpdatesScopeMediaWikiStack' );
|