MediaWiki REL1_41
SiteStatsUpdate.php
Go to the documentation of this file.
1<?php
23use Wikimedia\Assert\Assert;
25
31 protected $edits = 0;
33 protected $pages = 0;
35 protected $articles = 0;
37 protected $users = 0;
39 protected $images = 0;
40
41 private const SHARDS_OFF = 1;
42 public const SHARDS_ON = 10;
43
45 private const COUNTERS = [
46 'ss_total_edits' => 'edits',
47 'ss_total_pages' => 'pages',
48 'ss_good_articles' => 'articles',
49 'ss_users' => 'users',
50 'ss_images' => 'images'
51 ];
52
56 public function __construct( $views, $edits, $good, $pages = 0, $users = 0 ) {
57 $this->edits = $edits;
58 $this->articles = $good;
59 $this->pages = $pages;
60 $this->users = $users;
61 }
62
63 public function merge( MergeableUpdate $update ) {
65 Assert::parameterType( __CLASS__, $update, '$update' );
66 '@phan-var SiteStatsUpdate $update';
67
68 foreach ( self::COUNTERS as $field ) {
69 $this->$field += $update->$field;
70 }
71 }
72
86 public static function factory( array $deltas ) {
87 $update = new self( 0, 0, 0 );
88
89 foreach ( $deltas as $name => $unused ) {
90 if ( !in_array( $name, self::COUNTERS ) ) { // T187585
91 throw new UnexpectedValueException( __METHOD__ . ": no field called '$name'" );
92 }
93 }
94
95 foreach ( self::COUNTERS as $field ) {
96 $update->$field = $deltas[$field] ?? 0;
97 }
98
99 return $update;
100 }
101
102 public function doUpdate() {
103 $services = MediaWikiServices::getInstance();
104 $stats = $services->getStatsdDataFactory();
105 $shards = $services->getMainConfig()->get( MainConfigNames::MultiShardSiteStats ) ?
106 self::SHARDS_ON : self::SHARDS_OFF;
107
108 $deltaByType = [];
109 foreach ( self::COUNTERS as $type ) {
110 $delta = $this->$type;
111 if ( $delta !== 0 ) {
112 $stats->updateCount( "site.$type", $delta );
113 }
114 $deltaByType[$type] = $delta;
115 }
116
117 ( new AutoCommitUpdate(
118 $services->getDBLoadBalancerFactory()->getPrimaryDatabase(),
119 __METHOD__,
120 static function ( IDatabase $dbw, $fname ) use ( $deltaByType, $shards ) {
121 $set = [];
122 $initValues = [];
123 if ( $shards > 1 ) {
124 $shard = mt_rand( 1, $shards );
125 } else {
126 $shard = 1;
127 }
128
129 $hasNegativeDelta = false;
130 foreach ( self::COUNTERS as $field => $type ) {
131 $delta = (int)$deltaByType[$type];
132 $initValues[$field] = $delta;
133 if ( $delta > 0 ) {
134 $set[] = "$field=" . $dbw->buildGreatest(
135 [ $field => $dbw->addIdentifierQuotes( $field ) . '+' . abs( $delta ) ],
136 0
137 );
138 } elseif ( $delta < 0 ) {
139 $hasNegativeDelta = true;
140 $set[] = "$field=" . $dbw->buildGreatest(
141 [ 'new' => $dbw->addIdentifierQuotes( $field ) . '-' . abs( $delta ) ],
142 0
143 );
144 }
145 }
146
147 if ( $set ) {
148 if ( $hasNegativeDelta ) {
150 ->update( 'site_stats' )
151 ->set( $set )
152 ->where( [ 'ss_row_id' => $shard ] )
153 ->caller( $fname )->execute();
154 } else {
156 ->insertInto( 'site_stats' )
157 ->row( array_merge( [ 'ss_row_id' => $shard ], $initValues ) )
158 ->onDuplicateKeyUpdate()
159 ->uniqueIndexFields( [ 'ss_row_id' ] )
160 ->set( $set )
161 ->caller( $fname )->execute();
162 }
163 }
164 }
165 ) )->doUpdate();
166
167 // Invalidate cache used by parser functions
168 SiteStats::unload();
169 }
170
175 public static function cacheUpdate( IDatabase $dbw ) {
176 $services = MediaWikiServices::getInstance();
177 $config = $services->getMainConfig();
178
179 $dbr = $services->getDBLoadBalancerFactory()->getReplicaDatabase( false, 'vslow' );
180 # Get non-bot users than did some recent action other than making accounts.
181 # If account creation is included, the number gets inflated ~20+ fold on enwiki.
182 $activeUsers = $dbr->newSelectQueryBuilder()
183 ->select( 'COUNT(DISTINCT rc_actor)' )
184 ->from( 'recentchanges' )
185 ->join( 'actor', 'actor', 'actor_id=rc_actor' )
186 ->where( [
187 'rc_type != ' . $dbr->addQuotes( RC_EXTERNAL ), // Exclude external (Wikidata)
188 'actor_user IS NOT NULL',
189 'rc_bot' => 0,
190 'rc_log_type != ' . $dbr->addQuotes( 'newusers' ) . ' OR rc_log_type IS NULL',
191 'rc_timestamp >= ' . $dbr->addQuotes(
192 $dbr->timestamp( time() - $config->get( MainConfigNames::ActiveUserDays ) * 24 * 3600 ) ),
193 ] )
194 ->caller( __METHOD__ )
195 ->fetchField();
197 ->update( 'site_stats' )
198 ->set( [ 'ss_active_users' => intval( $activeUsers ) ] )
199 ->where( [ 'ss_row_id' => 1 ] )
200 ->caller( __METHOD__ )->execute();
201
202 // Invalid cache used by parser functions
203 SiteStats::unload();
204
205 return $activeUsers;
206 }
207}
const RC_EXTERNAL
Definition Defines.php:119
Deferrable Update for closure/callback updates that should use auto-commit mode.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
Static accessor class for site_stats and related things.
Definition SiteStats.php:36
Class for handling updates to the site_stats table.
static factory(array $deltas)
doUpdate()
Perform the actual work.
__construct( $views, $edits, $good, $pages=0, $users=0)
static cacheUpdate(IDatabase $dbw)
merge(MergeableUpdate $update)
Merge this enqueued update with a new MergeableUpdate of the same qualified class name.
Interface that deferrable updates should implement.
Interface that deferrable updates can implement to signal that updates can be combined.
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:36
newUpdateQueryBuilder()
Get an UpdateQueryBuilder bound to this connection.
newInsertQueryBuilder()
Get an InsertQueryBuilder bound to this connection.
buildGreatest( $fields, $values)
Build a GREATEST function statement comparing columns/values.
addIdentifierQuotes( $s)
Escape a SQL identifier (e.g.