getMainConfig(); $lb = self::getLB(); $dbr = $lb->getConnection( DB_REPLICA ); wfDebug( __METHOD__ . ": reading site_stats from replica DB" ); $row = self::doLoadFromDB( $dbr ); if ( !self::isRowSensible( $row ) && $lb->hasOrMadeRecentPrimaryChanges() ) { // Might have just been initialized during this request? Underflow? wfDebug( __METHOD__ . ": site_stats damaged or missing on replica DB" ); $row = self::doLoadFromDB( $lb->getConnection( DB_PRIMARY ) ); } if ( !self::isRowSensible( $row ) ) { if ( $config->get( MainConfigNames::MiserMode ) ) { // Start off with all zeroes, assuming that this is a new wiki or any // repopulations where done manually via script. SiteStatsInit::doPlaceholderInit(); } else { // Normally the site_stats table is initialized at install time. // Some manual construction scenarios may leave the table empty or // broken, however, for instance when importing from a dump into a // clean schema with mwdumper. wfDebug( __METHOD__ . ": initializing damaged or missing site_stats" ); SiteStatsInit::doAllAndCommit( $dbr ); } $row = self::doLoadFromDB( $lb->getConnection( DB_PRIMARY ) ); } return $row; } /** * @return int */ public static function edits() { self::load(); return (int)self::$row->ss_total_edits; } /** * @return int */ public static function articles() { self::load(); return (int)self::$row->ss_good_articles; } /** * @return int */ public static function pages() { self::load(); return (int)self::$row->ss_total_pages; } /** * @return int */ public static function users() { self::load(); return (int)self::$row->ss_users; } /** * @return int */ public static function activeUsers() { self::load(); return (int)self::$row->ss_active_users; } /** * @return int */ public static function images() { self::load(); return (int)self::$row->ss_images; } /** * Find the number of users in a given user group. * @param string $group Name of group * @return int */ public static function numberingroup( $group ) { $cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); $fname = __METHOD__; return $cache->getWithSetCallback( $cache->makeKey( 'SiteStats', 'groupcounts', $group ), $cache::TTL_HOUR, static function ( $oldValue, &$ttl, array &$setOpts ) use ( $group, $fname ) { $dbr = self::getLB()->getConnection( DB_REPLICA ); $setOpts += Database::getCacheSetOptions( $dbr ); return (int)$dbr->newSelectQueryBuilder() ->select( 'COUNT(*)' ) ->from( 'user_groups' ) ->where( [ 'ug_group' => $group, $dbr->expr( 'ug_expiry', '=', null )->or( 'ug_expiry', '>=', $dbr->timestamp() ) ] ) ->caller( $fname ) ->fetchField(); }, [ 'pcTTL' => $cache::TTL_PROC_LONG ] ); } /** * Total number of jobs in the job queue. * @return int */ public static function jobs() { $cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); return $cache->getWithSetCallback( $cache->makeKey( 'SiteStats', 'jobscount' ), $cache::TTL_MINUTE, static function ( $oldValue, &$ttl, array &$setOpts ) { try { $jobs = array_sum( MediaWikiServices::getInstance()->getJobQueueGroup()->getQueueSizes() ); } catch ( JobQueueError $e ) { $jobs = 0; } return $jobs; }, [ 'pcTTL' => $cache::TTL_PROC_LONG ] ); } /** * @param int $ns * @return int */ public static function pagesInNs( $ns ) { $cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); $fname = __METHOD__; return $cache->getWithSetCallback( $cache->makeKey( 'SiteStats', 'page-in-namespace', $ns ), $cache::TTL_HOUR, static function ( $oldValue, &$ttl, array &$setOpts ) use ( $ns, $fname ) { $dbr = self::getLB()->getConnection( DB_REPLICA ); $setOpts += Database::getCacheSetOptions( $dbr ); return (int)$dbr->newSelectQueryBuilder() ->select( 'COUNT(*)' ) ->from( 'page' ) ->where( [ 'page_namespace' => $ns ] ) ->caller( $fname )->fetchField(); }, [ 'pcTTL' => $cache::TTL_PROC_LONG ] ); } /** * @return array */ public static function selectFields() { return [ 'ss_total_edits', 'ss_good_articles', 'ss_total_pages', 'ss_users', 'ss_active_users', 'ss_images', ]; } /** * @param IReadableDatabase $db * @return stdClass|false */ private static function doLoadFromDB( IReadableDatabase $db ) { $fields = self::selectFields(); $rows = $db->newSelectQueryBuilder() ->select( $fields ) ->from( 'site_stats' ) ->caller( __METHOD__ ) ->fetchResultSet(); if ( !$rows->numRows() ) { return false; } $finalRow = new stdClass(); foreach ( $rows as $row ) { foreach ( $fields as $field ) { $finalRow->$field ??= 0; if ( $row->$field ) { $finalRow->$field += $row->$field; } } } return $finalRow; } /** * Is the provided row of site stats sensible, or should it be regenerated? * * Checks only fields which are filled by SiteStatsInit::refresh. * * @param stdClass|false $row * @return bool */ private static function isRowSensible( $row ) { if ( $row === false || $row->ss_total_pages < $row->ss_good_articles || $row->ss_total_edits < $row->ss_total_pages ) { return false; } // Now check for underflow/overflow foreach ( [ 'ss_total_edits', 'ss_good_articles', 'ss_total_pages', 'ss_users', 'ss_images', ] as $member ) { if ( $row->$member < 0 ) { return false; } } return true; } /** * @return ILoadBalancer */ private static function getLB() { return MediaWikiServices::getInstance()->getDBLoadBalancer(); } } /** @deprecated class alias since 1.41 */ class_alias( SiteStats::class, 'SiteStats' );