MediaWiki  REL1_24
SiteStatsUpdate.php
Go to the documentation of this file.
00001 <?php
00024 class SiteStatsUpdate implements DeferrableUpdate {
00026     protected $views = 0;
00027 
00029     protected $edits = 0;
00030 
00032     protected $pages = 0;
00033 
00035     protected $articles = 0;
00036 
00038     protected $users = 0;
00039 
00041     protected $images = 0;
00042 
00043     // @todo deprecate this constructor
00044     function __construct( $views, $edits, $good, $pages = 0, $users = 0 ) {
00045         $this->views = $views;
00046         $this->edits = $edits;
00047         $this->articles = $good;
00048         $this->pages = $pages;
00049         $this->users = $users;
00050     }
00051 
00056     public static function factory( array $deltas ) {
00057         $update = new self( 0, 0, 0 );
00058 
00059         $fields = array( 'views', 'edits', 'pages', 'articles', 'users', 'images' );
00060         foreach ( $fields as $field ) {
00061             if ( isset( $deltas[$field] ) && $deltas[$field] ) {
00062                 $update->$field = $deltas[$field];
00063             }
00064         }
00065 
00066         return $update;
00067     }
00068 
00069     public function doUpdate() {
00070         global $wgSiteStatsAsyncFactor;
00071 
00072         $rate = $wgSiteStatsAsyncFactor; // convenience
00073         // If set to do so, only do actual DB updates 1 every $rate times.
00074         // The other times, just update "pending delta" values in memcached.
00075         if ( $rate && ( $rate < 0 || mt_rand( 0, $rate - 1 ) != 0 ) ) {
00076             $this->doUpdatePendingDeltas();
00077         } else {
00078             // Need a separate transaction because this a global lock
00079             wfGetDB( DB_MASTER )->onTransactionIdle( array( $this, 'tryDBUpdateInternal' ) );
00080         }
00081     }
00082 
00086     public function tryDBUpdateInternal() {
00087         global $wgSiteStatsAsyncFactor;
00088 
00089         $dbw = wfGetDB( DB_MASTER );
00090         $lockKey = wfMemcKey( 'site_stats' ); // prepend wiki ID
00091         $pd = array();
00092         if ( $wgSiteStatsAsyncFactor ) {
00093             // Lock the table so we don't have double DB/memcached updates
00094             if ( !$dbw->lockIsFree( $lockKey, __METHOD__ )
00095                 || !$dbw->lock( $lockKey, __METHOD__, 1 ) // 1 sec timeout
00096             ) {
00097                 $this->doUpdatePendingDeltas();
00098 
00099                 return;
00100             }
00101             $pd = $this->getPendingDeltas();
00102             // Piggy-back the async deltas onto those of this stats update....
00103             $this->views += ( $pd['ss_total_views']['+'] - $pd['ss_total_views']['-'] );
00104             $this->edits += ( $pd['ss_total_edits']['+'] - $pd['ss_total_edits']['-'] );
00105             $this->articles += ( $pd['ss_good_articles']['+'] - $pd['ss_good_articles']['-'] );
00106             $this->pages += ( $pd['ss_total_pages']['+'] - $pd['ss_total_pages']['-'] );
00107             $this->users += ( $pd['ss_users']['+'] - $pd['ss_users']['-'] );
00108             $this->images += ( $pd['ss_images']['+'] - $pd['ss_images']['-'] );
00109         }
00110 
00111         // Build up an SQL query of deltas and apply them...
00112         $updates = '';
00113         $this->appendUpdate( $updates, 'ss_total_views', $this->views );
00114         $this->appendUpdate( $updates, 'ss_total_edits', $this->edits );
00115         $this->appendUpdate( $updates, 'ss_good_articles', $this->articles );
00116         $this->appendUpdate( $updates, 'ss_total_pages', $this->pages );
00117         $this->appendUpdate( $updates, 'ss_users', $this->users );
00118         $this->appendUpdate( $updates, 'ss_images', $this->images );
00119         if ( $updates != '' ) {
00120             $dbw->update( 'site_stats', array( $updates ), array(), __METHOD__ );
00121         }
00122 
00123         if ( $wgSiteStatsAsyncFactor ) {
00124             // Decrement the async deltas now that we applied them
00125             $this->removePendingDeltas( $pd );
00126             // Commit the updates and unlock the table
00127             $dbw->unlock( $lockKey, __METHOD__ );
00128         }
00129     }
00130 
00135     public static function cacheUpdate( $dbw ) {
00136         global $wgActiveUserDays;
00137         $dbr = wfGetDB( DB_SLAVE, array( 'SpecialStatistics', 'vslow' ) );
00138         # Get non-bot users than did some recent action other than making accounts.
00139         # If account creation is included, the number gets inflated ~20+ fold on enwiki.
00140         $activeUsers = $dbr->selectField(
00141             'recentchanges',
00142             'COUNT( DISTINCT rc_user_text )',
00143             array(
00144                 'rc_user != 0',
00145                 'rc_bot' => 0,
00146                 'rc_log_type != ' . $dbr->addQuotes( 'newusers' ) . ' OR rc_log_type IS NULL',
00147                 'rc_timestamp >= ' . $dbr->addQuotes( $dbr->timestamp( wfTimestamp( TS_UNIX )
00148                     - $wgActiveUserDays * 24 * 3600 ) ),
00149             ),
00150             __METHOD__
00151         );
00152         $dbw->update(
00153             'site_stats',
00154             array( 'ss_active_users' => intval( $activeUsers ) ),
00155             array( 'ss_row_id' => 1 ),
00156             __METHOD__
00157         );
00158 
00159         return $activeUsers;
00160     }
00161 
00162     protected function doUpdatePendingDeltas() {
00163         $this->adjustPending( 'ss_total_views', $this->views );
00164         $this->adjustPending( 'ss_total_edits', $this->edits );
00165         $this->adjustPending( 'ss_good_articles', $this->articles );
00166         $this->adjustPending( 'ss_total_pages', $this->pages );
00167         $this->adjustPending( 'ss_users', $this->users );
00168         $this->adjustPending( 'ss_images', $this->images );
00169     }
00170 
00176     protected function appendUpdate( &$sql, $field, $delta ) {
00177         if ( $delta ) {
00178             if ( $sql ) {
00179                 $sql .= ',';
00180             }
00181             if ( $delta < 0 ) {
00182                 $sql .= "$field=$field-" . abs( $delta );
00183             } else {
00184                 $sql .= "$field=$field+" . abs( $delta );
00185             }
00186         }
00187     }
00188 
00194     private function getTypeCacheKey( $type, $sign ) {
00195         return wfMemcKey( 'sitestatsupdate', 'pendingdelta', $type, $sign );
00196     }
00197 
00204     protected function adjustPending( $type, $delta ) {
00205         global $wgMemc;
00206 
00207         if ( $delta < 0 ) { // decrement
00208             $key = $this->getTypeCacheKey( $type, '-' );
00209         } else { // increment
00210             $key = $this->getTypeCacheKey( $type, '+' );
00211         }
00212 
00213         $magnitude = abs( $delta );
00214         if ( !$wgMemc->incr( $key, $magnitude ) ) { // not there?
00215             if ( !$wgMemc->add( $key, $magnitude ) ) { // race?
00216                 $wgMemc->incr( $key, $magnitude );
00217             }
00218         }
00219     }
00220 
00225     protected function getPendingDeltas() {
00226         global $wgMemc;
00227 
00228         $pending = array();
00229         foreach ( array( 'ss_total_views', 'ss_total_edits',
00230             'ss_good_articles', 'ss_total_pages', 'ss_users', 'ss_images' ) as $type
00231         ) {
00232             // Get pending increments and pending decrements
00233             $pending[$type]['+'] = (int)$wgMemc->get( $this->getTypeCacheKey( $type, '+' ) );
00234             $pending[$type]['-'] = (int)$wgMemc->get( $this->getTypeCacheKey( $type, '-' ) );
00235         }
00236 
00237         return $pending;
00238     }
00239 
00244     protected function removePendingDeltas( array $pd ) {
00245         global $wgMemc;
00246 
00247         foreach ( $pd as $type => $deltas ) {
00248             foreach ( $deltas as $sign => $magnitude ) {
00249                 // Lower the pending counter now that we applied these changes
00250                 $wgMemc->decr( $this->getTypeCacheKey( $type, $sign ), $magnitude );
00251             }
00252         }
00253     }
00254 }