MediaWiki
REL1_24
|
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 }