HomePhabricator

Made WikiPage recall the source of the data used to load its state.

Description

Made WikiPage recall the source of the data used to load its state.

In WikiPage.php:

  • Added WikiPage::$mDataLoadedFrom to store the source of the data used to load the state of the object and four new WikiPage::DATA_* constants for its possible values.
  • Added WikiPage::convertSelectType() to convert 'fromdb', 'fromdbmaster' and 'forupdate' to the new WikiPage::DATA_* constants.
  • Added $options to WikiPage::pageData(), WikiPage::pageDataFromTitle() and WikiPage::pageDataFromId() so that the "FOR UPDATE" option can be passed to DatabaseBase::select().
  • Added new possibility "forupdate" to WikiPage::loadPageData() load the data from the master database using SELECT FOR UPDATE; this avoids to have to do this by LinkCache (via Title::getArticleID( Title::GAID_FOR_UPDATE ) )).
  • Changed WikiPage::doDeleteArticleReal() to use this new feature so that all the data stored in WikiPage is up-to-date.

My point is also to deprecate the loading using SELECT FOR UPDATE in Title and remove LinkCache::forUpdate() at some point (there are still one usage in Title::moveTo(), two other in UploadFromUrlTest plus some in extensions).

In EditPage.php:

  • Don't call WikiPage::clear() after fetching informations from master, this destroys all we work we did to get the correct data.
  • Reload the whole data from master using SELECT FOR UPDATE directly in WikiPage and not only in Title. The problem was that before, the up-to-date information was only available in Title and not in WikiPage. Consider the following sequence from a MySQL prompt (where both revision 1 and 2 belong to page one, revision #2 being the current one):

mysql> UPDATE page SET page_latest=1 WHERE page_id=1;
mysql> COMMIT;
Now grad the edit form for page #1 from the web and do some changes
mysql> BEGIN;
mysql> SELECT page_latest FROM page WHERE page_id=1 FOR UPDATE;
Now submit the web form
mysql> UPDATE page SET page_latest=2 WHERE page_id=1;
mysql> COMMIT;

Before you ended-up with a "edit conflict" form with revision #1's text being displayed as current text (even if the texts are mergeable), due to the fact that
in the submit request the WikiPage object was loaded at the moment where page_latest was 1 (certainly due to MySQL's "consistent read" feature) making the
"UPDATE page SET ... WHERE page_id=1 AND page_latest=1" query of WikiPage::updateRevisionOn() return zero row, and thus WikiPage::doEdit returing a fatal Status object with message "edit-conflict".
Now the SELECT FOR UPDATE is done in the WikiPage, meaning that the object has the correct data and EditPage will correctly try to merge the revisions (and show the correct edit conflict if needed).

Change-Id: Ic4878ddb4dd96432b7ecaf43f9f359458d966999

Details