use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
use MediaWiki\MediaWikiServices;
+use Wikimedia\Rdbms\LoadBalancer;
/**
* Static accessor class for site_stats and related things
*/
class SiteStats {
- /** @var bool|stdClass */
+ /** @var stdClass */
private static $row;
- /** @var bool */
- private static $loaded = false;
- /** @var int[] */
- private static $pageCount = [];
-
- static function unload() {
- self::$loaded = false;
- }
-
- static function recache() {
- self::load( true );
- }
-
/**
- * @param bool $recache
+ * Trigger a reload next time a field is accessed
*/
- static function load( $recache = false ) {
- if ( self::$loaded && !$recache ) {
- return;
- }
-
- self::$row = self::loadAndLazyInit();
+ public static function unload() {
+ self::$row = null;
+ }
- self::$loaded = true;
+ protected static function load() {
+ if ( self::$row === null ) {
+ self::$row = self::loadAndLazyInit();
+ }
}
/**
- * @return bool|stdClass
+ * @return stdClass
*/
- static function loadAndLazyInit() {
- global $wgMiserMode;
+ protected static function loadAndLazyInit() {
+ $config = MediaWikiServices::getInstance()->getMainConfig();
+ $lb = self::getLB();
+ $dbr = $lb->getConnection( DB_REPLICA );
wfDebug( __METHOD__ . ": reading site_stats from replica DB\n" );
- $row = self::doLoad( wfGetDB( DB_REPLICA ) );
+ $row = self::doLoadFromDB( $dbr );
- if ( !self::isSane( $row ) ) {
- $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
- if ( $lb->hasOrMadeRecentMasterChanges() ) {
- // Might have just been initialized during this request? Underflow?
- wfDebug( __METHOD__ . ": site_stats damaged or missing on replica DB\n" );
- $row = self::doLoad( wfGetDB( DB_MASTER ) );
- }
+ if ( !self::isSane( $row ) && $lb->hasOrMadeRecentMasterChanges() ) {
+ // Might have just been initialized during this request? Underflow?
+ wfDebug( __METHOD__ . ": site_stats damaged or missing on replica DB\n" );
+ $row = self::doLoadFromDB( $lb->getConnection( DB_MASTER ) );
}
if ( !self::isSane( $row ) ) {
- if ( $wgMiserMode ) {
+ if ( $config->get( 'MiserMode' ) ) {
// Start off with all zeroes, assuming that this is a new wiki or any
// repopulations where done manually via script.
SiteStatsInit::doPlaceholderInit();
// broken, however, for instance when importing from a dump into a
// clean schema with mwdumper.
wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" );
- SiteStatsInit::doAllAndCommit( wfGetDB( DB_REPLICA ) );
+ SiteStatsInit::doAllAndCommit( $dbr );
}
- $row = self::doLoad( wfGetDB( DB_MASTER ) );
+ $row = self::doLoadFromDB( $lb->getConnection( DB_MASTER ) );
}
if ( !self::isSane( $row ) ) {
wfDebug( __METHOD__ . ": site_stats persistently nonsensical o_O\n" );
+ // Always return a row-like object
$row = (object)array_fill_keys( self::selectFields(), 0 );
}
/**
* @param IDatabase $db
- * @return bool|stdClass
+ * @return stdClass|bool
*/
- static function doLoad( $db ) {
+ private static function doLoadFromDB( IDatabase $db ) {
return $db->selectRow(
'site_stats',
self::selectFields(),
}
/**
- * Return the total number of page views. Except we don't track those anymore.
- * Stop calling this function, it will be removed some time in the future. It's
- * kept here simply to prevent fatal errors.
- *
- * @deprecated since 1.25
* @return int
*/
- static function views() {
- wfDeprecated( __METHOD__, '1.25' );
- return 0;
- }
-
- /**
- * @return int
- */
- static function edits() {
+ public static function edits() {
self::load();
+
return self::$row->ss_total_edits;
}
/**
* @return int
*/
- static function articles() {
+ public static function articles() {
self::load();
+
return self::$row->ss_good_articles;
}
/**
* @return int
*/
- static function pages() {
+ public static function pages() {
self::load();
+
return self::$row->ss_total_pages;
}
/**
* @return int
*/
- static function users() {
+ public static function users() {
self::load();
+
return self::$row->ss_users;
}
/**
* @return int
*/
- static function activeUsers() {
+ public static function activeUsers() {
self::load();
+
return self::$row->ss_active_users;
}
/**
* @return int
*/
- static function images() {
+ public static function images() {
self::load();
+
return self::$row->ss_images;
}
* @param string $group Name of group
* @return int
*/
- static function numberingroup( $group ) {
+ public static function numberingroup( $group ) {
$cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+
return $cache->getWithSetCallback(
$cache->makeKey( 'SiteStats', 'groupcounts', $group ),
$cache::TTL_HOUR,
function ( $oldValue, &$ttl, array &$setOpts ) use ( $group ) {
- $dbr = wfGetDB( DB_REPLICA );
-
+ $dbr = self::getLB()->getConnection( DB_REPLICA );
$setOpts += Database::getCacheSetOptions( $dbr );
- return $dbr->selectField(
+ return (int)$dbr->selectField(
'user_groups',
'COUNT(*)',
[
* Total number of jobs in the job queue.
* @return int
*/
- static function jobs() {
+ public static function jobs() {
$cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+
return $cache->getWithSetCallback(
$cache->makeKey( 'SiteStats', 'jobscount' ),
$cache::TTL_MINUTE,
/**
* @param int $ns
- *
* @return int
*/
- static function pagesInNs( $ns ) {
- if ( !isset( self::$pageCount[$ns] ) ) {
- $dbr = wfGetDB( DB_REPLICA );
- self::$pageCount[$ns] = (int)$dbr->selectField(
- 'page',
- 'COUNT(*)',
- [ 'page_namespace' => $ns ],
- __METHOD__
- );
- }
- return self::$pageCount[$ns];
+ public static function pagesInNs( $ns ) {
+ $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+
+ return $cache->getWithSetCallback(
+ $cache->makeKey( 'SiteStats', 'page-in-namespace', $ns ),
+ $cache::TTL_HOUR,
+ function ( $oldValue, &$ttl, array &$setOpts ) use ( $ns ) {
+ $dbr = self::getLB()->getConnection( DB_REPLICA );
+ $setOpts += Database::getCacheSetOptions( $dbr );
+
+ return (int)$dbr->selectField(
+ 'page',
+ 'COUNT(*)',
+ [ 'page_namespace' => $ns ],
+ __METHOD__
+ );
+ },
+ [ 'pcTTL' => $cache::TTL_PROC_LONG ]
+ );
}
/**
return false;
}
}
+
return true;
}
+
+ /**
+ * @return LoadBalancer
+ */
+ private static function getLB() {
+ return MediaWikiServices::getInstance()->getDBLoadBalancer();
+ }
}
* @file
*/
use Wikimedia\Rdbms\IDatabase;
+use MediaWiki\MediaWikiServices;
/**
* Class designed for counting of stats.
*/
class SiteStatsInit {
-
- // Database connection
- private $db;
-
- // Various stats
- private $mEdits = null, $mArticles = null, $mPages = null;
- private $mUsers = null, $mFiles = null;
+ /* @var IDatabase */
+ private $dbr;
+ /** @var int */
+ private $edits;
+ /** @var int */
+ private $articles;
+ /** @var int */
+ private $pages;
+ /** @var int */
+ private $users;
+ /** @var int */
+ private $files;
/**
* @param bool|IDatabase $database
*/
public function __construct( $database = false ) {
if ( $database instanceof IDatabase ) {
- $this->db = $database;
+ $this->dbr = $database;
} elseif ( $database ) {
- $this->db = wfGetDB( DB_MASTER );
+ $this->dbr = self::getDB( DB_MASTER );
} else {
- $this->db = wfGetDB( DB_REPLICA, 'vslow' );
+ $this->dbr = self::getDB( DB_REPLICA, 'vslow' );
}
}
* @return int
*/
public function edits() {
- $this->mEdits = $this->db->selectField( 'revision', 'COUNT(*)', '', __METHOD__ );
- $this->mEdits += $this->db->selectField( 'archive', 'COUNT(*)', '', __METHOD__ );
- return $this->mEdits;
+ $this->edits = $this->dbr->selectField( 'revision', 'COUNT(*)', '', __METHOD__ );
+ $this->edits += $this->dbr->selectField( 'archive', 'COUNT(*)', '', __METHOD__ );
+
+ return $this->edits;
}
/**
* @return int
*/
public function articles() {
- global $wgArticleCountMethod;
+ $config = MediaWikiServices::getInstance()->getMainConfig();
$tables = [ 'page' ];
$conds = [
'page_is_redirect' => 0,
];
- if ( $wgArticleCountMethod == 'link' ) {
+ if ( $config->get( 'ArticleCountMethod' ) == 'link' ) {
$tables[] = 'pagelinks';
$conds[] = 'pl_from=page_id';
- } elseif ( $wgArticleCountMethod == 'comma' ) {
+ } elseif ( $config->get( 'ArticleCountMethod' ) == 'comma' ) {
// To make a correct check for this, we would need, for each page,
// to load the text, maybe uncompress it, maybe decode it and then
// check if there's one comma.
$conds[] = 'page_len > 0';
}
- $this->mArticles = $this->db->selectField( $tables, 'COUNT(DISTINCT page_id)',
- $conds, __METHOD__ );
- return $this->mArticles;
+ $this->articles = $this->dbr->selectField(
+ $tables,
+ 'COUNT(DISTINCT page_id)',
+ $conds,
+ __METHOD__
+ );
+
+ return $this->articles;
}
/**
* @return int
*/
public function pages() {
- $this->mPages = $this->db->selectField( 'page', 'COUNT(*)', '', __METHOD__ );
- return $this->mPages;
+ $this->pages = $this->dbr->selectField( 'page', 'COUNT(*)', '', __METHOD__ );
+
+ return $this->pages;
}
/**
* @return int
*/
public function users() {
- $this->mUsers = $this->db->selectField( 'user', 'COUNT(*)', '', __METHOD__ );
- return $this->mUsers;
+ $this->users = $this->dbr->selectField( 'user', 'COUNT(*)', '', __METHOD__ );
+
+ return $this->users;
}
/**
* @return int
*/
public function files() {
- $this->mFiles = $this->db->selectField( 'image', 'COUNT(*)', '', __METHOD__ );
- return $this->mFiles;
+ $this->files = $this->dbr->selectField( 'image', 'COUNT(*)', '', __METHOD__ );
+
+ return $this->files;
}
/**
$options += [ 'update' => false, 'activeUsers' => false ];
// Grab the object and count everything
- $counter = new SiteStatsInit( $database );
+ $counter = new self( $database );
$counter->edits();
$counter->articles();
// Count active users if need be
if ( $options['activeUsers'] ) {
- SiteStatsUpdate::cacheUpdate( wfGetDB( DB_MASTER ) );
+ SiteStatsUpdate::cacheUpdate( self::getDB( DB_MASTER ) );
}
}
* Insert a dummy row with all zeroes if no row is present
*/
public static function doPlaceholderInit() {
- $dbw = wfGetDB( DB_MASTER );
+ $dbw = self::getDB( DB_MASTER );
$exists = $dbw->selectField( 'site_stats', '1', [ 'ss_row_id' => 1 ], __METHOD__ );
if ( $exists === false ) {
$dbw->insert(
public function refresh() {
$values = [
'ss_row_id' => 1,
- 'ss_total_edits' => ( $this->mEdits === null ? $this->edits() : $this->mEdits ),
- 'ss_good_articles' => ( $this->mArticles === null ? $this->articles() : $this->mArticles ),
- 'ss_total_pages' => ( $this->mPages === null ? $this->pages() : $this->mPages ),
- 'ss_users' => ( $this->mUsers === null ? $this->users() : $this->mUsers ),
- 'ss_images' => ( $this->mFiles === null ? $this->files() : $this->mFiles ),
+ 'ss_total_edits' => $this->edits === null ? $this->edits() : $this->edits,
+ 'ss_good_articles' => $this->articles === null ? $this->articles() : $this->articles,
+ 'ss_total_pages' => $this->pages === null ? $this->pages() : $this->pages,
+ 'ss_users' => $this->users === null ? $this->users() : $this->users,
+ 'ss_images' => $this->files === null ? $this->files() : $this->files,
];
- $dbw = wfGetDB( DB_MASTER );
- $dbw->upsert( 'site_stats', $values, [ 'ss_row_id' ], $values, __METHOD__ );
+ self::getDB( DB_MASTER )->upsert(
+ 'site_stats',
+ $values,
+ [ 'ss_row_id' ],
+ $values,
+ __METHOD__
+ );
+ }
+
+ /**
+ * @param int $index
+ * @return IDatabase
+ */
+ private static function getDB( $index ) {
+ return MediaWikiServices::getInstance()->getDBLoadBalancer()->getConnection( $index );
}
}
* Class for handling updates to the site_stats table
*/
class SiteStatsUpdate implements DeferrableUpdate, MergeableUpdate {
+ /** @var BagOStuff */
+ protected $stash;
/** @var int */
protected $edits = 0;
/** @var int */
$this->articles = $good;
$this->pages = $pages;
$this->users = $users;
+
+ $this->stash = MediaWikiServices::getInstance()->getMainObjectStash();
}
public function merge( MergeableUpdate $update ) {
}
public function doUpdate() {
- global $wgSiteStatsAsyncFactor;
-
$this->doUpdateContextStats();
- $rate = $wgSiteStatsAsyncFactor; // convenience
+ $rate = MediaWikiServices::getInstance()->getMainConfig()->get( 'SiteStatsAsyncFactor' );
// If set to do so, only do actual DB updates 1 every $rate times.
// The other times, just update "pending delta" values in memcached.
if ( $rate && ( $rate < 0 || mt_rand( 0, $rate - 1 ) != 0 ) ) {
* Do not call this outside of SiteStatsUpdate
*/
public function tryDBUpdateInternal() {
- global $wgSiteStatsAsyncFactor;
+ $services = MediaWikiServices::getInstance();
+ $config = $services->getMainConfig();
- $dbw = wfGetDB( DB_MASTER );
- $lockKey = wfWikiID() . ':site_stats'; // prepend wiki ID
+ $dbw = $services->getDBLoadBalancer()->getConnection( DB_MASTER );
+ $lockKey = $dbw->getDomainID() . ':site_stats'; // prepend wiki ID
$pd = [];
- if ( $wgSiteStatsAsyncFactor ) {
+ if ( $config->get( 'SiteStatsAsyncFactor' ) ) {
// Lock the table so we don't have double DB/memcached updates
if ( !$dbw->lockIsFree( $lockKey, __METHOD__ )
|| !$dbw->lock( $lockKey, __METHOD__, 1 ) // 1 sec timeout
$dbw->update( 'site_stats', [ $updates ], [], __METHOD__ );
}
- if ( $wgSiteStatsAsyncFactor ) {
+ if ( $config->get( 'SiteStatsAsyncFactor' ) ) {
// Decrement the async deltas now that we applied them
$this->removePendingDeltas( $pd );
// Commit the updates and unlock the table
* @param IDatabase $dbw
* @return bool|mixed
*/
- public static function cacheUpdate( $dbw ) {
- global $wgActiveUserDays;
- $dbr = wfGetDB( DB_REPLICA, 'vslow' );
+ public static function cacheUpdate( IDatabase $dbw ) {
+ $services = MediaWikiServices::getInstance();
+ $config = $services->getMainConfig();
+
+ $dbr = $services->getDBLoadBalancer()->getConnection( DB_REPLICA, 'vslow' );
# Get non-bot users than did some recent action other than making accounts.
# If account creation is included, the number gets inflated ~20+ fold on enwiki.
$activeUsers = $dbr->selectField(
'rc_user != 0',
'rc_bot' => 0,
'rc_log_type != ' . $dbr->addQuotes( 'newusers' ) . ' OR rc_log_type IS NULL',
- 'rc_timestamp >= ' . $dbr->addQuotes( $dbr->timestamp( wfTimestamp( TS_UNIX )
- - $wgActiveUserDays * 24 * 3600 ) ),
+ 'rc_timestamp >= ' . $dbr->addQuotes(
+ $dbr->timestamp( time() - $config->get( 'ActiveUserDays' ) * 24 * 3600 ) ),
],
__METHOD__
);
}
/**
- * @param BagOStuff $cache
+ * @param BagOStuff $stash
* @param string $type
* @param string $sign ('+' or '-')
* @return string
*/
- private function getTypeCacheKey( BagOStuff $cache, $type, $sign ) {
- return $cache->makeKey( 'sitestatsupdate', 'pendingdelta', $type, $sign );
+ private function getTypeCacheKey( BagOStuff $stash, $type, $sign ) {
+ return $stash->makeKey( 'sitestatsupdate', 'pendingdelta', $type, $sign );
}
/**
* @param int $delta Delta (positive or negative)
*/
protected function adjustPending( $type, $delta ) {
- $cache = MediaWikiServices::getInstance()->getMainObjectStash();
if ( $delta < 0 ) { // decrement
- $key = $this->getTypeCacheKey( $cache, $type, '-' );
+ $key = $this->getTypeCacheKey( $this->stash, $type, '-' );
} else { // increment
- $key = $this->getTypeCacheKey( $cache, $type, '+' );
+ $key = $this->getTypeCacheKey( $this->stash, $type, '+' );
}
$magnitude = abs( $delta );
- $cache->incrWithInit( $key, 0, $magnitude, $magnitude );
+ $this->stash->incrWithInit( $key, 0, $magnitude, $magnitude );
}
/**
* @return array Positive and negative deltas for each type
*/
protected function getPendingDeltas() {
- $cache = MediaWikiServices::getInstance()->getMainObjectStash();
-
$pending = [];
foreach ( [ 'ss_total_edits',
'ss_good_articles', 'ss_total_pages', 'ss_users', 'ss_images' ] as $type
) {
// Get pending increments and pending decrements
$flg = BagOStuff::READ_LATEST;
- $pending[$type]['+'] = (int)$cache->get( $this->getTypeCacheKey( $cache, $type, '+' ), $flg );
- $pending[$type]['-'] = (int)$cache->get( $this->getTypeCacheKey( $cache, $type, '-' ), $flg );
+ $pending[$type]['+'] = (int)$this->stash->get(
+ $this->getTypeCacheKey( $this->stash, $type, '+' ),
+ $flg
+ );
+ $pending[$type]['-'] = (int)$this->stash->get(
+ $this->getTypeCacheKey( $this->stash, $type, '-' ),
+ $flg
+ );
}
return $pending;
* @param array $pd Result of getPendingDeltas(), used for DB update
*/
protected function removePendingDeltas( array $pd ) {
- $cache = MediaWikiServices::getInstance()->getMainObjectStash();
-
foreach ( $pd as $type => $deltas ) {
foreach ( $deltas as $sign => $magnitude ) {
// Lower the pending counter now that we applied these changes
- $cache->decr( $this->getTypeCacheKey( $cache, $type, $sign ), $magnitude );
+ $key = $this->getTypeCacheKey( $this->stash, $type, $sign );
+ $this->stash->decr( $key, $magnitude );
}
}
}