MediaWiki  master
SiteStatsUpdate.php
Go to the documentation of this file.
1 <?php
22 use 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  private 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 ) ?
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:118
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