r107927 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r107926‎ | r107927 | r107928 >
Date:19:28, 3 January 2012
Author:ialex
Status:reverted (Comments)
Tags:
Comment:
Give Title a decent loading mechanism:
* Added Title::load() to factorise common code that load member variables instead of having each accessor doing it own loading system for its related member variable
* Removed usage of LinkCache::addLinkObj() to do the database query and do this directly in Title::load(). This allows to select the complete database row and populate all member variables; previously, requesting a field not stored in LinkCache (using getCount(), getTouched() or isNewPage()) results in two database query, one to load LinkCache data and the second to load the requested field; now there'll be only one query.
* Added Title::FIELD_IN_LINKCACHE and Title::FIELD_NOT_IN_LINKCACHE to specify whether the requested field is stored in LinkCache or not. LinkCache will be used if possible (i.e. Title::FIELD_IN_LINKCACHE is passed), otherwise a DB query to select the complete row is issued.
* Made Title::loadFromRow() save the row to LinkCache if possible.
* Added $wasFromMaster parameter to Title::loadFromRow() to tell that method whether the row was loaded from the master database or not and pass it from WikiPage::loadPageData()
* Added Title::GAID_USE_MASTER in addition to Title::GAID_FOR_UPDATE to get the row from the master database without having to do a SELECT FROM UPDATE query
* Added Title::selectFields() method to return the fields to select to given Title::loadFromRow() (and methods using it such as Title::newFromRow()) a complete row
* Made Title::$mCounter private since it has only been added recently (in r105790)
* Mark the object as loaded if Title::resetArticleID() is called with as new ID as 0
Modified paths:
  • /trunk/phase3/includes/Title.php (modified) (history)
  • /trunk/phase3/includes/WikiPage.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/Title.php
@@ -43,11 +43,27 @@
4444
4545 /**
4646 * Used to be GAID_FOR_UPDATE define. Used with getArticleID() and friends
47 - * to use the master DB
 47+ * to SELECT FOR UPDATE
4848 */
4949 const GAID_FOR_UPDATE = 1;
5050
5151 /**
 52+ * Used with getArticleID() and friends to load the object from the master
 53+ * database
 54+ */
 55+ const GAID_USE_MASTER = 2;
 56+
 57+ /**
 58+ * For use in load(). Field is available in LinkCache.
 59+ */
 60+ const FIELD_IN_LINKCACHE = 1;
 61+
 62+ /**
 63+ * For use in load(). Field is not available in LinkCache.
 64+ */
 65+ const FIELD_NOT_IN_LINKCACHE = 2;
 66+
 67+ /**
5268 * @name Private member variables
5369 * Please use the accessor functions instead.
5470 * @private
@@ -61,9 +77,11 @@
6278 var $mNamespace = NS_MAIN; // /< Namespace index, i.e. one of the NS_xxxx constants
6379 var $mInterwiki = ''; // /< Interwiki prefix (or null string)
6480 var $mFragment; // /< Title fragment (i.e. the bit after the #)
 81+ private $mLoadedLevel = 0; // /<
 82+ private $mLoadedFromMaster = false;
6583 var $mArticleID = -1; // /< Article ID, fetched from the link cache on demand
6684 var $mLatestID = false; // /< ID of most recent revision
67 - var $mCounter = -1; // /< Number of times this page has been viewed (-1 means "not loaded")
 85+ private $mCounter = false; // /< Number of times this page has been viewed (-1 means "not loaded")
6886 private $mTouched; // /< Timestamp of the last time this page was touched
6987 private $mIsNew; // /< Whether this is a "new page" (i.e. it has only one revision)
7088 private $mEstimateRevisions; // /< Estimated number of revisions; null of not loaded
@@ -204,11 +222,11 @@
205223 * Create a new Title from an article ID
206224 *
207225 * @param $id Int the page_id corresponding to the Title to create
208 - * @param $flags Int use Title::GAID_FOR_UPDATE to use master
 226+ * @param $flags Int use Title::GAID_USE_MASTER to use master
209227 * @return Title the new object, or NULL on an error
210228 */
211229 public static function newFromID( $id, $flags = 0 ) {
212 - $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
 230+ $db = $flags ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
213231 $row = $db->selectRow( 'page', '*', array( 'page_id' => $id ), __METHOD__ );
214232 if ( $row !== false ) {
215233 $title = Title::newFromRow( $row );
@@ -230,17 +248,8 @@
231249 }
232250 $dbr = wfGetDB( DB_SLAVE );
233251
234 - $res = $dbr->select(
235 - 'page',
236 - array(
237 - 'page_namespace', 'page_title', 'page_id',
238 - 'page_len', 'page_is_redirect', 'page_latest',
239 - 'page_counter', 'page_touched', 'page_is_new',
240 - 'page_restrictions',
241 - ),
242 - array( 'page_id' => $ids ),
243 - __METHOD__
244 - );
 252+ $res = $dbr->select( 'page', self::selectFields(),
 253+ array( 'page_id' => $ids ), __METHOD__ );
245254
246255 $titles = array();
247256 foreach ( $res as $row ) {
@@ -266,26 +275,67 @@
267276 * If false is given, the title will be treated as non-existing.
268277 *
269278 * @param $row Object|false database row
 279+ * @param $wasFromMaster bool: whether the row was loaded from the master
 280+ * database
270281 * @return void
271282 */
272 - public function loadFromRow( $row ) {
 283+ public function loadFromRow( $row, $wasFromMaster = false ) {
273284 if ( $row ) { // page found
274 - if ( isset( $row->page_id ) )
 285+ $cacheLevel = self::FIELD_NOT_IN_LINKCACHE;
 286+
 287+ # Items that cannot be stored in LinkCache
 288+ # If one (or more) of these field is missing, the row can still
 289+ # be stored in LinkCache
 290+ if ( isset( $row->page_counter ) ) {
 291+ $this->mCounter = (int)$row->page_counter;
 292+ } else {
 293+ $cacheLevel = self::FIELD_IN_LINKCACHE;
 294+ }
 295+ if ( isset( $row->page_touched ) ) {
 296+ $this->mTouched = $row->page_touched;
 297+ } else {
 298+ $cacheLevel = self::FIELD_IN_LINKCACHE;
 299+ }
 300+ if ( isset( $row->page_is_new ) ) {
 301+ $this->mIsNew = (bool)$row->page_is_new;
 302+ } else {
 303+ $cacheLevel = self::FIELD_IN_LINKCACHE;
 304+ }
 305+ if ( isset( $row->page_restrictions ) ) {
 306+ $this->mOldRestrictions = $row->page_restrictions;
 307+ } else {
 308+ $cacheLevel = self::FIELD_IN_LINKCACHE;
 309+ }
 310+
 311+ # Items that can be stored in LinkCache
 312+ # If one (or more) of these field is missing, the row cannot
 313+ # be stored in LinkCache
 314+ if ( isset( $row->page_id ) ) {
275315 $this->mArticleID = (int)$row->page_id;
276 - if ( isset( $row->page_len ) )
 316+ } else {
 317+ $cacheLevel = 0;
 318+ }
 319+ if ( isset( $row->page_len ) ) {
277320 $this->mLength = (int)$row->page_len;
278 - if ( isset( $row->page_is_redirect ) )
 321+ } else {
 322+ $cacheLevel = 0;
 323+ }
 324+ if ( isset( $row->page_is_redirect ) ) {
279325 $this->mRedirect = (bool)$row->page_is_redirect;
280 - if ( isset( $row->page_latest ) )
 326+ } else {
 327+ $cacheLevel = 0;
 328+ }
 329+ if ( isset( $row->page_latest ) ) {
281330 $this->mLatestID = (int)$row->page_latest;
282 - if ( isset( $row->page_counter ) )
283 - $this->mCounter = (int)$row->page_counter;
284 - if ( isset( $row->page_touched ) )
285 - $this->mTouched = $row->page_touched;
286 - if ( isset( $row->page_is_new ) )
287 - $this->mIsNew = (bool)$row->page_is_new;
288 - if ( isset( $row->page_restrictions ) )
289 - $this->mOldRestrictions = $row->page_restrictions;
 331+ } else {
 332+ $cacheLevel = 0;
 333+ }
 334+
 335+ $this->mLoadedLevel = $cacheLevel;
 336+ if ( $cacheLevel > 0 ) {
 337+ # We have all fields required by LinkCache
 338+ LinkCache::singleton()->addGoodLinkObjFromRow( $this, $row );
 339+ }
290340 } else { // page not found
291341 $this->mArticleID = 0;
292342 $this->mLength = 0;
@@ -295,7 +345,10 @@
296346 $this->mTouched = '19700101000000';
297347 $this->mIsNew = false;
298348 $this->mOldRestrictions = false;
 349+ $this->mLoadedLevel = 2;
 350+ LinkCache::singleton()->addBadLinkObj( $this );
299351 }
 352+ $this->mLoadedFromMaster = $wasFromMaster;
300353 }
301354
302355 /**
@@ -484,6 +537,27 @@
485538 }
486539
487540 /**
 541+ * Get the fields of the `page` table that have to be select if you want
 542+ * to give a complete row to loadFromRow()
 543+ *
 544+ * @return array
 545+ */
 546+ public static function selectFields() {
 547+ return array(
 548+ 'page_namespace',
 549+ 'page_title',
 550+ 'page_id',
 551+ 'page_len',
 552+ 'page_is_redirect',
 553+ 'page_latest',
 554+ 'page_counter',
 555+ 'page_touched',
 556+ 'page_is_new',
 557+ 'page_restrictions',
 558+ );
 559+ }
 560+
 561+ /**
488562 * Get a regex character class describing the legal characters in a link
489563 *
490564 * @return String the list of characters, not delimited
@@ -1504,6 +1578,82 @@
15051579 }
15061580
15071581 /**
 1582+ * Load field from database into this object
 1583+ *
 1584+ * @param $level int, may be on of the following values:
 1585+ * - self::FIELD_IN_LINKCACHE: the field can be retrived from the LinkCache
 1586+ * - self::FIELD_NOT_IN_LINKCACHE: the field is not stored in LinkCache and
 1587+ * must be loaded from the database
 1588+ * @param $flags int, may be on of the following values:
 1589+ * - 0: to use a slave connection
 1590+ * - self::GAID_USE_MASTER to use a master connection
 1591+ * - self::GAID_FOR_UPDATE to SELECT FROM UPDATE from a master connection
 1592+ */
 1593+ private function load( $level, $flags = 0 ) {
 1594+ global $wgAntiLockFlags;
 1595+
 1596+ if ( !$this->canExist() ) {
 1597+ return;
 1598+ }
 1599+
 1600+ // Check whether the wanted item is already loaded
 1601+ // and from where it is requested.
 1602+ // If $flags is self::GAID_FOR_UPDATE, it will always be reloaded.
 1603+ if ( $level <= $this->mLoadedLevel && ( $flags === 0 ||
 1604+ ( $flags === self::GAID_USE_MASTER && $this->mLoadedFromMaster ) ) )
 1605+ {
 1606+ return;
 1607+ }
 1608+
 1609+ $linkCache = LinkCache::singleton();
 1610+
 1611+ # Only use the LinkCache if we can load from a slave database
 1612+ if ( $flags === 0 ) {
 1613+
 1614+ # If the LinkCache says the page doesn't exist; we can load all fields
 1615+ if ( $linkCache->isBadLink( $this->getPrefixedDBkey() ) ) {
 1616+ $this->loadFromRow( false );
 1617+ return;
 1618+ }
 1619+
 1620+ # For existing pages we can only load some fields
 1621+ if ( $level === self::FIELD_IN_LINKCACHE ) {
 1622+ $id = $linkCache->getGoodLinkID( $this->getPrefixedDBkey() );
 1623+ if ( $id ) {
 1624+ $this->mArticleID = $id;
 1625+ $this->mRedirect = (bool)$linkCache->getGoodLinkFieldObj( $this, 'redirect' );
 1626+ $this->mLength = (int)$linkCache->getGoodLinkFieldObj( $this, 'length' );
 1627+ $this->mLatestID = (int)$linkCache->getGoodLinkFieldObj( $this, 'revision' );
 1628+ $this->mLoadedLevel = 1;
 1629+ $this->mLoadedFromMaster = false;
 1630+ return;
 1631+ }
 1632+ }
 1633+ }
 1634+
 1635+ # Just in case it's already loaded from a slave database
 1636+ $linkCache->clearLink( $this );
 1637+
 1638+ # No success using LinkCache, we need to use the database
 1639+ # In this case we load the complete row regardless of $level
 1640+ $options = array();
 1641+ if ( $flags === 0 ) {
 1642+ $db = wfGetDB( DB_SLAVE );
 1643+ $this->mLoadedFromMaster = false;
 1644+ } else {
 1645+ $db = wfGetDB( DB_MASTER );
 1646+ if ( $flags == self::GAID_FOR_UPDATE && !( $wgAntiLockFlags & ALF_NO_LINK_LOCK ) ) {
 1647+ $options[] = 'FOR UPDATE';
 1648+ }
 1649+ $this->mLoadedFromMaster = true;
 1650+ }
 1651+
 1652+ $row = $db->selectRow( 'page', self::selectFields(),
 1653+ $this->pageCond(), __METHOD__, $options );
 1654+ $this->loadFromRow( $row );
 1655+ }
 1656+
 1657+ /**
15081658 * Is $wgUser watching this page?
15091659 *
15101660 * @return Bool
@@ -2769,17 +2919,8 @@
27702920 * @return int The view count for the page
27712921 */
27722922 public function getCount() {
2773 - if ( $this->mCounter == -1 ) {
2774 - if ( $this->exists() ) {
2775 - $dbr = wfGetDB( DB_SLAVE );
2776 - $this->mCounter = $dbr->selectField( 'page',
2777 - 'page_counter',
2778 - array( 'page_id' => $this->getArticleID() ),
2779 - __METHOD__
2780 - );
2781 - } else {
2782 - $this->mCounter = 0;
2783 - }
 2923+ if ( $this->mCounter == false ) {
 2924+ $this->load( self::FIELD_NOT_IN_LINKCACHE );
27842925 }
27852926
27862927 return $this->mCounter;
@@ -2792,16 +2933,7 @@
27932934 */
27942935 public function getTouched() {
27952936 if ( $this->mTouched == null ) {
2796 - if ( $this->exists() ) {
2797 - $dbr = wfGetDB( DB_SLAVE );
2798 - $this->mTouched = $dbr->selectField( 'page',
2799 - 'page_touched',
2800 - array( 'page_id' => $this->getArticleID() ),
2801 - __METHOD__
2802 - );
2803 - } else {
2804 - $this->mTouched = '19700101000000';
2805 - }
 2937+ $this->load( self::FIELD_NOT_IN_LINKCACHE );
28062938 }
28072939
28082940 return $this->mTouched;
@@ -2814,84 +2946,56 @@
28152947 */
28162948 public function isNewPage() {
28172949 if ( $this->mIsNew === null ) {
2818 - if ( $this->exists() ) {
2819 - $dbr = wfGetDB( DB_SLAVE );
2820 - $this->mIsNew = (bool)$dbr->selectField( 'page',
2821 - 'page_is_new',
2822 - array( 'page_id' => $this->getArticleID() ),
2823 - __METHOD__
2824 - );
2825 - } else {
2826 - $this->mIsNew = false;
2827 - }
 2950+ $this->load( self::FIELD_NOT_IN_LINKCACHE );
28282951 }
 2952+
28292953 return $this->mIsNew;
28302954 }
28312955
28322956 /**
2833 - * Get the article ID for this Title from the link cache,
2834 - * adding it if necessary
 2957+ * Get the article ID for this page.
 2958+ * Uses link cache, adding it if necessary.
28352959 *
2836 - * @param $flags Int a bit field; may be Title::GAID_FOR_UPDATE to select
2837 - * for update
 2960+ * @param $flags Int a bit field; may be Title::GAID_USE_MASTER to select
 2961+ * from the master database or Title::GAID_FOR_UPDATE to select for update.
28382962 * @return Int the ID
28392963 */
28402964 public function getArticleID( $flags = 0 ) {
2841 - if ( $this->getNamespace() < 0 ) {
2842 - return $this->mArticleID = 0;
 2965+ if ( $this->mArticleID === -1 || $flags ) {
 2966+ $this->load( self::FIELD_IN_LINKCACHE, $flags );
28432967 }
2844 - $linkCache = LinkCache::singleton();
2845 - if ( $flags & self::GAID_FOR_UPDATE ) {
2846 - $oldUpdate = $linkCache->forUpdate( true );
2847 - $linkCache->clearLink( $this );
2848 - $this->mArticleID = $linkCache->addLinkObj( $this );
2849 - $linkCache->forUpdate( $oldUpdate );
2850 - } else {
2851 - if ( -1 == $this->mArticleID ) {
2852 - $this->mArticleID = $linkCache->addLinkObj( $this );
2853 - }
2854 - }
 2968+
28552969 return $this->mArticleID;
28562970 }
28572971
28582972 /**
28592973 * Is this an article that is a redirect page?
2860 - * Uses link cache, adding it if necessary
 2974+ * Uses link cache, adding it if necessary.
28612975 *
2862 - * @param $flags Int a bit field; may be Title::GAID_FOR_UPDATE to select for update
 2976+ * @param $flags Int a bit field; may be Title::GAID_USE_MASTER to select
 2977+ * from the master database or Title::GAID_FOR_UPDATE to select for update.
28632978 * @return Bool
28642979 */
28652980 public function isRedirect( $flags = 0 ) {
2866 - if ( !is_null( $this->mRedirect ) ) {
2867 - return $this->mRedirect;
 2981+ if ( $this->mRedirect === null || $flags ) {
 2982+ $this->load( self::FIELD_IN_LINKCACHE, $flags );
28682983 }
2869 - # Calling getArticleID() loads the field from cache as needed
2870 - if ( !$this->getArticleID( $flags ) ) {
2871 - return $this->mRedirect = false;
2872 - }
2873 - $linkCache = LinkCache::singleton();
2874 - $this->mRedirect = (bool)$linkCache->getGoodLinkFieldObj( $this, 'redirect' );
28752984
28762985 return $this->mRedirect;
28772986 }
28782987
28792988 /**
28802989 * What is the length of this page?
2881 - * Uses link cache, adding it if necessary
 2990+ * Uses link cache, adding it if necessary.
28822991 *
2883 - * @param $flags Int a bit field; may be Title::GAID_FOR_UPDATE to select for update
 2992+ * @param $flags Int a bit field; may be Title::GAID_USE_MASTER to select
 2993+ * from the master database or Title::GAID_FOR_UPDATE to select for update.
28842994 * @return Int
28852995 */
28862996 public function getLength( $flags = 0 ) {
2887 - if ( $this->mLength != -1 ) {
2888 - return $this->mLength;
 2997+ if ( $this->mLength === -1 || $flags ) {
 2998+ $this->load( self::FIELD_IN_LINKCACHE, $flags );
28892999 }
2890 - # Calling getArticleID() loads the field from cache as needed
2891 - if ( !$this->getArticleID( $flags ) ) {
2892 - return $this->mLength = 0;
2893 - }
2894 - $linkCache = LinkCache::singleton();
2895 - $this->mLength = intval( $linkCache->getGoodLinkFieldObj( $this, 'length' ) );
28963000
28973001 return $this->mLength;
28983002 }
@@ -2899,19 +3003,14 @@
29003004 /**
29013005 * What is the page_latest field for this page?
29023006 *
2903 - * @param $flags Int a bit field; may be Title::GAID_FOR_UPDATE to select for update
 3007+ * @param $flags Int a bit field; may be Title::GAID_USE_MASTER to select
 3008+ * from the master database or Title::GAID_FOR_UPDATE to select for update.
29043009 * @return Int or 0 if the page doesn't exist
29053010 */
29063011 public function getLatestRevID( $flags = 0 ) {
2907 - if ( $this->mLatestID !== false ) {
2908 - return intval( $this->mLatestID );
 3012+ if ( $this->mLatestID === false || $flags ) {
 3013+ $this->load( self::FIELD_IN_LINKCACHE, $flags );
29093014 }
2910 - # Calling getArticleID() loads the field from cache as needed
2911 - if ( !$this->getArticleID( $flags ) ) {
2912 - return $this->mLatestID = 0;
2913 - }
2914 - $linkCache = LinkCache::singleton();
2915 - $this->mLatestID = intval( $linkCache->getGoodLinkFieldObj( $this, 'revision' ) );
29163015
29173016 return $this->mLatestID;
29183017 }
@@ -2927,24 +3026,29 @@
29283027 * @param $newid Int the new Article ID
29293028 */
29303029 public function resetArticleID( $newid ) {
2931 - $linkCache = LinkCache::singleton();
2932 - $linkCache->clearLink( $this );
 3030+ LinkCache::singleton()->clearLink( $this );
29333031
2934 - if ( $newid === false ) {
2935 - $this->mArticleID = -1;
 3032+ if ( $newid === 0 ) {
 3033+ $this->loadFromRow( false );
29363034 } else {
2937 - $this->mArticleID = intval( $newid );
 3035+ if ( $newid === false ) {
 3036+ $this->mArticleID = -1;
 3037+ } else {
 3038+ $this->mArticleID = intval( $newid );
 3039+ }
 3040+ $this->mRestrictionsLoaded = false;
 3041+ $this->mRestrictions = array();
 3042+ $this->mOldRestrictions = null;
 3043+ $this->mRedirect = null;
 3044+ $this->mLength = -1;
 3045+ $this->mLatestID = false;
 3046+ $this->mCounter = false;
 3047+ $this->mTouched = '19700101000000';
 3048+ $this->mIsNew = null;
 3049+ $this->mEstimateRevisions = null;
 3050+ $this->mLoadedLevel = 0;
 3051+ $this->mLoadedFromMaster = false;
29383052 }
2939 - $this->mRestrictionsLoaded = false;
2940 - $this->mRestrictions = array();
2941 - $this->mOldRestrictions = null;
2942 - $this->mRedirect = null;
2943 - $this->mLength = -1;
2944 - $this->mLatestID = false;
2945 - $this->mCounter = -1;
2946 - $this->mTouched = '19700101000000';
2947 - $this->mIsNew = null;
2948 - $this->mEstimateRevisions = null;
29493053 }
29503054
29513055 /**
Index: trunk/phase3/includes/WikiPage.php
@@ -355,8 +355,12 @@
356356 * @return void
357357 */
358358 public function loadPageData( $data = 'fromdb' ) {
 359+ # If we get a DB row, God knows from where it comes
 360+ $fromMaster = false;
 361+
359362 if ( $data === 'fromdbmaster' ) {
360363 $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
 364+ $fromMaster = true;
361365 } elseif ( $data === 'fromdb' ) { // slave
362366 $data = $this->pageDataFromTitle( wfGetDB( DB_SLAVE ), $this->mTitle );
363367 # Use a "last rev inserted" timestamp key to dimish the issue of slave lag.
@@ -365,23 +369,16 @@
366370 if ( $touched ) { // key set
367371 if ( !$data || $touched > wfTimestamp( TS_MW, $data->page_touched ) ) {
368372 $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
 373+ $fromMaster = true;
369374 }
370375 }
371376 }
372377
373 - $lc = LinkCache::singleton();
 378+ $this->mTitle->loadFromRow( $data, $fromMaster );
374379
375380 if ( $data ) {
376 - $lc->addGoodLinkObjFromRow( $this->mTitle, $data );
377 -
378 - $this->mTitle->loadFromRow( $data );
379 -
380381 $this->mIsRedirect = intval( $data->page_is_redirect );
381382 $this->mLatest = intval( $data->page_latest );
382 - } else {
383 - $lc->addBadLinkObj( $this->mTitle );
384 -
385 - $this->mTitle->loadFromRow( false );
386383 }
387384
388385 $this->mDataLoaded = true;

Follow-up revisions

RevisionCommit summaryAuthorDate
r107945Revert r107769, r107771, r107825, r107840, r107927, r107934...brion21:44, 3 January 2012

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r105790Moved view count from WikiPage to Title; avoids an extra DB query when showin...ialex11:30, 11 December 2011

Comments

#Comment by Brion VIBBER (talk | contribs)   21:29, 3 January 2012

Title should have fewer, not more internal state variables. Strongly recommend against this. Really don't need to be doing a refactor like this at the end of a version cycle, with no tests.

Status & tagging log