| Current File : /home/jvzmxxx/wiki1/extensions/Wikibase/repo/includes/Store/Sql/ChangesSubscriptionTableBuilder.php |
<?php
namespace Wikibase\Repo\Store\Sql;
use DatabaseBase;
use InvalidArgumentException;
use LoadBalancer;
use ResultWrapper;
use Wikibase\DataModel\Entity\ItemId;
use Wikibase\Lib\Reporting\ExceptionHandler;
use Wikibase\Lib\Reporting\LogWarningExceptionHandler;
use Wikibase\Lib\Reporting\MessageReporter;
use Wikibase\Lib\Reporting\NullMessageReporter;
/**
* Implements initial population (priming) for the wb_changes_subscription table,
* based on the wb_items_per_site. Any wiki linked via the wb_items_per_site table
* will be considered a subscriber.
*
* @license GPL-2.0+
* @author Daniel Kinzler
*/
class ChangesSubscriptionTableBuilder {
/**
* @var LoadBalancer
*/
private $loadBalancer;
/**
* @var int
*/
private $batchSize;
/**
* @var ExceptionHandler
*/
private $exceptionHandler;
/**
* @var MessageReporter
*/
private $progressReporter;
/**
* @var string 'verbose' or 'standard'
*/
private $verbosity;
/**
* @param LoadBalancer $loadBalancer
* @param string $tableName
* @param int $batchSize
* @param string $verbosity Either 'standard' or 'verbose'
*
* @throws InvalidArgumentException
*/
public function __construct(
LoadBalancer $loadBalancer,
$tableName,
$batchSize,
$verbosity = 'standard'
) {
if ( !is_string( $tableName ) ) {
throw new InvalidArgumentException( '$tableName must be a string' );
}
if ( !is_int( $batchSize ) || $batchSize < 1 ) {
throw new InvalidArgumentException( '$batchSize must be an integer >= 1' );
}
if ( $verbosity !== 'standard' && $verbosity !== 'verbose' ) {
throw new InvalidArgumentException( '$verbosity must be either "verbose"'
. ' or "standard".' );
}
$this->loadBalancer = $loadBalancer;
$this->tableName = $tableName;
$this->batchSize = $batchSize;
$this->verbosity = $verbosity;
$this->exceptionHandler = new LogWarningExceptionHandler();
$this->progressReporter = new NullMessageReporter();
}
/**
* @param MessageReporter $progressReporter
*/
public function setProgressReporter( MessageReporter $progressReporter ) {
$this->progressReporter = $progressReporter;
}
/**
* @return MessageReporter
*/
public function getProgressReporter() {
return $this->progressReporter;
}
/**
* @param ExceptionHandler $exceptionHandler
*/
public function setExceptionHandler( ExceptionHandler $exceptionHandler ) {
$this->exceptionHandler = $exceptionHandler;
}
/**
* @return ExceptionHandler
*/
public function getExceptionHandler() {
return $this->exceptionHandler;
}
/**
* Fill the subscription table with rows based on entries in wb_items_per_site.
*
* @param ItemId|null $startItem The item to start with.
*/
public function fillSubscriptionTable( ItemId $startItem = null ) {
$continuation = $startItem === null ? null : array( $startItem->getNumericId(), 0 );
while ( true ) {
$count = $this->processSubscriptionBatch( $continuation );
if ( $count > 0 ) {
$this->progressReporter->reportMessage( 'Populating subscription table: '
. "inserted $count subscriptions, continuing at item #{$continuation[0]}." );
} else {
break;
}
}
}
/**
* @param array &$continuation
*
* @return int The number of subscriptions inserted.
*/
private function processSubscriptionBatch( &$continuation = array() ) {
$db = $this->loadBalancer->getConnection( DB_MASTER );
$subscriptionsPerItemBatch = $this->getSubscriptionsPerItemBatch( $db, $continuation );
if ( empty( $subscriptionsPerItemBatch ) ) {
return 0;
}
$count = $this->insertSubscriptionBatch( $db, $subscriptionsPerItemBatch );
$this->loadBalancer->reuseConnection( $db );
return $count;
}
/**
* @param DatabaseBase $db
* @param array[] $subscriptionsPerItem
*
* @return int The number of rows inserted.
*/
private function insertSubscriptionBatch( DatabaseBase $db, array $subscriptionsPerItem ) {
$db->startAtomic( __METHOD__ );
$c = 0;
foreach ( $subscriptionsPerItem as $itemId => $subscribers ) {
$rows = $this->makeSubscriptionRows( $itemId, $subscribers );
$db->insert(
$this->tableName,
$rows,
__METHOD__,
array(
'IGNORE'
)
);
if ( $this->verbosity === 'verbose' ) {
$this->progressReporter->reportMessage( 'Inserted ' . $db->affectedRows()
. ' into wb_changes_subscription' );
}
$c += count( $rows );
}
$db->endAtomic( __METHOD__ );
return $c;
}
/**
* @param DatabaseBase $db
* @param array &$continuation
*
* @return array[] An associative array mapping item IDs to lists of site IDs.
*/
private function getSubscriptionsPerItemBatch( DatabaseBase $db, &$continuation = array() ) {
if ( empty( $continuation ) ) {
$continuationCondition = '1';
} else {
list( $fromItemId, $fromRowId ) = $continuation;
$continuationCondition = 'ips_item_id > ' . (int)$fromItemId
. ' OR ( '
. 'ips_item_id = ' . (int)$fromItemId
. ' AND '
. 'ips_row_id > ' . $fromRowId
. ' )';
}
$res = $db->select(
'wb_items_per_site',
array( 'ips_row_id', 'ips_item_id', 'ips_site_id' ),
$continuationCondition,
__METHOD__,
array(
'LIMIT' => $this->batchSize,
'ORDER BY' => 'ips_item_id, ips_row_id'
)
);
if ( $this->verbosity === 'verbose' ) {
$this->progressReporter->reportMessage( 'Selected ' . $res->numRows() . ' wb_item_per_site records'
. ' with continuation: ' . $continuationCondition );
}
return $this->getSubscriptionsPerItemFromRows( $res, $continuation );
}
/**
* @param ResultWrapper $res A result set with the ips_item_id and ips_site_id fields
* set for each row.
* @param array &$continuation Single item ID => site ID pair or empty.
*
* @return array[] An associative array mapping item IDs to lists of site IDs.
*/
private function getSubscriptionsPerItemFromRows(
ResultWrapper $res,
&$continuation = array()
) {
$subscriptionsPerItem = array();
$currentItemId = 0;
$itemId = null;
foreach ( $res as $row ) {
if ( $row->ips_item_id != $currentItemId ) {
$currentItemId = $row->ips_item_id;
$itemId = ItemId::newFromNumber( $currentItemId )->getSerialization();
}
$subscriptionsPerItem[$itemId][] = $row->ips_site_id;
$continuation = array( $currentItemId, $row->ips_row_id );
}
return $subscriptionsPerItem;
}
/**
* Returns a list of rows for insertion, using DatabaseBase's multi-row insert mechanism.
* Each row is represented as array( $itemId, $subscriber ).
*
* @param string $itemId
* @param string[] $subscribers
*
* @return array[] rows
*/
private function makeSubscriptionRows( $itemId, array $subscribers ) {
$rows = array();
foreach ( $subscribers as $subscriber ) {
$rows[] = array(
'cs_entity_id' => $itemId,
'cs_subscriber_id' => $subscriber
);
}
return $rows;
}
}