Page MenuHomePhabricator

Translate does not update content page when saving units
Closed, ResolvedPublic

Description

When saving translation it creates page in "Translation:" namespace but "Pagename/lang" is not updated (previously, saving translation made two edits).

Impact

Translation pages (glossary) are not created or updated. This means users do not see some latest translations.

Workarounds

Null / dummy edits to translations unit in the corresponding languages. For example, add a space at the end of a translation. It will enable Save button, but will be ignored during save.

See also https://meta.wikimedia.org/wiki/Meta_talk:Babylon#Temporary_solution:_%22Translate_does_not_update_content_page_when_saving_units%22

Cause

Changes due to T228675: Remove direct access to the text table from the Translate extension. made it so that text contents are loaded in a separate query. In environments with replication lag (as in, Wikimedia production and in none of out test environments), it could now fail to find the text contents with the following errors.

  1. batch loading fails to find the contents, causing fallback (separate loading) to be used for the newly translated translation unit:
Use of Revision::getRevisionText was deprecated in MediaWiki 1.32. [Called from ThinMessage::translation in /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/Message.php at line 170]
  1. Sometimes (but not always) it also fails to find the title information:
MediaWiki\Revision\RevisionStore::getTitle fell back to READ_LATEST and got a Title.

	<ul>
<li>RevisionStore.php line 367 calls wfBacktrace()</li>
<li>RevisionStore.php line 1849 calls MediaWiki\Revision\RevisionStore->getTitle()</li>
<li>RevisionStore.php line 1816 calls MediaWiki\Revision\RevisionStore->newRevisionFromRowAndSlots()</li>
<li>Revision.php line 880 calls MediaWiki\Revision\RevisionStore->newRevisionFromRow()</li>
<li>Message.php line 170 calls Revision::getRevisionText()</li>
<li>MessageCollection.php line 910 calls ThinMessage->translation()</li>
<li>MessageCollection.php line 250 calls MessageCollection->initMessages()</li>
<li>TPParse.php line 182 calls MessageCollection->loadTranslations()</li>
<li>TranslateRenderJob.php line 62 calls TPParse->getTranslationPageText()</li>
<li>PageTranslationHooks.php line 219 calls TranslateRenderJob->run()</li>
<li>PageTranslationHooks.php line 199 calls PageTranslationHooks::updateTranslationPage()</li>
<li>TranslateEditAddons.php line 248 calls PageTranslationHooks::onSectionSave()</li>
<li>Hooks.php line 174 calls TranslateEditAddons::onSave()</li>
<li>Hooks.php line 202 calls Hooks::callHook()</li>
<li>PageUpdater.php line 1239 calls Hooks::run()</li>
<li>Database.php line 3972 calls MediaWiki\Storage\PageUpdater->MediaWiki\Storage\{closure}()</li>
<li>DBConnRef.php line 68 calls Wikimedia\Rdbms\Database->doAtomicSection()</li>
<li>DBConnRef.php line 635 calls Wikimedia\Rdbms\DBConnRef->__call()</li>
<li>AtomicSectionUpdate.php line 39 calls Wikimedia\Rdbms\DBConnRef->doAtomicSection()</li>
<li>DeferredUpdates.php line 383 calls AtomicSectionUpdate->doUpdate()</li>
<li>DeferredUpdates.php line 281 calls DeferredUpdates::attemptUpdate()</li>
<li>DeferredUpdates.php line 226 calls DeferredUpdates::run()</li>
<li>DeferredUpdates.php line 145 calls DeferredUpdates::handleUpdateQueue()</li>
<li>MediaWiki.php line 669 calls DeferredUpdates::doUpdates()</li>
<li>ApiMain.php line 550 calls MediaWiki::preOutputCommit()</li>
<li>ApiMain.php line 508 calls ApiMain->executeActionWithErrorHandling()</li>
<li>api.php line 83 calls ApiMain->execute()</li>
<li>api.php line 3 calls require()</li>
</ul>
  1. This too fails to find the context, throwing an error:
Deferred update AtomicSectionUpdate_MediaWiki\Storage\PageUpdater::getAtomicSectionUpdate failed: Main slot of revision 19462971 not found in database!

	#0 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision/RevisionStore.php(1608): MediaWiki\Revision\RevisionStore->constructSlotRecords('19462971', Object(Wikimedia\Rdbms\ResultWrapper), 0, Object(Title))
#1 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision/RevisionStore.php(1719): MediaWiki\Revision\RevisionStore->loadSlotRecords('19462971', 0, Object(Title))
#2 [internal function]: MediaWiki\Revision\RevisionStore->MediaWiki\Revision\{closure}()
#3 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision/RevisionSlots.php(165): call_user_func(Object(Closure))
#4 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision/RevisionSlots.php(107): MediaWiki\Revision\RevisionSlots->getSlots()
#5 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision/RevisionRecord.php(192): MediaWiki\Revision\RevisionSlots->getSlot('main')
#6 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision/RevisionRecord.php(175): MediaWiki\Revision\RevisionRecord->getSlot('main', 1, NULL)
#7 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision.php(882): MediaWiki\Revision\RevisionRecord->getContent('main')
#8 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/Message.php(170): Revision::getRevisionText(Object(stdClass))
#9 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/MessageCollection.php(910): ThinMessage->translation()
#10 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/MessageCollection.php(250): MessageCollection->initMessages()
#11 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/tag/TPParse.php(182): MessageCollection->loadTranslations()
#12 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/tag/TranslateRenderJob.php(62): TPParse->getTranslationPageText(Object(MessageCollection), true)
#13 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/tag/PageTranslationHooks.php(219): TranslateRenderJob->run()
#14 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/tag/PageTranslationHooks.php(199): PageTranslationHooks::updateTranslationPage(Object(TranslatablePage), 'uk', Object(User), 64, '')
#15 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/TranslateEditAddons.php(248): PageTranslationHooks::onSectionSave(Object(WikiPage), Object(User), Object(WikitextContent), '', 0, 66, Object(Revision), Object(MessageHandle))
#16 /srv/mediawiki/php-1.35.0-wmf.1/includes/Hooks.php(174): TranslateEditAddons::onSave(Object(WikiPage), Object(User), Object(WikitextContent), '', 0, NULL, NULL, 66, Object(Revision), Object(Status), false, 0)
#17 /srv/mediawiki/php-1.35.0-wmf.1/includes/Hooks.php(202): Hooks::callHook('PageContentSave...', Array, Array, NULL)
#18 /srv/mediawiki/php-1.35.0-wmf.1/includes/Storage/PageUpdater.php(1239): Hooks::run('PageContentSave...', Array)
#19 /srv/mediawiki/php-1.35.0-wmf.1/includes/libs/rdbms/database/Database.php(3972): MediaWiki\Storage\PageUpdater->MediaWiki\Storage\{closure}(Object(Wikimedia\Rdbms\DatabaseMysqli), 'MediaWiki\\Stora...')
#20 /srv/mediawiki/php-1.35.0-wmf.1/includes/libs/rdbms/database/DBConnRef.php(68): Wikimedia\Rdbms\Database->doAtomicSection('MediaWiki\\Stora...', Object(Closure))
#21 /srv/mediawiki/php-1.35.0-wmf.1/includes/libs/rdbms/database/DBConnRef.php(635): Wikimedia\Rdbms\DBConnRef->__call('doAtomicSection', Array)
#22 /srv/mediawiki/php-1.35.0-wmf.1/includes/deferred/AtomicSectionUpdate.php(39): Wikimedia\Rdbms\DBConnRef->doAtomicSection('MediaWiki\\Stora...', Object(Closure))
#23 /srv/mediawiki/php-1.35.0-wmf.1/includes/deferred/DeferredUpdates.php(383): AtomicSectionUpdate->doUpdate()
#24 /srv/mediawiki/php-1.35.0-wmf.1/includes/deferred/DeferredUpdates.php(281): DeferredUpdates::attemptUpdate(Object(AtomicSectionUpdate), Object(Wikimedia\Rdbms\LBFactoryMulti))
#25 /srv/mediawiki/php-1.35.0-wmf.1/includes/deferred/DeferredUpdates.php(226): DeferredUpdates::run(Object(AtomicSectionUpdate), Object(Wikimedia\Rdbms\LBFactoryMulti), Object(Monolog\Logger), Object(BufferingStatsdDataFactory), 'post')
#26 /srv/mediawiki/php-1.35.0-wmf.1/includes/deferred/DeferredUpdates.php(145): DeferredUpdates::handleUpdateQueue(Array, 'run', 1)
#27 /srv/mediawiki/php-1.35.0-wmf.1/includes/MediaWiki.php(669): DeferredUpdates::doUpdates('run', 1)
#28 /srv/mediawiki/php-1.35.0-wmf.1/includes/api/ApiMain.php(550): MediaWiki::preOutputCommit(Object(DerivativeContext))
#29 /srv/mediawiki/php-1.35.0-wmf.1/includes/api/ApiMain.php(508): ApiMain->executeActionWithErrorHandling()
#30 /srv/mediawiki/php-1.35.0-wmf.1/api.php(83): ApiMain->execute()
#31 /srv/mediawiki/w/api.php(3): require('/srv/mediawiki/...')
#32 {main}

This previously worked, so what is different? The new code always loaded from DB_REPLICA, while the old code uses a separate logic to determine whether to load from DB_MASTER.

Fixes

Applied the same logic to the new batch loading code, to read from master consistently.

Also deferred the updating of the translation page. This probably did not fix the issue, but it has other advantages such as reducing user visible saving delay, better ordering and encapsulation.

Event Timeline

Wargo triaged this task as High priority.Oct 9 2019, 9:44 AM

Related request IDs:

  1. XZ2IngpAEDMAAGBwY4EAAABQ
  2. XZ2IAwpAIDUAAJzJlMoAAABR

Message
Deferred update AtomicSectionUpdate_MediaWiki\Storage\PageUpdater::getAtomicSectionUpdate failed: Main slot of revision 3451102 not found in database!

Stack trace

#0 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision/RevisionStore.php(1608): MediaWiki\Revision\RevisionStore->constructSlotRecords('3451102', Object(Wikimedia\Rdbms\ResultWrapper), 0, Object(Title))
#1 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision/RevisionStore.php(1719): MediaWiki\Revision\RevisionStore->loadSlotRecords('3451102', 0, Object(Title))
#2 [internal function]: MediaWiki\Revision\RevisionStore->MediaWiki\Revision\{closure}()
#3 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision/RevisionSlots.php(165): call_user_func(Object(Closure))
#4 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision/RevisionSlots.php(107): MediaWiki\Revision\RevisionSlots->getSlots()
#5 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision/RevisionRecord.php(192): MediaWiki\Revision\RevisionSlots->getSlot('main')
#6 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision/RevisionRecord.php(175): MediaWiki\Revision\RevisionRecord->getSlot('main', 1, NULL)
#7 /srv/mediawiki/php-1.35.0-wmf.1/includes/Revision.php(882): MediaWiki\Revision\RevisionRecord->getContent('main')
#8 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/Message.php(170): Revision::getRevisionText(Object(stdClass))
#9 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/MessageCollection.php(910): ThinMessage->translation()
#10 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/MessageCollection.php(250): MessageCollection->initMessages()
#11 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/tag/TPParse.php(182): MessageCollection->loadTranslations()
#12 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/tag/TranslateRenderJob.php(62): TPParse->getTranslationPageText(Object(MessageCollection), true)
#13 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/tag/PageTranslationHooks.php(219): TranslateRenderJob->run()
#14 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/tag/PageTranslationHooks.php(199): PageTranslationHooks::updateTranslationPage(Object(TranslatablePage), 'nqo', Object(User), 64, 'Created page wi...')
#15 /srv/mediawiki/php-1.35.0-wmf.1/extensions/Translate/TranslateEditAddons.php(248): PageTranslationHooks::onSectionSave(Object(WikiPage), Object(User), Object(WikitextContent), 'Created page wi...', 0, 65, Object(Revision), Object(MessageHandle))
#16 /srv/mediawiki/php-1.35.0-wmf.1/includes/Hooks.php(174): TranslateEditAddons::onSave(Object(WikiPage), Object(User), Object(WikitextContent), 'Created page wi...', 0, NULL, NULL, 65, Object(Revision), Object(Status), false, 0)
#17 /srv/mediawiki/php-1.35.0-wmf.1/includes/Hooks.php(202): Hooks::callHook('PageContentSave...', Array, Array, NULL)

I was looking at Logstash this morning and notices that one job was failing (Help:Log/nqo), while others were running successfully. Having the TranslateRenderJob fail obviously causes the reported problem that the translation page is not getting updated.

My quick guess was that it does not find the content for the page that was just saved (but finds the revision?), fall backs to Revision::getRevisionText and that also fails to find the content, but throws an exception.

Is there a way to pass some kind of READ_LATEST through this interface? Or am I perhaps on a wrong track altogether?

To investigate, any errors reported in the StatusValue returned by the calls to getContentBlobsForBatch() could be logged. We currently just ignore them.

You can indeed pass READ_LATEST to getContentBlobsForBatch() as the third parameter. That should fix it, but might be bad for performance.

Finding the revision but not the content may happen if the reads hit different replicas. Which can easily happen with ES, but I find curious in the absense of CS.

We have seen some mystery problems that may be related... I can't find it right now, @Anomie would know.

Could the opposite happen as well: it would not find the latest revision, and then fetch stale content (penultimate)? That could also show up as the translation page not updating (or more specifically, lagging behind one edit).

Could the opposite happen as well: it would not find the latest revision, and then fetch stale content (penultimate)?

yes. if the read from the page table is stale, that would happen.

EDIT: whoever, that query has not changed, and it is uses the connection returned by TranslateUtils::getSafeReadDB().

It occurs for first revision, as for correction of an already translated text. Workaround: applying @Shirayuki 's proposal, I correct the translation adding a dummy modification (one space after last character of the translated string) and then reload the page => updated text is then taken into account.

The new page seems not to display until 100% of the articles are translated. Then only, and after you apply the workaround herabove, the language appears in 'Other languages:' and your translations are viewable.

We have seen some mystery problems that may be related... I can't find it right now, @Anomie would know.

Are the mystery problems anything like T235188: Preemptive refresh in getMultiWithSetCallback() and getMultiWithUnionSetCallback() pollutes cache?

We have seen some mystery problems that may be related... I can't find it right now, @Anomie would know.

Are the mystery problems anything like T235188: Preemptive refresh in getMultiWithSetCallback() and getMultiWithUnionSetCallback() pollutes cache?

No. Page content failed to load intermittently. It didn't get mixed up.
That sounds really strange...

Wargo, was there another task you wanted to link to in the sentence above? Thanks.

It occurs for first revision, as for correction of an already translated text. Workaround: applying @Shirayuki 's proposal, I correct the translation adding a dummy modification (one space after last character of the translated string) and then reload the page => updated text is then taken into account.

Actually, a usual null edit (i.e. no change at all) is enough, although the TUX interface doesn’t allow saving a translation unit until it’s changed (but adding and removing the space is just as good as adding and not removing it).

The new page seems not to display until 100% of the articles are translated. Then only, and after you apply the workaround herabove, the language appears in 'Other languages:' and your translations are viewable.

I managed to get https://www.mediawiki.org/wiki/Help:Talk_pages/hu created with just one translated unit (of course only with the null edit workaround used for existing pages as well).

This is now happening on Meta-Wiki, which is non-group0. Translating on Meta right now is broken by this.

Change 542923 had a related patch set uploaded (by Nikerabbit; owner: Nikerabbit):
[mediawiki/extensions/Translate@master] Try to avoid stale reads when rendering translation page

https://gerrit.wikimedia.org/r/542923

Change 542928 had a related patch set uploaded (by Nikerabbit; owner: Nikerabbit):
[mediawiki/extensions/Translate@master] Defer TranslateRenderJob on translation unit updates

https://gerrit.wikimedia.org/r/542928

Change 542923 merged by jenkins-bot:
[mediawiki/extensions/Translate@master] Try to avoid stale reads when rendering translation page

https://gerrit.wikimedia.org/r/542923

Change 542928 merged by jenkins-bot:
[mediawiki/extensions/Translate@master] Defer TranslateRenderJob on translation unit updates

https://gerrit.wikimedia.org/r/542928

Hello everyone,

According to feedback from users, the temporary solution "null edit" does not seem to be accessible to everyone. I made a small explanation to have the whole page available. This is where it happens.

Looking at LogStash using this search term c_type:AtomicSectionUpdate_MediaWiki\Storage\PageUpdater\:\:getAtomicSectionUpdate AND wiki:mediawikiwiki (since only mediawikiwiki so far has the fixes) I see last failure of this type was at 2019-10-15T19:55:28, which nicely matches 2019-10-15T20:10:15 "rebuilt and synchronized wikiversions files: group0 wikis to 1.35.0-wmf.2 refs T233850".

Last event happened at 2019-10-16T19:04:46 and fixes were deployed 2019-10-16T19:07:22. This is perfect match.

refresh-translatable-pages.php will be run after T235188 is resolved.

Mentioned in SAL (#wikimedia-operations) [2019-12-11T09:04:51Z] <Nikerabbit> running Translate/refresh-translatable-pages.php --jobqueue for Translate wikis - T235027 T235188

Mentioned in SAL (#wikimedia-operations) [2019-12-11T10:34:14Z] <Nikerabbit> Finished running Translate/refresh-translatable-pages.php --jobqueue for Translate wikis - T235027 T235188