3 * Accessors and mutators for the site-wide statistics.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
23 use Wikimedia\Rdbms\Database
;
24 use Wikimedia\Rdbms\IDatabase
;
25 use MediaWiki\MediaWikiServices
;
26 use Wikimedia\Rdbms\LoadBalancer
;
29 * Static accessor class for site_stats and related things
36 * Trigger a reload next time a field is accessed
38 public static function unload() {
42 protected static function load() {
43 if ( self
::$row === null ) {
44 self
::$row = self
::loadAndLazyInit();
51 protected static function loadAndLazyInit() {
52 $config = MediaWikiServices
::getInstance()->getMainConfig();
55 $dbr = $lb->getConnectionRef( DB_REPLICA
);
56 wfDebug( __METHOD__
. ": reading site_stats from replica DB\n" );
57 $row = self
::doLoadFromDB( $dbr );
59 if ( !self
::isRowSane( $row ) && $lb->hasOrMadeRecentMasterChanges() ) {
60 // Might have just been initialized during this request? Underflow?
61 wfDebug( __METHOD__
. ": site_stats damaged or missing on replica DB\n" );
62 $row = self
::doLoadFromDB( $lb->getConnectionRef( DB_MASTER
) );
65 if ( !self
::isRowSane( $row ) ) {
66 if ( $config->get( 'MiserMode' ) ) {
67 // Start off with all zeroes, assuming that this is a new wiki or any
68 // repopulations where done manually via script.
69 SiteStatsInit
::doPlaceholderInit();
71 // Normally the site_stats table is initialized at install time.
72 // Some manual construction scenarios may leave the table empty or
73 // broken, however, for instance when importing from a dump into a
74 // clean schema with mwdumper.
75 wfDebug( __METHOD__
. ": initializing damaged or missing site_stats\n" );
76 SiteStatsInit
::doAllAndCommit( $dbr );
79 $row = self
::doLoadFromDB( $lb->getConnectionRef( DB_MASTER
) );
82 if ( !self
::isRowSane( $row ) ) {
83 wfDebug( __METHOD__
. ": site_stats persistently nonsensical o_O\n" );
84 // Always return a row-like object
85 $row = self
::salvageInsaneRow( $row );
94 public static function edits() {
97 return (int)self
::$row->ss_total_edits
;
103 public static function articles() {
106 return (int)self
::$row->ss_good_articles
;
112 public static function pages() {
115 return (int)self
::$row->ss_total_pages
;
121 public static function users() {
124 return (int)self
::$row->ss_users
;
130 public static function activeUsers() {
133 return (int)self
::$row->ss_active_users
;
139 public static function images() {
142 return (int)self
::$row->ss_images
;
146 * Find the number of users in a given user group.
147 * @param string $group Name of group
150 public static function numberingroup( $group ) {
151 $cache = MediaWikiServices
::getInstance()->getMainWANObjectCache();
154 return $cache->getWithSetCallback(
155 $cache->makeKey( 'SiteStats', 'groupcounts', $group ),
157 function ( $oldValue, &$ttl, array &$setOpts ) use ( $group, $fname ) {
158 $dbr = self
::getLB()->getConnectionRef( DB_REPLICA
);
159 $setOpts +
= Database
::getCacheSetOptions( $dbr );
161 return (int)$dbr->selectField(
165 'ug_group' => $group,
166 'ug_expiry IS NULL OR ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() )
171 [ 'pcTTL' => $cache::TTL_PROC_LONG
]
176 * Total number of jobs in the job queue.
179 public static function jobs() {
180 $cache = MediaWikiServices
::getInstance()->getMainWANObjectCache();
182 return $cache->getWithSetCallback(
183 $cache->makeKey( 'SiteStats', 'jobscount' ),
185 function ( $oldValue, &$ttl, array &$setOpts ) {
187 $jobs = array_sum( JobQueueGroup
::singleton()->getQueueSizes() );
188 } catch ( JobQueueError
$e ) {
193 [ 'pcTTL' => $cache::TTL_PROC_LONG
]
201 public static function pagesInNs( $ns ) {
202 $cache = MediaWikiServices
::getInstance()->getMainWANObjectCache();
205 return $cache->getWithSetCallback(
206 $cache->makeKey( 'SiteStats', 'page-in-namespace', $ns ),
208 function ( $oldValue, &$ttl, array &$setOpts ) use ( $ns, $fname ) {
209 $dbr = self
::getLB()->getConnectionRef( DB_REPLICA
);
210 $setOpts +
= Database
::getCacheSetOptions( $dbr );
212 return (int)$dbr->selectField(
215 [ 'page_namespace' => $ns ],
219 [ 'pcTTL' => $cache::TTL_PROC_LONG
]
226 public static function selectFields() {
238 * @param IDatabase $db
239 * @return stdClass|bool
241 private static function doLoadFromDB( IDatabase
$db ) {
242 return $db->selectRow(
244 self
::selectFields(),
245 [ 'ss_row_id' => 1 ],
251 * Is the provided row of site stats sane, or should it be regenerated?
253 * Checks only fields which are filled by SiteStatsInit::refresh.
255 * @param bool|object $row
258 private static function isRowSane( $row ) {
260 ||
$row->ss_total_pages
< $row->ss_good_articles
261 ||
$row->ss_total_edits
< $row->ss_total_pages
265 // Now check for underflow/overflow
273 if ( $row->$member < 0 ) {
282 * @param stdClass|bool $row
285 private static function salvageInsaneRow( $row ) {
286 $map = $row ?
(array)$row : [];
287 // Fill in any missing values with zero
288 $map +
= array_fill_keys( self
::selectFields(), 0 );
289 // Convert negative values to zero
290 foreach ( $map as $field => $value ) {
291 $map[$field] = max( 0, $value );
298 * @return LoadBalancer
300 private static function getLB() {
301 return MediaWikiServices
::getInstance()->getDBLoadBalancer();