12use UnexpectedValueException;
13use Wikimedia\Assert\Assert;
32 private const SHARDS_OFF = 1;
36 private const COUNTERS = [
37 'ss_total_edits' =>
'edits',
38 'ss_total_pages' =>
'pages',
39 'ss_good_articles' =>
'articles',
40 'ss_users' =>
'users',
41 'ss_images' =>
'images'
49 $this->articles = $good;
56 Assert::parameterType( __CLASS__, $update,
'$update' );
57 '@phan-var SiteStatsUpdate $update';
59 foreach ( self::COUNTERS as $field ) {
60 $this->$field += $update->$field;
77 public static function factory( array $deltas ) {
78 $update =
new self( 0, 0, 0 );
80 foreach ( $deltas as $name => $unused ) {
81 if ( !in_array( $name, self::COUNTERS ) ) {
82 throw new UnexpectedValueException( __METHOD__ .
": no field called '$name'" );
86 foreach ( self::COUNTERS as $field ) {
87 $update->$field = $deltas[$field] ?? 0;
95 $stats = $services->getStatsFactory();
101 foreach ( self::COUNTERS as $type ) {
102 $delta = $this->$type;
103 $deltaByType[$type] = $delta;
112 $stats->getCounter(
'site_stats_total' )
113 ->setLabel(
'engagement', $type )
114 ->incrementBy( $delta );
119 $services->getConnectionProvider()->getPrimaryDatabase(),
121 static function (
IDatabase $dbw, $fname ) use ( $deltaByType, $shards ) {
125 $shard = mt_rand( 1, $shards );
130 $hasNegativeDelta =
false;
131 foreach ( self::COUNTERS as $field => $type ) {
132 $delta = (int)$deltaByType[$type];
133 $initValues[$field] = $delta;
136 } elseif ( $delta < 0 ) {
137 $hasNegativeDelta =
true;
141 ) .
'-' . abs( $delta ) );
146 if ( $hasNegativeDelta ) {
148 ->update(
'site_stats' )
150 ->where( [
'ss_row_id' => $shard ] )
151 ->caller( $fname )->execute();
154 ->insertInto(
'site_stats' )
155 ->row( $initValues + [
'ss_row_id' => $shard ] )
156 ->onDuplicateKeyUpdate()
157 ->uniqueIndexFields( [
'ss_row_id' ] )
159 ->caller( $fname )->execute();
175 $config = $services->getMainConfig();
177 $rcl = $services->getRecentChangeLookup();
178 $dbr = $services->getConnectionProvider()->getReplicaDatabase(
false,
'vslow' );
179 # Get non-bot users that did some recent action other than making accounts.
180 # If account creation is included, the number gets inflated ~20+ fold on enwiki.
181 $activeUsers = $dbr->newSelectQueryBuilder()
182 ->select(
'COUNT(DISTINCT rc_actor)' )
183 ->from(
'recentchanges' )
184 ->join(
'actor',
'actor',
'actor_id=rc_actor' )
186 $dbr->expr(
'rc_source',
'=', $rcl->getPrimarySources() ),
187 $dbr->expr(
'actor_user',
'!=',
null ),
188 $dbr->expr(
'rc_bot',
'=', 0 ),
189 $dbr->expr(
'rc_log_type',
'!=',
'newusers' )->or(
'rc_log_type',
'=',
null ),
190 $dbr->expr(
'rc_timestamp',
'>=',
193 ->caller( __METHOD__ )
196 ->update(
'site_stats' )
197 ->set( [
'ss_active_users' => intval( $activeUsers ) ] )
198 ->where( [
'ss_row_id' => 1 ] )
199 ->caller( __METHOD__ )->execute();
209class_alias( SiteStatsUpdate::class,
'SiteStatsUpdate' );
A class containing constants representing the names of configuration variables.
const MultiShardSiteStats
Name constant for the MultiShardSiteStats setting, for use with Config::get()
const ActiveUserDays
Name constant for the ActiveUserDays setting, for use with Config::get()