Page MenuHomePhabricator

Wikibase property names are missing when renders
Closed, ResolvedPublicBUG REPORT

Description

I'm hosting a personal structured data site, powered by MediaWiki and Wikibase. After a recent upgrade of Wikibase extension to fceb8a5, I found all the properties links are rendered without labels, while the entities are rendered correctly. I'm not sure whether this is caused by a bug in the extension, or some mis-configuration of my MW site.

I'm not using any special cache settings. After digging in the code, I think the problem is related to useNormalizedPropertyTerms being enabled. However, I'm not sure about the next step to resovle the problem.

Actual Results:
https://crystalpool.cxuesong.com/wiki/Special:AllPages?namespace=122

image.png (975ร—896 px, 55 KB)

https://crystalpool.cxuesong.com/wiki/Item:Q37
image.png (622ร—846 px, 28 KB)

Expected Results:
I should be able to see the property names, rather than IDs only.

Steps to Reproduce:

  • After debugging into the source code, I found the root cause is when constructing SimpleCacheWithBagOStuff instance in SingleEntitySourceServices::getPrefetchingTermLookup, L429, [Stack trace 1] MediaWikiServices::getInstance()->getLocalServerObjectCache() returns EmptyBagOStuff, which is a dummy implementation that actually does nothing.
    • This will eventually cause SimpleCacheWithBagOStuff::setMultiple throw the received items ($values) into oblivion, [Stack trace 2] and returns nothing when calling SimpleCacheWithBagOStuff::getMultiple.
    • Consequently EntityIdLabelFormatter::getLabel returns nothing, so no property label is rendered.

Stack traces for reference:

n.b. The line number may offset a bit, as I've inserted some snippets for debugging. But basically you may get something from the function names.

Stack trace 1: SimpleCacheWithBagOStuff::__construct

/mw_root/extensions/Wikibase/lib/includes/SimpleCacheWithBagOStuff.php:45
Stack trace:
#0 /mw_root/extensions/Wikibase/data-access/src/SingleEntitySourceServices.php(431): Wikibase\Lib\SimpleCacheWithBagOStuff->__construct(Object(EmptyBagOStuff), 'wikibase.prefet...', 'f2d7ceb7bd23b6a...')
#1 /mw_root/extensions/Wikibase/data-access/src/MultipleEntitySourceServices.php(112): Wikibase\DataAccess\SingleEntitySourceServices->getPrefetchingTermLookup()
#2 /mw_root/extensions/Wikibase/data-access/src/MultipleEntitySourceServices.php(204): Wikibase\DataAccess\MultipleEntitySourceServices->getPrefetchingTermLookup()
#3 /mw_root/extensions/Wikibase/repo/includes/WikibaseRepo.php(1159): Wikibase\DataAccess\MultipleEntitySourceServices->getTermBuffer()
#4 /mw_root/extensions/Wikibase/repo/includes/WikibaseRepo.php(1152): Wikibase\Repo\WikibaseRepo->getPrefetchingTermLookup()
#5 /mw_root/extensions/Wikibase/repo/includes/WikibaseRepo.php(506): Wikibase\Repo\WikibaseRepo->getTermLookup()
#6 /mw_root/extensions/Wikibase/repo/includes/WikibaseRepo.php(487): Wikibase\Repo\WikibaseRepo->newWikibaseValueFormatterBuilders(Array)
#7 /mw_root/extensions/Wikibase/repo/WikibaseRepo.datatypes.php(101): Wikibase\Repo\WikibaseRepo::getDefaultValueFormatterBuilders()
#8 [internal function]: {closure}('text/x-wiki', Object(ValueFormatters\FormatterOptions))
#9 /mw_root/extensions/Wikibase/lib/includes/Formatters/OutputFormatValueFormatterFactory.php(159): call_user_func(Object(Closure), 'text/x-wiki', Object(ValueFormatters\FormatterOptions))
#10 /mw_root/extensions/Wikibase/lib/includes/Formatters/OutputFormatValueFormatterFactory.php(139): Wikibase\Lib\Formatters\OutputFormatValueFormatterFactory->buildDefinedFormatters('text/x-wiki', Object(ValueFormatters\FormatterOptions))
#11 /mw_root/extensions/Wikibase/repo/includes/WikibaseRepo.php(1465): Wikibase\Lib\Formatters\OutputFormatValueFormatterFactory->getValueFormatter('text/x-wiki', Object(ValueFormatters\FormatterOptions))
#12 /mw_root/extensions/Wikibase/repo/includes/WikibaseRepo.php(1436): Wikibase\Repo\WikibaseRepo->getMessageParameterFormatter()
#13 /mw_root/extensions/Wikibase/repo/includes/WikibaseRepo.php(1724): Wikibase\Repo\WikibaseRepo->getValidatorErrorLocalizer()
#14 /mw_root/extensions/Wikibase/repo/WikibaseRepo.entitytypes.php(84): Wikibase\Repo\WikibaseRepo->newItemHandler()
#15 [internal function]: Wikibase\Repo\WikibaseRepo::{closure}()
#16 /mw_root/extensions/Wikibase/repo/includes/Content/EntityContentFactory.php(299): call_user_func(Object(Closure))
#17 /mw_root/extensions/Wikibase/repo/includes/Content/EntityContentFactory.php(327): Wikibase\Repo\Content\EntityContentFactory->getContentHandlerForType('item')
#18 /mw_root/extensions/Wikibase/repo/includes/Content/EntityContentFactory.php(219): Wikibase\Repo\Content\EntityContentFactory->getEntityHandlerForContentModel('wikibase-item')
#19 /mw_root/extensions/WikibaseQualityConstraints/src/WikibaseQualityConstraintsHooks.php(117): Wikibase\Repo\Content\EntityContentFactory->getEntityIdForTitle(Object(Title))
#20 /mw_root/includes/Hooks.php(174): WikibaseQuality\ConstraintReport\WikibaseQualityConstraintsHooks::onArticlePurge(Object(WikiPage))
#21 /mw_root/includes/Hooks.php(202): Hooks::callHook('ArticlePurge', Array, Array, NULL)
#22 /mw_root/includes/page/WikiPage.php(1310): Hooks::run('ArticlePurge', Array)
#23 /mw_root/includes/api/ApiPurge.php(53): WikiPage->doPurge()
#24 /mw_root/includes/api/ApiMain.php(1590): ApiPurge->execute()
#25 /mw_root/includes/api/ApiMain.php(522)

Stack trace 2: SimpleCacheWithBagOStuff::setMultiple

/mw_root/extensions/Wikibase/lib/includes/SimpleCacheWithBagOStuff.php:184
Stack trace:
#0 /mw_root/extensions/Wikibase/lib/includes/StatsdRecordingSimpleCache.php(109): Wikibase\Lib\SimpleCacheWithBagOStuff->setMultiple(Array, 60)
#1 /mw_root/extensions/Wikibase/lib/includes/Store/UncachedTermsPrefetcher.php(72): Wikibase\Lib\StatsdRecordingSimpleCache->setMultiple(Array, 60)
#2 /mw_root/extensions/Wikibase/lib/includes/Store/CachingPrefetchingTermLookup.php(55): Wikibase\Lib\Store\UncachedTermsPrefetcher->prefetchUncached(Object(Wikibase\Lib\StatsdRecordingSimpleCache), Array, Array, Array)
#3 /mw_root/extensions/Wikibase/data-access/src/ByTypeDispatchingPrefetchingTermLookup.php(52): Wikibase\Lib\Store\CachingPrefetchingTermLookup->prefetchTerms(Array, Array, Array)
#4 /mw_root/extensions/Wikibase/data-access/src/ByTypeDispatchingPrefetchingTermLookup.php(52): Wikibase\DataAccess\ByTypeDispatchingPrefetchingTermLookup->prefetchTerms(Array, Array, Array)
#5 /mw_root/extensions/Wikibase/data-access/src/ByTypeDispatchingPrefetchingTermLookup.php(91): Wikibase\DataAccess\ByTypeDispatchingPrefetchingTermLookup->prefetchTerms(Array, Array, Array)
#6 /mw_root/extensions/Wikibase/lib/includes/Store/EntityTermLookupBase.php(41): Wikibase\DataAccess\ByTypeDispatchingPrefetchingTermLookup->getTermsOfType(Object(Wikibase\DataModel\Entity\PropertyId), 'label', Array)
#7 /mw_root/extensions/Wikibase/lib/includes/Store/LanguageFallbackLabelDescriptionLookup.php(53): Wikibase\Lib\Store\EntityTermLookupBase->getLabels(Object(Wikibase\DataModel\Entity\PropertyId), Array)
#8 /mw_root/vendor/wikibase/data-model-services/src/EntityId/EntityIdLabelFormatter.php(54): Wikibase\Lib\Store\LanguageFallbackLabelDescriptionLookup->getLabel(Object(Wikibase\DataModel\Entity\PropertyId))
#9 /mw_root/extensions/Wikibase/lib/includes/Formatters/LabelsProviderEntityIdHtmlLinkFormatter.php(69): Wikibase\DataModel\Services\EntityId\EntityIdLabelFormatter->lookupEntityLabel(Object(Wikibase\DataModel\Entity\PropertyId))
#10 /mw_root/extensions/Wikibase/lib/includes/Formatters/DispatchingEntityIdHtmlLinkFormatter.php(50): Wikibase\Lib\Formatters\LabelsProviderEntityIdHtmlLinkFormatter->formatEntityId(Object(Wikibase\DataModel\Entity\PropertyId))
#11 /mw_root/extensions/WikibaseQualityConstraints/src/ConstraintCheck/Message/ViolationMessageRenderer.php(215): Wikibase\Lib\Formatters\DispatchingEntityIdHtmlLinkFormatter->formatEntityId(Object(Wikibase\DataModel\Entity\PropertyId))
#12 /mw_root/extensions/WikibaseQualityConstraints/src/ConstraintCheck/Message/ViolationMessageRenderer.php(144): WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessageRenderer->renderEntityId(Object(Wikibase\DataModel\Entity\PropertyId), 'constraint-para...')
#13 /mw_root/extensions/WikibaseQualityConstraints/src/ConstraintCheck/Message/ViolationMessageRenderer.php(85): WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessageRenderer->renderArgument(Array)
#14 /mw_root/extensions/WikibaseQualityConstraints/src/ConstraintCheck/Message/MultilingualTextViolationMessageRenderer.php(60): WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessageRenderer->render(Object(WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessage))
#15 /mw_root/extensions/WikibaseQualityConstraints/src/Api/CheckResultsRenderer.php(94): WikibaseQuality\ConstraintReport\ConstraintCheck\Message\MultilingualTextViolationMessageRenderer->render(Object(WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessage))
#16 /mw_root/extensions/WikibaseQualityConstraints/src/Api/CheckResultsRenderer.php(57): WikibaseQuality\ConstraintReport\Api\CheckResultsRenderer->checkResultToArray(Object(WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult))
#17 /mw_root/extensions/WikibaseQualityConstraints/src/Api/CheckConstraints.php(172): WikibaseQuality\ConstraintReport\Api\CheckResultsRenderer->render(Object(WikibaseQuality\ConstraintReport\ConstraintCheck\Cache\CachedCheckResults))
#18 /mw_root/includes/api/ApiMain.php(1590): WikibaseQuality\ConstraintReport\Api\CheckConstraints->execute()
#19 /mw_root/includes/api/ApiMain.php(522): ApiMain->executeAction()
#20 /mw_root/includes/api/ApiMain.php(493): ApiMain->executeActionWithErrorHandling()
#21 /mw_root/api.php(84): ApiMain->execute()
#22 {main}, referer: https://crystalpool.cxuesong.com/wiki/Item:Q37

Possible directions from T247196#5983948

The actionables that we came to from the discussion are:

  • CachingPrefetchingTermLookup should contain a second internal cache that wraps the external one ie. not loose information in the case no local cache is available
  • Document the implementations of PrefetchingTermLookup and TermLookup methods stating where they will be retrieving data from, and what the fallbacks are. Also including details for PrefetchingTermLookup methods about what will happen when methods are called without prefetching first

Event Timeline

Restricted Application added a subscriber: Aklapper. ยท View Herald Transcript
CXuesong renamed this task from Wikibase property names are missing to Wikibase property names are missing when renders.Mar 8 2020, 2:36 PM
Addshore subscribed.

Did you run update.php as part of the upgrade process?

Yes, I have.

And actually the Property labels can be fetched from DB successfully. It just gets lost when storing the labels to the cache, i.e. EmptyBagOStuff.

Addshore triaged this task as High priority.
Addshore added a project: MW-1.35-release.
Addshore added a project: Regression.

Thanks for the detailed analysis in the description!
We will get this picked up and worked on ASAP!

Steps to Reproduce:

After debugging into the source code, I found the root cause is when constructing SimpleCacheWithBagOStuff instance in SingleEntitySourceServices::getPrefetchingTermLookup, L429, [Stack trace 1] MediaWikiServices::getInstance()->getLocalServerObjectCache() returns EmptyBagOStuff, which is a dummy implementation that actually does nothing.
This will eventually cause SimpleCacheWithBagOStuff::setMultiple throw the received items ($values) into oblivion, [Stack trace 2] and returns nothing when calling SimpleCacheWithBagOStuff::getMultiple.
Consequently EntityIdLabelFormatter::getLabel returns nothing, so no property label is rendered.

This is actually combination of two rather important bugs.

  • PrefetchingTermLookup implementation of the old term store (BufferingTermIndexTermLookup) guarantees methods like getLabels, getLabel, etc. (Methods of TermLookup) would work even the terms are not prefetched before (through calling prefetchTerms) which makes a lot of sense.
  • PrefetchingTermLookup implementations of the new term store work in the same way as well (Basically by prefetching the term first which is noop when it's already prefetched, e.g. look at PrefetchingPropertyTermLookup::getTermsOfType)
  • The problem is in CachingPrefetchingTermLookup. getLabel, etc. Doesn't return anything if you haven't prefetched the term and put them into the cache. It shouldn't, It should fallback to the inner lookup.

This is not the case here though, the terms are actually being prefetched (not properly though, one by one, but let's not worry about that for now.)

The second problem is the exact opposite. CachingPrefetchingTermLookup::getPrefetched* looks inside the cache first, then returns empty while it should just check the class cache (delegate it to internal prefetching term lookup, the internal prefecthing term lookup is guaranteed not to do a db query, it just looks it up in the class cache) and not memcached, meaning we are doing lots of wasteful requests to memcached right now, also causing this bug.

Am I missing something obvious here? @Addshore @Jakob_WMDE @Tarrow ?

Change 578298 had a related patch set uploaded (by Ladsgroup; owner: Ladsgroup):
[mediawiki/extensions/Wikibase@master] Do not try to load terms from cache in getPrefetched*()

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

Yeah, the CachingPrefetchingTermLookup definitely doesn't work without a functioning cache, i.e. won't work with EmptyBagOStuff. I'm a bit surprised that PrefetchingTermLookup should fall back to making lookups when prefetched terms are unavailable. My understanding was that only what's prefetched can be looked up.

@Ladsgroup I don't understand what you're describing as the second problem. It's not using memcached, right? It's a local object cache. I also don't know what you mean by "the class cache". CachingPrefetchingTermLookup has no class cache and no internal lookup. It prefetches to write to a cache, and reads everything from a cache.

The two possible steps that I believe we should look into are
a) Check whether we can do something cleverer than passing a /dev/null type of cache. Can we just "cache" in memory per request? Or not use a caching service when caching is disabled?
b) Investigate whether it's really necessary that terms that didn't get prefetched can be looked up via PrefetchingTermLookup.

Change 579030 had a related patch set uploaded (by Ladsgroup; owner: Ladsgroup):
[mediawiki/extensions/Wikibase@master] [WIP] Add internal caching to CachingPrefetchingTermLookup

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

So we just had a little discussion and determined that 2 issues have now been identified here:

  1. as described in this ticket, CachingPrefetchingTermLookup does not work with getLocalServerObjectCache returns an EmptyBagOStuff, as the Lookup expects to be able to store knowledge there in every case.
  2. also CachingPrefetchingTermLookup expects that callers to methods of of the TermLookup interface have already called prefetchTerms which is not the currently expected situation in Wikibase.

The actionables that we came to from the discussion are:

  • CachingPrefetchingTermLookup should contain a second internal cache that wraps the external one ie. not loose information in the case no local cache is available
  • Document the implementations of PrefetchingTermLookup and TermLookup methods stating where they will be retrieving data from, and what the fallbacks are. Also including details for PrefetchingTermLookup methods about what will happen when methods are called without prefetching first

@Ladsgroup @Jakob_WMDE @Tarrow Sorry this took so long to write, I hope my memory served me correctly...

I've applied patch r/578298 and it seems the problem has been mitigated after purging the pages. Thanks in advance!

Change 579030 abandoned by Addshore:
[WIP] Add internal caching to CachingPrefetchingTermLookup

Reason:
I'm going to submit a patch with another approach

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

Change 594450 had a related patch set uploaded (by Addshore; owner: Addshore):
[mediawiki/extensions/Wikibase@master] Only use CachingPrefetchingTermLookup when we have a cache.

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

Change 594459 had a related patch set uploaded (by Addshore; owner: Addshore):
[mediawiki/extensions/Wikibase@master] Improve documentation of PrefetchingTermLookup implementations

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

Change 594450 merged by jenkins-bot:
[mediawiki/extensions/Wikibase@master] Only use CachingPrefetchingTermLookup when we have a cache.

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

Change 594459 merged by jenkins-bot:
[mediawiki/extensions/Wikibase@master] Improve documentation of PrefetchingTermLookup implementations

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

Moving back to peer review as we still need to try and resolve https://gerrit.wikimedia.org/r/#/c/mediawiki/extensions/Wikibase/+/578298/
Assigning to @Ladsgroup for now as it is currently his patch

Addshore lowered the priority of this task from High to Medium.Jun 2 2020, 2:54 PM

Change 607878 had a related patch set uploaded (by Michael GroรŸe; owner: Michael GroรŸe):
[mediawiki/extensions/Wikibase@master] [DNM] Also prefetch data from cache

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

Change 607878 abandoned by Michael GroรŸe:
[mediawiki/extensions/Wikibase@master] [DNM] Also prefetch data from cache

Reason:
has been squashed into the previous commit for review

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

Legoktm subscribed.

It looks like all of this is in Wikibase, so I'm removing as a 1.35 blocker.

Change 578298 merged by jenkins-bot:
[mediawiki/extensions/Wikibase@master] Do not try to load terms from cache in getPrefetched*()

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

Michael subscribed.

As far as I can tell, all associated patches have been either merged or abandoned.

This bug does not reproduce on 1.36.0-wmf.10.

Addshore claimed this task.

Resolving, I dont think anything needs backporting here as a "fix" for the issue was https://gerrit.wikimedia.org/r/c/mediawiki/extensions/Wikibase/+/594459/ which is in REL1_35, even if it was then also fixed later in another patch,