MediaWiki REL1_39
SiteStatsUpdate.php
Go to the documentation of this file.
1<?php
22use Wikimedia\Assert\Assert;
24
30 protected $edits = 0;
32 protected $pages = 0;
34 protected $articles = 0;
36 protected $users = 0;
38 protected $images = 0;
39
40 private const SHARDS_OFF = 1;
41 public const SHARDS_ON = 10;
42
44 private const COUNTERS = [
45 'ss_total_edits' => 'edits',
46 'ss_total_pages' => 'pages',
47 'ss_good_articles' => 'articles',
48 'ss_users' => 'users',
49 'ss_images' => 'images'
50 ];
51
55 public function __construct( $views, $edits, $good, $pages = 0, $users = 0 ) {
56 $this->edits = $edits;
57 $this->articles = $good;
58 $this->pages = $pages;
59 $this->users = $users;
60 }
61
62 public function merge( MergeableUpdate $update ) {
64 Assert::parameterType( __CLASS__, $update, '$update' );
65 '@phan-var SiteStatsUpdate $update';
66
67 foreach ( self::COUNTERS as $field ) {
68 $this->$field += $update->$field;
69 }
70 }
71
85 public static function factory( array $deltas ) {
86 $update = new self( 0, 0, 0 );
87
88 foreach ( $deltas as $name => $unused ) {
89 if ( !in_array( $name, self::COUNTERS ) ) { // T187585
90 throw new UnexpectedValueException( __METHOD__ . ": no field called '$name'" );
91 }
92 }
93
94 foreach ( self::COUNTERS as $field ) {
95 $update->$field = $deltas[$field] ?? 0;
96 }
97
98 return $update;
99 }
100
101 public function doUpdate() {
102 $services = MediaWikiServices::getInstance();
103 $stats = $services->getStatsdDataFactory();
104 $shards = $services->getMainConfig()->get( MainConfigNames::MultiShardSiteStats ) ?
105 self::SHARDS_ON : self::SHARDS_OFF;
106
107 $deltaByType = [];
108 foreach ( self::COUNTERS as $type ) {
109 $delta = $this->$type;
110 if ( $delta !== 0 ) {
111 $stats->updateCount( "site.$type", $delta );
112 }
113 $deltaByType[$type] = $delta;
114 }
115
116 ( new AutoCommitUpdate(
117 $services->getDBLoadBalancer()->getConnectionRef( DB_PRIMARY ),
118 __METHOD__,
119 static function ( IDatabase $dbw, $fname ) use ( $deltaByType, $shards ) {
120 $set = [];
121 $initValues = [];
122 if ( $shards > 1 ) {
123 $shard = mt_rand( 1, $shards );
124 } else {
125 $shard = 1;
126 }
127
128 $hasNegativeDelta = false;
129 foreach ( self::COUNTERS as $field => $type ) {
130 $delta = (int)$deltaByType[$type];
131 $initValues[$field] = $delta;
132 if ( $delta > 0 ) {
133 $set[] = "$field=" . $dbw->buildGreatest(
134 [ $field => $dbw->addIdentifierQuotes( $field ) . '+' . abs( $delta ) ],
135 0
136 );
137 } elseif ( $delta < 0 ) {
138 $hasNegativeDelta = true;
139 $set[] = "$field=" . $dbw->buildGreatest(
140 [ 'new' => $dbw->addIdentifierQuotes( $field ) . '-' . abs( $delta ) ],
141 0
142 );
143 }
144 }
145
146 if ( $set ) {
147 if ( $hasNegativeDelta ) {
148 $dbw->update( 'site_stats', $set, [ 'ss_row_id' => $shard ], $fname );
149 } else {
150 $dbw->upsert(
151 'site_stats',
152 array_merge( [ 'ss_row_id' => $shard ], $initValues ),
153 'ss_row_id',
154 $set,
155 $fname
156 );
157 }
158 }
159 }
160 ) )->doUpdate();
161
162 // Invalidate cache used by parser functions
164 }
165
170 public static function cacheUpdate( IDatabase $dbw ) {
171 $services = MediaWikiServices::getInstance();
172 $config = $services->getMainConfig();
173
174 $dbr = $services->getDBLoadBalancer()->getConnectionRef( DB_REPLICA, 'vslow' );
175 # Get non-bot users than did some recent action other than making accounts.
176 # If account creation is included, the number gets inflated ~20+ fold on enwiki.
177 $activeUsers = $dbr->newSelectQueryBuilder()
178 ->select( 'COUNT(DISTINCT rc_actor)' )
179 ->from( 'recentchanges' )
180 ->join( 'actor', 'actor', 'actor_id=rc_actor' )
181 ->where( [
182 'rc_type != ' . $dbr->addQuotes( RC_EXTERNAL ), // Exclude external (Wikidata)
183 'actor_user IS NOT NULL',
184 'rc_bot' => 0,
185 'rc_log_type != ' . $dbr->addQuotes( 'newusers' ) . ' OR rc_log_type IS NULL',
186 'rc_timestamp >= ' . $dbr->addQuotes(
187 $dbr->timestamp( time() - $config->get( MainConfigNames::ActiveUserDays ) * 24 * 3600 ) ),
188 ] )
189 ->caller( __METHOD__ )
190 ->fetchField();
191 $dbw->update(
192 'site_stats',
193 [ 'ss_active_users' => intval( $activeUsers ) ],
194 [ 'ss_row_id' => 1 ],
195 __METHOD__
196 );
197
198 // Invalid cache used by parser functions
200
201 return $activeUsers;
202 }
203}
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.
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.
static unload()
Trigger a reload next time a field is accessed.
Definition SiteStats.php:39
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:39
update( $table, $set, $conds, $fname=__METHOD__, $options=[])
Update all rows in a table that match a given condition.
upsert( $table, array $rows, $uniqueKeys, array $set, $fname=__METHOD__)
Upsert row(s) into a table, in the provided order, while updating conflicting rows.
buildGreatest( $fields, $values)
Build a GREATEST function statement comparing columns/values.
addIdentifierQuotes( $s)
Escape a SQL identifier (e.g.
const DB_REPLICA
Definition defines.php:26
const DB_PRIMARY
Definition defines.php:28