23use Wikimedia\Assert\Assert;
41 private const SHARDS_OFF = 1;
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'
56 public function __construct( $views, $edits, $good, $pages = 0, $users = 0 ) {
58 $this->articles = $good;
65 Assert::parameterType( __CLASS__, $update,
'$update' );
66 '@phan-var SiteStatsUpdate $update';
68 foreach ( self::COUNTERS as $field ) {
69 $this->$field += $update->$field;
86 public static function factory( array $deltas ) {
87 $update =
new self( 0, 0, 0 );
89 foreach ( $deltas as $name => $unused ) {
90 if ( !in_array( $name, self::COUNTERS ) ) {
91 throw new UnexpectedValueException( __METHOD__ .
": no field called '$name'" );
95 foreach ( self::COUNTERS as $field ) {
96 $update->$field = $deltas[$field] ?? 0;
103 $services = MediaWikiServices::getInstance();
104 $stats = $services->getStatsdDataFactory();
105 $shards = $services->getMainConfig()->get( MainConfigNames::MultiShardSiteStats ) ?
106 self::SHARDS_ON : self::SHARDS_OFF;
109 foreach ( self::COUNTERS as $type ) {
110 $delta = $this->$type;
111 if ( $delta !== 0 ) {
112 $stats->updateCount(
"site.$type", $delta );
114 $deltaByType[$type] = $delta;
118 $services->getDBLoadBalancerFactory()->getPrimaryDatabase(),
120 static function (
IDatabase $dbw, $fname ) use ( $deltaByType, $shards ) {
124 $shard = mt_rand( 1, $shards );
129 $hasNegativeDelta =
false;
130 foreach ( self::COUNTERS as $field => $type ) {
131 $delta = (int)$deltaByType[$type];
132 $initValues[$field] = $delta;
138 } elseif ( $delta < 0 ) {
139 $hasNegativeDelta =
true;
148 if ( $hasNegativeDelta ) {
150 ->update(
'site_stats' )
152 ->where( [
'ss_row_id' => $shard ] )
153 ->caller( $fname )->execute();
156 ->insertInto(
'site_stats' )
157 ->row( array_merge( [
'ss_row_id' => $shard ], $initValues ) )
158 ->onDuplicateKeyUpdate()
159 ->uniqueIndexFields( [
'ss_row_id' ] )
161 ->caller( $fname )->execute();
176 $services = MediaWikiServices::getInstance();
177 $config = $services->getMainConfig();
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' )
188 'actor_user IS NOT NULL',
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 ) ),
194 ->caller( __METHOD__ )
197 ->update(
'site_stats' )
198 ->set( [
'ss_active_users' => intval( $activeUsers ) ] )
199 ->where( [
'ss_row_id' => 1 ] )
200 ->caller( __METHOD__ )->execute();
Deferrable Update for closure/callback updates that should use auto-commit mode.
A class containing constants representing the names of configuration variables.
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.